Merge pull request #9704 from home-assistant/release-0-55

0.55
This commit is contained in:
Paulus Schoutsen 2017-10-07 12:50:52 -07:00 committed by GitHub
commit 8a2d7a3e11
237 changed files with 8059 additions and 1812 deletions

View File

@ -149,6 +149,9 @@ omit =
homeassistant/components/rachio.py homeassistant/components/rachio.py
homeassistant/components/*/rachio.py homeassistant/components/*/rachio.py
homeassistant/components/raincloud.py
homeassistant/components/*/raincloud.py
homeassistant/components/raspihats.py homeassistant/components/raspihats.py
homeassistant/components/*/raspihats.py homeassistant/components/*/raspihats.py
@ -179,6 +182,9 @@ omit =
homeassistant/components/tesla.py homeassistant/components/tesla.py
homeassistant/components/*/tesla.py homeassistant/components/*/tesla.py
homeassistant/components/thethingsnetwork.py
homeassistant/components/*/thethingsnetwork.py
homeassistant/components/*/thinkingcleaner.py homeassistant/components/*/thinkingcleaner.py
homeassistant/components/tradfri.py homeassistant/components/tradfri.py
@ -399,6 +405,7 @@ omit =
homeassistant/components/notify/aws_sqs.py homeassistant/components/notify/aws_sqs.py
homeassistant/components/notify/ciscospark.py homeassistant/components/notify/ciscospark.py
homeassistant/components/notify/clicksend.py homeassistant/components/notify/clicksend.py
homeassistant/components/notify/clicksendaudio.py
homeassistant/components/notify/discord.py homeassistant/components/notify/discord.py
homeassistant/components/notify/facebook.py homeassistant/components/notify/facebook.py
homeassistant/components/notify/free_mobile.py homeassistant/components/notify/free_mobile.py
@ -538,6 +545,7 @@ omit =
homeassistant/components/sensor/tank_utility.py homeassistant/components/sensor/tank_utility.py
homeassistant/components/sensor/ted5000.py homeassistant/components/sensor/ted5000.py
homeassistant/components/sensor/temper.py homeassistant/components/sensor/temper.py
homeassistant/components/sensor/tibber.py
homeassistant/components/sensor/time_date.py homeassistant/components/sensor/time_date.py
homeassistant/components/sensor/torque.py homeassistant/components/sensor/torque.py
homeassistant/components/sensor/transmission.py homeassistant/components/sensor/transmission.py
@ -581,7 +589,6 @@ omit =
homeassistant/components/thingspeak.py homeassistant/components/thingspeak.py
homeassistant/components/tts/amazon_polly.py homeassistant/components/tts/amazon_polly.py
homeassistant/components/tts/picotts.py homeassistant/components/tts/picotts.py
homeassistant/components/upnp.py
homeassistant/components/vacuum/roomba.py homeassistant/components/vacuum/roomba.py
homeassistant/components/weather/bom.py homeassistant/components/weather/bom.py
homeassistant/components/weather/buienradar.py homeassistant/components/weather/buienradar.py

View File

@ -39,3 +39,7 @@ homeassistant/components/*/zwave.py @home-assistant/z-wave
homeassistant/components/cover/template.py @PhracturedBlue homeassistant/components/cover/template.py @PhracturedBlue
homeassistant/components/device_tracker/automatic.py @armills homeassistant/components/device_tracker/automatic.py @armills
homeassistant/components/media_player/kodi.py @armills homeassistant/components/media_player/kodi.py @armills
homeassistant/components/light/tplink.py @rytilahti
homeassistant/components/switch/tplink.py @rytilahti
homeassistant/components/climate/eq3btsmart.py @rytilahti
homeassistant/components/*/xiaomi_miio.py @rytilahti

View File

@ -11,9 +11,10 @@ MAINTAINER Paulus Schoutsen <Paulus@PaulusSchoutsen.nl>
#ENV INSTALL_FFMPEG no #ENV INSTALL_FFMPEG no
#ENV INSTALL_LIBCEC no #ENV INSTALL_LIBCEC no
#ENV INSTALL_PHANTOMJS no #ENV INSTALL_PHANTOMJS no
#ENV INSTALL_COAP_CLIENT no #ENV INSTALL_COAP no
#ENV INSTALL_SSOCR no #ENV INSTALL_SSOCR no
VOLUME /config VOLUME /config
RUN mkdir -p /usr/src/app RUN mkdir -p /usr/src/app
@ -25,7 +26,6 @@ RUN virtualization/Docker/setup_docker_prereqs
# Install hass component dependencies # Install hass component dependencies
COPY requirements_all.txt requirements_all.txt COPY requirements_all.txt requirements_all.txt
# Uninstall enum34 because some depenndecies install it but breaks Python 3.4+. # Uninstall enum34 because some depenndecies install it but breaks Python 3.4+.
# See PR #8103 for more info. # See PR #8103 for more info.
RUN pip3 install --no-cache-dir -r requirements_all.txt && \ RUN pip3 install --no-cache-dir -r requirements_all.txt && \

View File

@ -117,7 +117,11 @@ def linkcode_resolve(domain, info):
linespec = "#L%d" % (lineno + 1) linespec = "#L%d" % (lineno + 1)
else: else:
linespec = "" linespec = ""
fn = relpath(fn, start='../') index = fn.find("/homeassistant/")
if index == -1:
index = 0
fn = fn[index:]
return '{}/blob/{}/{}{}'.format(GITHUB_URL, code_branch, fn, linespec) return '{}/blob/{}/{}{}'.format(GITHUB_URL, code_branch, fn, linespec)

View File

@ -83,6 +83,18 @@ def async_from_config_dict(config: Dict[str, Any],
This method is a coroutine. This method is a coroutine.
""" """
start = time() start = time()
if enable_log:
async_enable_logging(hass, verbose, log_rotate_days, log_file)
if sys.version_info[:2] < (3, 5):
_LOGGER.warning(
'Python 3.4 support has been deprecated and will be removed in '
'the begining of 2018. Please upgrade Python or your operating '
'system. More info: https://home-assistant.io/blog/2017/10/06/'
'deprecating-python-3.4-support/'
)
core_config = config.get(core.DOMAIN, {}) core_config = config.get(core.DOMAIN, {})
try: try:
@ -93,9 +105,6 @@ def async_from_config_dict(config: Dict[str, Any],
yield from hass.async_add_job(conf_util.process_ha_config_upgrade, hass) yield from hass.async_add_job(conf_util.process_ha_config_upgrade, hass)
if enable_log:
async_enable_logging(hass, verbose, log_rotate_days, log_file)
hass.config.skip_pip = skip_pip hass.config.skip_pip = skip_pip
if skip_pip: if skip_pip:
_LOGGER.warning("Skipping pip installation of required modules. " _LOGGER.warning("Skipping pip installation of required modules. "

View File

@ -21,7 +21,7 @@ from homeassistant.const import (ATTR_ATTRIBUTION, ATTR_DATE, ATTR_TIME,
EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_STOP,
EVENT_HOMEASSISTANT_START) EVENT_HOMEASSISTANT_START)
REQUIREMENTS = ['abodepy==0.11.8'] REQUIREMENTS = ['abodepy==0.11.9']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@ -107,7 +107,7 @@ class Concord232Alarm(alarm.AlarmControlPanel):
newstate = STATE_ALARM_ARMED_AWAY newstate = STATE_ALARM_ARMED_AWAY
if not newstate == self._state: if not newstate == self._state:
_LOGGER.info("State Chnage from %s to %s", self._state, newstate) _LOGGER.info("State Change from %s to %s", self._state, newstate)
self._state = newstate self._state = newstate
return self._state return self._state

View File

@ -18,13 +18,14 @@ from homeassistant.const import (
CONF_NAME, STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, CONF_NAME, STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_TRIGGERED) STATE_ALARM_ARMED_AWAY, STATE_ALARM_TRIGGERED)
REQUIREMENTS = ['pythonegardia==1.0.20'] REQUIREMENTS = ['pythonegardia==1.0.21']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CONF_REPORT_SERVER_CODES = 'report_server_codes' CONF_REPORT_SERVER_CODES = 'report_server_codes'
CONF_REPORT_SERVER_ENABLED = 'report_server_enabled' CONF_REPORT_SERVER_ENABLED = 'report_server_enabled'
CONF_REPORT_SERVER_PORT = 'report_server_port' CONF_REPORT_SERVER_PORT = 'report_server_port'
CONF_REPORT_SERVER_CODES_IGNORE = 'ignore'
DEFAULT_NAME = 'Egardia' DEFAULT_NAME = 'Egardia'
DEFAULT_PORT = 80 DEFAULT_PORT = 80
@ -148,9 +149,15 @@ class EgardiaAlarm(alarm.AlarmControlPanel):
def parsestatus(self, status): def parsestatus(self, status):
"""Parse the status.""" """Parse the status."""
_LOGGER.debug("Parsing status %s", status)
# Ignore the statuscode if it is IGNORE
if status.lower().strip() != CONF_REPORT_SERVER_CODES_IGNORE:
_LOGGER.debug("Not ignoring status")
newstatus = ([v for k, v in STATES.items() newstatus = ([v for k, v in STATES.items()
if status.upper() == k][0]) if status.upper() == k][0])
self._status = newstatus self._status = newstatus
else:
_LOGGER.error("Ignoring status")
def update(self): def update(self):
"""Update the alarm status.""" """Update the alarm status."""

View File

@ -5,6 +5,7 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.manual_mqtt/ https://home-assistant.io/components/alarm_control_panel.manual_mqtt/
""" """
import asyncio import asyncio
import copy
import datetime import datetime
import logging import logging
@ -13,9 +14,9 @@ import voluptuous as vol
import homeassistant.components.alarm_control_panel as alarm import homeassistant.components.alarm_control_panel as alarm
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from homeassistant.const import ( from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, CONF_PLATFORM, STATE_ALARM_DISARMED, STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED,
CONF_NAME, CONF_CODE, CONF_PENDING_TIME, CONF_TRIGGER_TIME, CONF_PLATFORM, CONF_NAME, CONF_CODE, CONF_PENDING_TIME, CONF_TRIGGER_TIME,
CONF_DISARM_AFTER_TRIGGER) CONF_DISARM_AFTER_TRIGGER)
import homeassistant.components.mqtt as mqtt import homeassistant.components.mqtt as mqtt
@ -28,6 +29,7 @@ from homeassistant.helpers.event import track_point_in_time
CONF_PAYLOAD_DISARM = 'payload_disarm' CONF_PAYLOAD_DISARM = 'payload_disarm'
CONF_PAYLOAD_ARM_HOME = 'payload_arm_home' CONF_PAYLOAD_ARM_HOME = 'payload_arm_home'
CONF_PAYLOAD_ARM_AWAY = 'payload_arm_away' CONF_PAYLOAD_ARM_AWAY = 'payload_arm_away'
CONF_PAYLOAD_ARM_NIGHT = 'payload_arm_night'
DEFAULT_ALARM_NAME = 'HA Alarm' DEFAULT_ALARM_NAME = 'HA Alarm'
DEFAULT_PENDING_TIME = 60 DEFAULT_PENDING_TIME = 60
@ -35,11 +37,32 @@ DEFAULT_TRIGGER_TIME = 120
DEFAULT_DISARM_AFTER_TRIGGER = False DEFAULT_DISARM_AFTER_TRIGGER = False
DEFAULT_ARM_AWAY = 'ARM_AWAY' DEFAULT_ARM_AWAY = 'ARM_AWAY'
DEFAULT_ARM_HOME = 'ARM_HOME' DEFAULT_ARM_HOME = 'ARM_HOME'
DEFAULT_ARM_NIGHT = 'ARM_NIGHT'
DEFAULT_DISARM = 'DISARM' DEFAULT_DISARM = 'DISARM'
SUPPORTED_PENDING_STATES = [STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_TRIGGERED]
ATTR_POST_PENDING_STATE = 'post_pending_state'
def _state_validator(config):
config = copy.deepcopy(config)
for state in SUPPORTED_PENDING_STATES:
if CONF_PENDING_TIME not in config[state]:
config[state][CONF_PENDING_TIME] = config[CONF_PENDING_TIME]
return config
STATE_SETTING_SCHEMA = vol.Schema({
vol.Optional(CONF_PENDING_TIME):
vol.All(vol.Coerce(int), vol.Range(min=0))
})
DEPENDENCIES = ['mqtt'] DEPENDENCIES = ['mqtt']
PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = vol.Schema(vol.All(mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({
vol.Required(CONF_PLATFORM): 'manual_mqtt', vol.Required(CONF_PLATFORM): 'manual_mqtt',
vol.Optional(CONF_NAME, default=DEFAULT_ALARM_NAME): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_ALARM_NAME): cv.string,
vol.Optional(CONF_CODE): cv.string, vol.Optional(CONF_CODE): cv.string,
@ -49,12 +72,17 @@ PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({
vol.All(vol.Coerce(int), vol.Range(min=1)), vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Optional(CONF_DISARM_AFTER_TRIGGER, vol.Optional(CONF_DISARM_AFTER_TRIGGER,
default=DEFAULT_DISARM_AFTER_TRIGGER): cv.boolean, default=DEFAULT_DISARM_AFTER_TRIGGER): cv.boolean,
vol.Optional(STATE_ALARM_ARMED_AWAY, default={}): STATE_SETTING_SCHEMA,
vol.Optional(STATE_ALARM_ARMED_HOME, default={}): STATE_SETTING_SCHEMA,
vol.Optional(STATE_ALARM_ARMED_NIGHT, default={}): STATE_SETTING_SCHEMA,
vol.Optional(STATE_ALARM_TRIGGERED, default={}): STATE_SETTING_SCHEMA,
vol.Required(mqtt.CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Required(mqtt.CONF_COMMAND_TOPIC): mqtt.valid_publish_topic,
vol.Required(mqtt.CONF_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Required(mqtt.CONF_STATE_TOPIC): mqtt.valid_subscribe_topic,
vol.Optional(CONF_PAYLOAD_ARM_AWAY, default=DEFAULT_ARM_AWAY): cv.string, vol.Optional(CONF_PAYLOAD_ARM_AWAY, default=DEFAULT_ARM_AWAY): cv.string,
vol.Optional(CONF_PAYLOAD_ARM_HOME, default=DEFAULT_ARM_HOME): cv.string, vol.Optional(CONF_PAYLOAD_ARM_HOME, default=DEFAULT_ARM_HOME): cv.string,
vol.Optional(CONF_PAYLOAD_ARM_NIGHT, default=DEFAULT_ARM_NIGHT): cv.string,
vol.Optional(CONF_PAYLOAD_DISARM, default=DEFAULT_DISARM): cv.string, vol.Optional(CONF_PAYLOAD_DISARM, default=DEFAULT_DISARM): cv.string,
}) }), _state_validator))
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -73,7 +101,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
config.get(mqtt.CONF_QOS), config.get(mqtt.CONF_QOS),
config.get(CONF_PAYLOAD_DISARM), config.get(CONF_PAYLOAD_DISARM),
config.get(CONF_PAYLOAD_ARM_HOME), config.get(CONF_PAYLOAD_ARM_HOME),
config.get(CONF_PAYLOAD_ARM_AWAY))]) config.get(CONF_PAYLOAD_ARM_AWAY),
config.get(CONF_PAYLOAD_ARM_NIGHT),
config)])
class ManualMQTTAlarm(alarm.AlarmControlPanel): class ManualMQTTAlarm(alarm.AlarmControlPanel):
@ -89,7 +119,8 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
def __init__(self, hass, name, code, pending_time, def __init__(self, hass, name, code, pending_time,
trigger_time, disarm_after_trigger, trigger_time, disarm_after_trigger,
state_topic, command_topic, qos, state_topic, command_topic, qos,
payload_disarm, payload_arm_home, payload_arm_away): payload_disarm, payload_arm_home, payload_arm_away,
payload_arm_night, config):
"""Init the manual MQTT alarm panel.""" """Init the manual MQTT alarm panel."""
self._state = STATE_ALARM_DISARMED self._state = STATE_ALARM_DISARMED
self._hass = hass self._hass = hass
@ -101,12 +132,18 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
self._pre_trigger_state = self._state self._pre_trigger_state = self._state
self._state_ts = None self._state_ts = None
self._pending_time_by_state = {}
for state in SUPPORTED_PENDING_STATES:
self._pending_time_by_state[state] = datetime.timedelta(
seconds=config[state][CONF_PENDING_TIME])
self._state_topic = state_topic self._state_topic = state_topic
self._command_topic = command_topic self._command_topic = command_topic
self._qos = qos self._qos = qos
self._payload_disarm = payload_disarm self._payload_disarm = payload_disarm
self._payload_arm_home = payload_arm_home self._payload_arm_home = payload_arm_home
self._payload_arm_away = payload_arm_away self._payload_arm_away = payload_arm_away
self._payload_arm_night = payload_arm_night
@property @property
def should_poll(self): def should_poll(self):
@ -121,23 +158,27 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
@property @property
def state(self): def state(self):
"""Return the state of the device.""" """Return the state of the device."""
if self._state in (STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_AWAY) and \
self._pending_time and self._state_ts + self._pending_time > \
dt_util.utcnow():
return STATE_ALARM_PENDING
if self._state == STATE_ALARM_TRIGGERED and self._trigger_time: if self._state == STATE_ALARM_TRIGGERED and self._trigger_time:
if self._state_ts + self._pending_time > dt_util.utcnow(): if self._within_pending_time(self._state):
return STATE_ALARM_PENDING return STATE_ALARM_PENDING
elif (self._state_ts + self._pending_time + elif (self._state_ts + self._pending_time_by_state[self._state] +
self._trigger_time) < dt_util.utcnow(): self._trigger_time) < dt_util.utcnow():
if self._disarm_after_trigger: if self._disarm_after_trigger:
return STATE_ALARM_DISARMED return STATE_ALARM_DISARMED
return self._pre_trigger_state else:
self._state = self._pre_trigger_state
return self._state
if self._state in SUPPORTED_PENDING_STATES and \
self._within_pending_time(self._state):
return STATE_ALARM_PENDING
return self._state return self._state
def _within_pending_time(self, state):
pending_time = self._pending_time_by_state[state]
return self._state_ts + pending_time > dt_util.utcnow()
@property @property
def code_format(self): def code_format(self):
"""One or more characters.""" """One or more characters."""
@ -157,44 +198,47 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
if not self._validate_code(code, STATE_ALARM_ARMED_HOME): if not self._validate_code(code, STATE_ALARM_ARMED_HOME):
return return
self._state = STATE_ALARM_ARMED_HOME self._update_state(STATE_ALARM_ARMED_HOME)
self._state_ts = dt_util.utcnow()
self.schedule_update_ha_state()
if self._pending_time:
track_point_in_time(
self._hass, self.async_update_ha_state,
self._state_ts + self._pending_time)
def alarm_arm_away(self, code=None): def alarm_arm_away(self, code=None):
"""Send arm away command.""" """Send arm away command."""
if not self._validate_code(code, STATE_ALARM_ARMED_AWAY): if not self._validate_code(code, STATE_ALARM_ARMED_AWAY):
return return
self._state = STATE_ALARM_ARMED_AWAY self._update_state(STATE_ALARM_ARMED_AWAY)
self._state_ts = dt_util.utcnow()
self.schedule_update_ha_state()
if self._pending_time: def alarm_arm_night(self, code=None):
track_point_in_time( """Send arm night command."""
self._hass, self.async_update_ha_state, if not self._validate_code(code, STATE_ALARM_ARMED_NIGHT):
self._state_ts + self._pending_time) return
self._update_state(STATE_ALARM_ARMED_NIGHT)
def alarm_trigger(self, code=None): def alarm_trigger(self, code=None):
"""Send alarm trigger command. No code needed.""" """Send alarm trigger command. No code needed."""
self._pre_trigger_state = self._state self._pre_trigger_state = self._state
self._state = STATE_ALARM_TRIGGERED
self._update_state(STATE_ALARM_TRIGGERED)
def _update_state(self, state):
self._state = state
self._state_ts = dt_util.utcnow() self._state_ts = dt_util.utcnow()
self.schedule_update_ha_state() self.schedule_update_ha_state()
if self._trigger_time: pending_time = self._pending_time_by_state[state]
if state == STATE_ALARM_TRIGGERED and self._trigger_time:
track_point_in_time( track_point_in_time(
self._hass, self.async_update_ha_state, self._hass, self.async_update_ha_state,
self._state_ts + self._pending_time) self._state_ts + pending_time)
track_point_in_time( track_point_in_time(
self._hass, self.async_update_ha_state, self._hass, self.async_update_ha_state,
self._state_ts + self._pending_time + self._trigger_time) self._state_ts + self._trigger_time + pending_time)
elif state in SUPPORTED_PENDING_STATES and pending_time:
track_point_in_time(
self._hass, self.async_update_ha_state,
self._state_ts + pending_time)
def _validate_code(self, code, state): def _validate_code(self, code, state):
"""Validate given code.""" """Validate given code."""
@ -203,6 +247,16 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
_LOGGER.warning("Invalid code given for %s", state) _LOGGER.warning("Invalid code given for %s", state)
return check return check
@property
def device_state_attributes(self):
"""Return the state attributes."""
state_attr = {}
if self.state == STATE_ALARM_PENDING:
state_attr[ATTR_POST_PENDING_STATE] = self._state
return state_attr
def async_added_to_hass(self): def async_added_to_hass(self):
"""Subscribe mqtt events. """Subscribe mqtt events.
@ -221,6 +275,8 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
self.async_alarm_arm_home(self._code) self.async_alarm_arm_home(self._code)
elif payload == self._payload_arm_away: elif payload == self._payload_arm_away:
self.async_alarm_arm_away(self._code) self.async_alarm_arm_away(self._code)
elif payload == self._payload_arm_night:
self.async_alarm_arm_night(self._code)
else: else:
_LOGGER.warning("Received unexpected payload: %s", payload) _LOGGER.warning("Received unexpected payload: %s", payload)
return return

View File

@ -6,7 +6,9 @@ from uuid import uuid4
from homeassistant.const import ( from homeassistant.const import (
ATTR_SUPPORTED_FEATURES, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF) ATTR_SUPPORTED_FEATURES, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF)
from homeassistant.components import switch, light from homeassistant.components import switch, light
from homeassistant.util.decorator import Registry
HANDLERS = Registry()
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
ATTR_HEADER = 'header' ATTR_HEADER = 'header'
@ -27,27 +29,13 @@ MAPPING_COMPONENT = {
} }
def mapping_api_function(name):
"""Return function pointer to api function for name.
Async friendly.
"""
mapping = {
'DiscoverAppliancesRequest': async_api_discovery,
'TurnOnRequest': async_api_turn_on,
'TurnOffRequest': async_api_turn_off,
'SetPercentageRequest': async_api_set_percentage,
}
return mapping.get(name, None)
@asyncio.coroutine @asyncio.coroutine
def async_handle_message(hass, message): def async_handle_message(hass, message):
"""Handle incomming API messages.""" """Handle incoming API messages."""
assert int(message[ATTR_HEADER][ATTR_PAYLOAD_VERSION]) == 2 assert int(message[ATTR_HEADER][ATTR_PAYLOAD_VERSION]) == 2
# Do we support this API request? # Do we support this API request?
funct_ref = mapping_api_function(message[ATTR_HEADER][ATTR_NAME]) funct_ref = HANDLERS.get(message[ATTR_HEADER][ATTR_NAME])
if not funct_ref: if not funct_ref:
_LOGGER.warning( _LOGGER.warning(
"Unsupported API request %s", message[ATTR_HEADER][ATTR_NAME]) "Unsupported API request %s", message[ATTR_HEADER][ATTR_NAME])
@ -57,14 +45,14 @@ def async_handle_message(hass, message):
def api_message(name, namespace, payload=None): def api_message(name, namespace, payload=None):
"""Create a API formated response message. """Create a API formatted response message.
Async friendly. Async friendly.
""" """
payload = payload or {} payload = payload or {}
return { return {
ATTR_HEADER: { ATTR_HEADER: {
ATTR_MESSAGE_ID: uuid4(), ATTR_MESSAGE_ID: str(uuid4()),
ATTR_NAME: name, ATTR_NAME: name,
ATTR_NAMESPACE: namespace, ATTR_NAMESPACE: namespace,
ATTR_PAYLOAD_VERSION: '2', ATTR_PAYLOAD_VERSION: '2',
@ -74,16 +62,17 @@ def api_message(name, namespace, payload=None):
def api_error(request, exc='DriverInternalError'): def api_error(request, exc='DriverInternalError'):
"""Create a API formated error response. """Create a API formatted error response.
Async friendly. Async friendly.
""" """
return api_message(exc, request[ATTR_HEADER][ATTR_NAMESPACE]) return api_message(exc, request[ATTR_HEADER][ATTR_NAMESPACE])
@HANDLERS.register('DiscoverAppliancesRequest')
@asyncio.coroutine @asyncio.coroutine
def async_api_discovery(hass, request): def async_api_discovery(hass, request):
"""Create a API formated discovery response. """Create a API formatted discovery response.
Async friendly. Async friendly.
""" """
@ -146,6 +135,7 @@ def extract_entity(funct):
return async_api_entity_wrapper return async_api_entity_wrapper
@HANDLERS.register('TurnOnRequest')
@extract_entity @extract_entity
@asyncio.coroutine @asyncio.coroutine
def async_api_turn_on(hass, request, entity): def async_api_turn_on(hass, request, entity):
@ -157,6 +147,7 @@ def async_api_turn_on(hass, request, entity):
return api_message('TurnOnConfirmation', 'Alexa.ConnectedHome.Control') return api_message('TurnOnConfirmation', 'Alexa.ConnectedHome.Control')
@HANDLERS.register('TurnOffRequest')
@extract_entity @extract_entity
@asyncio.coroutine @asyncio.coroutine
def async_api_turn_off(hass, request, entity): def async_api_turn_off(hass, request, entity):
@ -168,6 +159,7 @@ def async_api_turn_off(hass, request, entity):
return api_message('TurnOffConfirmation', 'Alexa.ConnectedHome.Control') return api_message('TurnOffConfirmation', 'Alexa.ConnectedHome.Control')
@HANDLERS.register('SetPercentageRequest')
@extract_entity @extract_entity
@asyncio.coroutine @asyncio.coroutine
def async_api_set_percentage(hass, request, entity): def async_api_set_percentage(hass, request, entity):

View File

@ -18,7 +18,7 @@ from homeassistant.helpers import discovery
from homeassistant.components.discovery import SERVICE_APPLE_TV from homeassistant.components.discovery import SERVICE_APPLE_TV
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pyatv==0.3.4'] REQUIREMENTS = ['pyatv==0.3.5']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@ -12,7 +12,7 @@ from requests.exceptions import HTTPError, ConnectTimeout
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
REQUIREMENTS = ['pyarlo==0.0.4'] REQUIREMENTS = ['pyarlo==0.0.6']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@ -55,12 +55,12 @@ class InsteonPLMBinarySensorDevice(BinarySensorDevice):
@property @property
def address(self): def address(self):
"""Return the the address of the node.""" """Return the address of the node."""
return self._address return self._address
@property @property
def name(self): def name(self):
"""Return the the name of the node.""" """Return the name of the node."""
return self._name return self._name
@property @property

View File

@ -0,0 +1,70 @@
"""
Support for Melnor RainCloud sprinkler water timer.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.raincloud/
"""
import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.raincloud import (
BINARY_SENSORS, DATA_RAINCLOUD, ICON_MAP, RainCloudEntity)
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.const import CONF_MONITORED_CONDITIONS
DEPENDENCIES = ['raincloud']
_LOGGER = logging.getLogger(__name__)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_MONITORED_CONDITIONS, default=list(BINARY_SENSORS)):
vol.All(cv.ensure_list, [vol.In(BINARY_SENSORS)]),
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up a sensor for a raincloud device."""
raincloud = hass.data[DATA_RAINCLOUD].data
sensors = []
for sensor_type in config.get(CONF_MONITORED_CONDITIONS):
if sensor_type == 'status':
sensors.append(
RainCloudBinarySensor(raincloud.controller, sensor_type))
sensors.append(
RainCloudBinarySensor(raincloud.controller.faucet,
sensor_type))
else:
# create an sensor for each zone managed by faucet
for zone in raincloud.controller.faucet.zones:
sensors.append(RainCloudBinarySensor(zone, sensor_type))
add_devices(sensors, True)
return True
class RainCloudBinarySensor(RainCloudEntity, BinarySensorDevice):
"""A sensor implementation for raincloud device."""
@property
def is_on(self):
"""Return true if the binary sensor is on."""
return self._state
def update(self):
"""Get the latest data and updates the state."""
_LOGGER.debug("Updating RainCloud sensor: %s", self._name)
self._state = getattr(self.data, self._sensor_type)
@property
def icon(self):
"""Return the icon of this device."""
if self._sensor_type == 'is_watering':
return 'mdi:water' if self.is_on else 'mdi:water-off'
elif self._sensor_type == 'status':
return 'mdi:pipe' if self.is_on else 'mdi:pipe-disconnected'
return ICON_MAP.get(self._sensor_type)

View File

@ -20,15 +20,18 @@ from homeassistant.helpers.event import async_track_state_change
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
ATTR_HYSTERESIS = 'hysteresis'
ATTR_SENSOR_VALUE = 'sensor_value' ATTR_SENSOR_VALUE = 'sensor_value'
ATTR_THRESHOLD = 'threshold' ATTR_THRESHOLD = 'threshold'
ATTR_TYPE = 'type' ATTR_TYPE = 'type'
CONF_HYSTERESIS = 'hysteresis'
CONF_LOWER = 'lower' CONF_LOWER = 'lower'
CONF_THRESHOLD = 'threshold' CONF_THRESHOLD = 'threshold'
CONF_UPPER = 'upper' CONF_UPPER = 'upper'
DEFAULT_NAME = 'Threshold' DEFAULT_NAME = 'Threshold'
DEFAULT_HYSTERESIS = 0.0
SENSOR_TYPES = [CONF_LOWER, CONF_UPPER] SENSOR_TYPES = [CONF_LOWER, CONF_UPPER]
@ -36,6 +39,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_ENTITY_ID): cv.entity_id, vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_THRESHOLD): vol.Coerce(float), vol.Required(CONF_THRESHOLD): vol.Coerce(float),
vol.Required(CONF_TYPE): vol.In(SENSOR_TYPES), vol.Required(CONF_TYPE): vol.In(SENSOR_TYPES),
vol.Optional(
CONF_HYSTERESIS, default=DEFAULT_HYSTERESIS): vol.Coerce(float),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
}) })
@ -47,28 +52,32 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
entity_id = config.get(CONF_ENTITY_ID) entity_id = config.get(CONF_ENTITY_ID)
name = config.get(CONF_NAME) name = config.get(CONF_NAME)
threshold = config.get(CONF_THRESHOLD) threshold = config.get(CONF_THRESHOLD)
hysteresis = config.get(CONF_HYSTERESIS)
limit_type = config.get(CONF_TYPE) limit_type = config.get(CONF_TYPE)
device_class = config.get(CONF_DEVICE_CLASS) device_class = config.get(CONF_DEVICE_CLASS)
async_add_devices( async_add_devices([ThresholdSensor(
[ThresholdSensor(hass, entity_id, name, threshold, limit_type, hass, entity_id, name, threshold,
device_class)], True) hysteresis, limit_type, device_class)
], True)
return True return True
class ThresholdSensor(BinarySensorDevice): class ThresholdSensor(BinarySensorDevice):
"""Representation of a Threshold sensor.""" """Representation of a Threshold sensor."""
def __init__(self, hass, entity_id, name, threshold, limit_type, def __init__(self, hass, entity_id, name, threshold,
device_class): hysteresis, limit_type, device_class):
"""Initialize the Threshold sensor.""" """Initialize the Threshold sensor."""
self._hass = hass self._hass = hass
self._entity_id = entity_id self._entity_id = entity_id
self.is_upper = limit_type == 'upper' self.is_upper = limit_type == 'upper'
self._name = name self._name = name
self._threshold = threshold self._threshold = threshold
self._hysteresis = hysteresis
self._device_class = device_class self._device_class = device_class
self._deviation = False self._state = False
self.sensor_value = 0 self.sensor_value = 0
@callback @callback
@ -97,7 +106,7 @@ class ThresholdSensor(BinarySensorDevice):
@property @property
def is_on(self): def is_on(self):
"""Return true if sensor is on.""" """Return true if sensor is on."""
return self._deviation return self._state
@property @property
def should_poll(self): def should_poll(self):
@ -116,13 +125,16 @@ class ThresholdSensor(BinarySensorDevice):
ATTR_ENTITY_ID: self._entity_id, ATTR_ENTITY_ID: self._entity_id,
ATTR_SENSOR_VALUE: self.sensor_value, ATTR_SENSOR_VALUE: self.sensor_value,
ATTR_THRESHOLD: self._threshold, ATTR_THRESHOLD: self._threshold,
ATTR_HYSTERESIS: self._hysteresis,
ATTR_TYPE: CONF_UPPER if self.is_upper else CONF_LOWER, ATTR_TYPE: CONF_UPPER if self.is_upper else CONF_LOWER,
} }
@asyncio.coroutine @asyncio.coroutine
def async_update(self): def async_update(self):
"""Get the latest data and updates the states.""" """Get the latest data and updates the states."""
if self.is_upper: if self._hysteresis == 0 and self.sensor_value == self._threshold:
self._deviation = bool(self.sensor_value > self._threshold) self._state = False
else: elif self.sensor_value > (self._threshold + self._hysteresis):
self._deviation = bool(self.sensor_value < self._threshold) self._state = self.is_upper
elif self.sensor_value < (self._threshold - self._hysteresis):
self._state = not self.is_upper

View File

@ -136,8 +136,9 @@ class WinkHub(WinkBinarySensorDevice):
def device_state_attributes(self): def device_state_attributes(self):
"""Return the state attributes.""" """Return the state attributes."""
return { return {
'update needed': self.wink.update_needed(), 'update_needed': self.wink.update_needed(),
'firmware version': self.wink.firmware_version() 'firmware_version': self.wink.firmware_version(),
'pairing_mode': self.wink.pairing_mode()
} }

View File

@ -277,7 +277,7 @@ class TodoistProjectData(object):
""" """
Class used by the Task Device service object to hold all Todoist Tasks. Class used by the Task Device service object to hold all Todoist Tasks.
This is analagous to the GoogleCalendarData found in the Google Calendar This is analogous to the GoogleCalendarData found in the Google Calendar
component. component.
Takes an object with a 'name' field and optionally an 'id' field (either Takes an object with a 'name' field and optionally an 'id' field (either

View File

@ -62,7 +62,7 @@ class AmcrestCam(Camera):
self._token = self._auth = authentication self._token = self._auth = authentication
def camera_image(self): def camera_image(self):
"""Return a still image reponse from the camera.""" """Return a still image response from the camera."""
# Send the request to snap a picture and return raw jpg data # Send the request to snap a picture and return raw jpg data
response = self._camera.snapshot(channel=self._resolution) response = self._camera.snapshot(channel=self._resolution)
return response.data return response.data

View File

@ -14,15 +14,31 @@ from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream
from homeassistant.components.arlo import DEFAULT_BRAND, DATA_ARLO from homeassistant.components.arlo import DEFAULT_BRAND, DATA_ARLO
from homeassistant.components.camera import Camera, PLATFORM_SCHEMA from homeassistant.components.camera import Camera, PLATFORM_SCHEMA
from homeassistant.components.ffmpeg import DATA_FFMPEG from homeassistant.components.ffmpeg import DATA_FFMPEG
from homeassistant.const import ATTR_BATTERY_LEVEL
DEPENDENCIES = ['arlo', 'ffmpeg'] DEPENDENCIES = ['arlo', 'ffmpeg']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
ATTR_BRIGHTNESS = 'brightness'
ATTR_FLIPPED = 'flipped'
ATTR_MIRRORED = 'mirrored'
ATTR_MOTION_SENSITIVITY = 'motion_detection_sensitivity'
ATTR_POWER_SAVE_MODE = 'power_save_mode'
ATTR_SIGNAL_STRENGTH = 'signal_strength'
ATTR_UNSEEN_VIDEOS = 'unseen_videos'
CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments'
ARLO_MODE_ARMED = 'armed' ARLO_MODE_ARMED = 'armed'
ARLO_MODE_DISARMED = 'disarmed' ARLO_MODE_DISARMED = 'disarmed'
POWERSAVE_MODE_MAPPING = {
1: 'best_battery_life',
2: 'optimized',
3: 'best_video'
}
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string, vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string,
}) })
@ -80,6 +96,28 @@ class ArloCam(Camera):
"""Return the name of this camera.""" """Return the name of this camera."""
return self._name return self._name
@property
def device_state_attributes(self):
"""Return the state attributes."""
return {
ATTR_BATTERY_LEVEL:
self._camera.get_battery_level,
ATTR_BRIGHTNESS:
self._camera.get_brightness,
ATTR_FLIPPED:
self._camera.get_flip_state,
ATTR_MIRRORED:
self._camera.get_mirror_state,
ATTR_MOTION_SENSITIVITY:
self._camera.get_motion_detection_sensitivity,
ATTR_POWER_SAVE_MODE:
POWERSAVE_MODE_MAPPING[self._camera.get_powersave_mode],
ATTR_SIGNAL_STRENGTH:
self._camera.get_signal_strength,
ATTR_UNSEEN_VIDEOS:
self._camera.unseen_videos
}
@property @property
def model(self): def model(self):
"""Camera model.""" """Camera model."""

View File

@ -76,6 +76,6 @@ class BlinkCamera(Camera):
return self.data.camera_thumbs[self._name] return self.data.camera_thumbs[self._name]
def camera_image(self): def camera_image(self):
"""Return a still image reponse from the camera.""" """Return a still image response from the camera."""
self.request_image() self.request_image()
return self.response.content return self.response.content

View File

@ -59,7 +59,7 @@ class FoscamCam(Camera):
self._password, verbose=False) self._password, verbose=False)
def camera_image(self): def camera_image(self):
"""Return a still image reponse from the camera.""" """Return a still image response from the camera."""
# Send the request to snap a picture and return raw jpg data # Send the request to snap a picture and return raw jpg data
# Handle exception if host is not reachable or url failed # Handle exception if host is not reachable or url failed
result, response = self._foscam_session.snap_picture_2() result, response = self._foscam_session.snap_picture_2()

View File

@ -7,44 +7,25 @@ https://home-assistant.io/components/camera.synology/
import asyncio import asyncio
import logging import logging
import requests
import voluptuous as vol import voluptuous as vol
import aiohttp
import async_timeout
from homeassistant.const import ( from homeassistant.const import (
CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_NAME, CONF_USERNAME, CONF_PASSWORD,
CONF_URL, CONF_WHITELIST, CONF_VERIFY_SSL, CONF_TIMEOUT) CONF_URL, CONF_WHITELIST, CONF_VERIFY_SSL, CONF_TIMEOUT)
from homeassistant.components.camera import ( from homeassistant.components.camera import (
Camera, PLATFORM_SCHEMA) Camera, PLATFORM_SCHEMA)
from homeassistant.helpers.aiohttp_client import ( from homeassistant.helpers.aiohttp_client import (
async_get_clientsession, async_create_clientsession, async_create_clientsession,
async_aiohttp_proxy_web) async_aiohttp_proxy_web)
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.util.async import run_coroutine_threadsafe
REQUIREMENTS = ['py-synology==0.1.1']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = 'Synology Camera' DEFAULT_NAME = 'Synology Camera'
DEFAULT_STREAM_ID = '0'
DEFAULT_TIMEOUT = 5 DEFAULT_TIMEOUT = 5
CONF_CAMERA_NAME = 'camera_name'
CONF_STREAM_ID = 'stream_id'
QUERY_CGI = 'query.cgi'
QUERY_API = 'SYNO.API.Info'
AUTH_API = 'SYNO.API.Auth'
CAMERA_API = 'SYNO.SurveillanceStation.Camera'
STREAMING_API = 'SYNO.SurveillanceStation.VideoStream'
SESSION_ID = '0'
WEBAPI_PATH = '/webapi/'
AUTH_PATH = 'auth.cgi'
CAMERA_PATH = 'camera.cgi'
STREAMING_PATH = 'SurveillanceStation/videoStreaming.cgi'
CONTENT_TYPE_HEADER = 'Content-Type'
SYNO_API_URL = '{0}{1}{2}'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
@ -62,189 +43,89 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up a Synology IP Camera.""" """Set up a Synology IP Camera."""
verify_ssl = config.get(CONF_VERIFY_SSL) verify_ssl = config.get(CONF_VERIFY_SSL)
timeout = config.get(CONF_TIMEOUT) timeout = config.get(CONF_TIMEOUT)
websession_init = async_get_clientsession(hass, verify_ssl)
# Determine API to use for authentication
syno_api_url = SYNO_API_URL.format(
config.get(CONF_URL), WEBAPI_PATH, QUERY_CGI)
query_payload = {
'api': QUERY_API,
'method': 'Query',
'version': '1',
'query': 'SYNO.'
}
try: try:
with async_timeout.timeout(timeout, loop=hass.loop): from synology.surveillance_station import SurveillanceStation
query_req = yield from websession_init.get( surveillance = SurveillanceStation(
syno_api_url, config.get(CONF_URL),
params=query_payload
)
# Skip content type check because Synology doesn't return JSON with
# right content type
query_resp = yield from query_req.json(content_type=None)
auth_path = query_resp['data'][AUTH_API]['path']
camera_api = query_resp['data'][CAMERA_API]['path']
camera_path = query_resp['data'][CAMERA_API]['path']
streaming_path = query_resp['data'][STREAMING_API]['path']
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.exception("Error on %s", syno_api_url)
return False
# Authticate to NAS to get a session id
syno_auth_url = SYNO_API_URL.format(
config.get(CONF_URL), WEBAPI_PATH, auth_path)
session_id = yield from get_session_id(
hass,
websession_init,
config.get(CONF_USERNAME), config.get(CONF_USERNAME),
config.get(CONF_PASSWORD), config.get(CONF_PASSWORD),
syno_auth_url, verify_ssl=verify_ssl,
timeout timeout=timeout
) )
except (requests.exceptions.RequestException, ValueError):
# init websession _LOGGER.exception("Error when initializing SurveillanceStation")
websession = async_create_clientsession(
hass, verify_ssl, cookies={'id': session_id})
# Use SessionID to get cameras in system
syno_camera_url = SYNO_API_URL.format(
config.get(CONF_URL), WEBAPI_PATH, camera_api)
camera_payload = {
'api': CAMERA_API,
'method': 'List',
'version': '1'
}
try:
with async_timeout.timeout(timeout, loop=hass.loop):
camera_req = yield from websession.get(
syno_camera_url,
params=camera_payload
)
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.exception("Error on %s", syno_camera_url)
return False return False
camera_resp = yield from camera_req.json(content_type=None) cameras = surveillance.get_all_cameras()
cameras = camera_resp['data']['cameras'] websession = async_create_clientsession(hass, verify_ssl)
# add cameras # add cameras
devices = [] devices = []
for camera in cameras: for camera in cameras:
if not config.get(CONF_WHITELIST): if not config.get(CONF_WHITELIST):
camera_id = camera['id'] device = SynologyCamera(websession, surveillance, camera.camera_id)
snapshot_path = camera['snapshot_path']
device = SynologyCamera(
hass, websession, config, camera_id, camera['name'],
snapshot_path, streaming_path, camera_path, auth_path, timeout
)
devices.append(device) devices.append(device)
async_add_devices(devices) async_add_devices(devices)
@asyncio.coroutine
def get_session_id(hass, websession, username, password, login_url, timeout):
"""Get a session id."""
auth_payload = {
'api': AUTH_API,
'method': 'Login',
'version': '2',
'account': username,
'passwd': password,
'session': 'SurveillanceStation',
'format': 'sid'
}
try:
with async_timeout.timeout(timeout, loop=hass.loop):
auth_req = yield from websession.get(
login_url,
params=auth_payload
)
auth_resp = yield from auth_req.json(content_type=None)
return auth_resp['data']['sid']
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.exception("Error on %s", login_url)
return False
class SynologyCamera(Camera): class SynologyCamera(Camera):
"""An implementation of a Synology NAS based IP camera.""" """An implementation of a Synology NAS based IP camera."""
def __init__(self, hass, websession, config, camera_id, def __init__(self, websession, surveillance, camera_id):
camera_name, snapshot_path, streaming_path, camera_path,
auth_path, timeout):
"""Initialize a Synology Surveillance Station camera.""" """Initialize a Synology Surveillance Station camera."""
super().__init__() super().__init__()
self.hass = hass
self._websession = websession self._websession = websession
self._name = camera_name self._surveillance = surveillance
self._synology_url = config.get(CONF_URL)
self._camera_name = config.get(CONF_CAMERA_NAME)
self._stream_id = config.get(CONF_STREAM_ID)
self._camera_id = camera_id self._camera_id = camera_id
self._snapshot_path = snapshot_path self._camera = self._surveillance.get_camera(camera_id)
self._streaming_path = streaming_path self._motion_setting = self._surveillance.get_motion_setting(camera_id)
self._camera_path = camera_path self.is_streaming = self._camera.is_enabled
self._auth_path = auth_path
self._timeout = timeout
def camera_image(self): def camera_image(self):
"""Return bytes of camera image.""" """Return bytes of camera image."""
return run_coroutine_threadsafe( return self._surveillance.get_camera_image(self._camera_id)
self.async_camera_image(), self.hass.loop).result()
@asyncio.coroutine
def async_camera_image(self):
"""Return a still image response from the camera."""
image_url = SYNO_API_URL.format(
self._synology_url, WEBAPI_PATH, self._camera_path)
image_payload = {
'api': CAMERA_API,
'method': 'GetSnapshot',
'version': '1',
'cameraId': self._camera_id
}
try:
with async_timeout.timeout(self._timeout, loop=self.hass.loop):
response = yield from self._websession.get(
image_url,
params=image_payload
)
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.error("Error fetching %s", image_url)
return None
image = yield from response.read()
return image
@asyncio.coroutine @asyncio.coroutine
def handle_async_mjpeg_stream(self, request): def handle_async_mjpeg_stream(self, request):
"""Return a MJPEG stream image response directly from the camera.""" """Return a MJPEG stream image response directly from the camera."""
streaming_url = SYNO_API_URL.format( streaming_url = self._camera.video_stream_url
self._synology_url, WEBAPI_PATH, self._streaming_path) stream_coro = self._websession.get(streaming_url)
streaming_payload = {
'api': STREAMING_API,
'method': 'Stream',
'version': '1',
'cameraId': self._camera_id,
'format': 'mjpeg'
}
stream_coro = self._websession.get(
streaming_url, params=streaming_payload)
yield from async_aiohttp_proxy_web(self.hass, request, stream_coro) yield from async_aiohttp_proxy_web(self.hass, request, stream_coro)
@property @property
def name(self): def name(self):
"""Return the name of this device.""" """Return the name of this device."""
return self._name return self._camera.name
@property
def is_recording(self):
"""Return true if the device is recording."""
return self._camera.is_recording
def should_poll(self):
"""Update the recording state periodically."""
return True
def update(self):
"""Update the status of the camera."""
self._surveillance.update()
self._camera = self._surveillance.get_camera(self._camera.camera_id)
self._motion_setting = self._surveillance.get_motion_setting(
self._camera.camera_id)
self.is_streaming = self._camera.is_enabled
@property
def motion_detection_enabled(self):
"""Return the camera motion detection status."""
return self._motion_setting.is_enabled
def enable_motion_detection(self):
"""Enable motion detection in the camera."""
self._surveillance.enable_motion_detection(self._camera_id)
def disable_motion_detection(self):
"""Disable motion detection in camera."""
self._surveillance.disable_motion_detection(self._camera_id)

View File

@ -44,6 +44,12 @@ STATE_IDLE = 'idle'
STATE_AUTO = 'auto' STATE_AUTO = 'auto'
STATE_DRY = 'dry' STATE_DRY = 'dry'
STATE_FAN_ONLY = 'fan_only' STATE_FAN_ONLY = 'fan_only'
STATE_ECO = 'eco'
STATE_ELECTRIC = 'electric'
STATE_PERFORMANCE = 'performance'
STATE_HIGH_DEMAND = 'high_demand'
STATE_HEAT_PUMP = 'heat_pump'
STATE_GAS = 'gas'
ATTR_CURRENT_TEMPERATURE = 'current_temperature' ATTR_CURRENT_TEMPERATURE = 'current_temperature'
ATTR_MAX_TEMP = 'max_temp' ATTR_MAX_TEMP = 'max_temp'
@ -147,7 +153,7 @@ def set_hold_mode(hass, hold_mode, entity_id=None):
@bind_hass @bind_hass
def set_aux_heat(hass, aux_heat, entity_id=None): def set_aux_heat(hass, aux_heat, entity_id=None):
"""Turn all or specified climate devices auxillary heater on.""" """Turn all or specified climate devices auxiliary heater on."""
data = { data = {
ATTR_AUX_HEAT: aux_heat ATTR_AUX_HEAT: aux_heat
} }
@ -661,22 +667,22 @@ class ClimateDevice(Entity):
return self.hass.async_add_job(self.set_hold_mode, hold_mode) return self.hass.async_add_job(self.set_hold_mode, hold_mode)
def turn_aux_heat_on(self): def turn_aux_heat_on(self):
"""Turn auxillary heater on.""" """Turn auxiliary heater on."""
raise NotImplementedError() raise NotImplementedError()
def async_turn_aux_heat_on(self): def async_turn_aux_heat_on(self):
"""Turn auxillary heater on. """Turn auxiliary heater on.
This method must be run in the event loop and returns a coroutine. This method must be run in the event loop and returns a coroutine.
""" """
return self.hass.async_add_job(self.turn_aux_heat_on) return self.hass.async_add_job(self.turn_aux_heat_on)
def turn_aux_heat_off(self): def turn_aux_heat_off(self):
"""Turn auxillary heater off.""" """Turn auxiliary heater off."""
raise NotImplementedError() raise NotImplementedError()
def async_turn_aux_heat_off(self): def async_turn_aux_heat_off(self):
"""Turn auxillary heater off. """Turn auxiliary heater off.
This method must be run in the event loop and returns a coroutine. This method must be run in the event loop and returns a coroutine.
""" """

View File

@ -114,7 +114,7 @@ class DemoClimate(ClimateDevice):
@property @property
def is_aux_heat_on(self): def is_aux_heat_on(self):
"""Return true if away mode is on.""" """Return true if aux heat is on."""
return self._aux return self._aux
@property @property
@ -183,11 +183,11 @@ class DemoClimate(ClimateDevice):
self.schedule_update_ha_state() self.schedule_update_ha_state()
def turn_aux_heat_on(self): def turn_aux_heat_on(self):
"""Turn away auxillary heater on.""" """Turn auxillary heater on."""
self._aux = True self._aux = True
self.schedule_update_ha_state() self.schedule_update_ha_state()
def turn_aux_heat_off(self): def turn_aux_heat_off(self):
"""Turn auxillary heater off.""" """Turn auxiliary heater off."""
self._aux = False self._aux = False
self.schedule_update_ha_state() self.schedule_update_ha_state()

View File

@ -27,6 +27,7 @@ ATTR_RESUME_ALL = 'resume_all'
DEFAULT_RESUME_ALL = False DEFAULT_RESUME_ALL = False
TEMPERATURE_HOLD = 'temp' TEMPERATURE_HOLD = 'temp'
VACATION_HOLD = 'vacation' VACATION_HOLD = 'vacation'
AWAY_MODE = 'awayMode'
DEPENDENCIES = ['ecobee'] DEPENDENCIES = ['ecobee']
@ -144,20 +145,20 @@ class Thermostat(ClimateDevice):
@property @property
def current_temperature(self): def current_temperature(self):
"""Return the current temperature.""" """Return the current temperature."""
return self.thermostat['runtime']['actualTemperature'] / 10 return self.thermostat['runtime']['actualTemperature'] / 10.0
@property @property
def target_temperature_low(self): def target_temperature_low(self):
"""Return the lower bound temperature we try to reach.""" """Return the lower bound temperature we try to reach."""
if self.current_operation == STATE_AUTO: if self.current_operation == STATE_AUTO:
return int(self.thermostat['runtime']['desiredHeat'] / 10) return self.thermostat['runtime']['desiredHeat'] / 10.0
return None return None
@property @property
def target_temperature_high(self): def target_temperature_high(self):
"""Return the upper bound temperature we try to reach.""" """Return the upper bound temperature we try to reach."""
if self.current_operation == STATE_AUTO: if self.current_operation == STATE_AUTO:
return int(self.thermostat['runtime']['desiredCool'] / 10) return self.thermostat['runtime']['desiredCool'] / 10.0
return None return None
@property @property
@ -166,9 +167,9 @@ class Thermostat(ClimateDevice):
if self.current_operation == STATE_AUTO: if self.current_operation == STATE_AUTO:
return None return None
if self.current_operation == STATE_HEAT: if self.current_operation == STATE_HEAT:
return int(self.thermostat['runtime']['desiredHeat'] / 10) return self.thermostat['runtime']['desiredHeat'] / 10.0
elif self.current_operation == STATE_COOL: elif self.current_operation == STATE_COOL:
return int(self.thermostat['runtime']['desiredCool'] / 10) return self.thermostat['runtime']['desiredCool'] / 10.0
return None return None
@property @property
@ -186,6 +187,11 @@ class Thermostat(ClimateDevice):
@property @property
def current_hold_mode(self): def current_hold_mode(self):
"""Return current hold mode.""" """Return current hold mode."""
mode = self._current_hold_mode
return None if mode == AWAY_MODE else mode
@property
def _current_hold_mode(self):
events = self.thermostat['events'] events = self.thermostat['events']
for event in events: for event in events:
if event['running']: if event['running']:
@ -195,8 +201,8 @@ class Thermostat(ClimateDevice):
int(event['startDate'][0:4]) <= 1: int(event['startDate'][0:4]) <= 1:
# A temporary hold from away climate is a hold # A temporary hold from away climate is a hold
return 'away' return 'away'
# A permanent hold from away climate is away_mode # A permanent hold from away climate
return None return AWAY_MODE
elif event['holdClimateRef'] != "": elif event['holdClimateRef'] != "":
# Any other hold based on climate # Any other hold based on climate
return event['holdClimateRef'] return event['holdClimateRef']
@ -269,7 +275,7 @@ class Thermostat(ClimateDevice):
@property @property
def is_away_mode_on(self): def is_away_mode_on(self):
"""Return true if away mode is on.""" """Return true if away mode is on."""
return self.current_hold_mode == 'away' return self._current_hold_mode == AWAY_MODE
@property @property
def is_aux_heat_on(self): def is_aux_heat_on(self):
@ -277,12 +283,17 @@ class Thermostat(ClimateDevice):
return 'auxHeat' in self.thermostat['equipmentStatus'] return 'auxHeat' in self.thermostat['equipmentStatus']
def turn_away_mode_on(self): def turn_away_mode_on(self):
"""Turn away on.""" """Turn away mode on by setting it on away hold indefinitely."""
self.set_hold_mode('away') if self._current_hold_mode != AWAY_MODE:
self.data.ecobee.set_climate_hold(self.thermostat_index, 'away',
'indefinite')
self.update_without_throttle = True
def turn_away_mode_off(self): def turn_away_mode_off(self):
"""Turn away off.""" """Turn away off."""
self.set_hold_mode(None) if self._current_hold_mode == AWAY_MODE:
self.data.ecobee.resume_program(self.thermostat_index)
self.update_without_throttle = True
def set_hold_mode(self, hold_mode): def set_hold_mode(self, hold_mode):
"""Set hold mode (away, home, temp, sleep, etc.).""" """Set hold mode (away, home, temp, sleep, etc.)."""
@ -299,7 +310,7 @@ class Thermostat(ClimateDevice):
self.data.ecobee.resume_program(self.thermostat_index) self.data.ecobee.resume_program(self.thermostat_index)
else: else:
if hold_mode == TEMPERATURE_HOLD: if hold_mode == TEMPERATURE_HOLD:
self.set_temp_hold(int(self.current_temperature)) self.set_temp_hold(self.current_temperature)
else: else:
self.data.ecobee.set_climate_hold( self.data.ecobee.set_climate_hold(
self.thermostat_index, hold_mode, self.hold_preference()) self.thermostat_index, hold_mode, self.hold_preference())
@ -325,15 +336,11 @@ class Thermostat(ClimateDevice):
elif self.current_operation == STATE_COOL: elif self.current_operation == STATE_COOL:
heat_temp = temp - 20 heat_temp = temp - 20
cool_temp = temp cool_temp = temp
else:
self.data.ecobee.set_hold_temp(self.thermostat_index, cool_temp, # In auto mode set temperature between
heat_temp, self.hold_preference()) heat_temp = temp - 10
_LOGGER.debug("Setting ecobee hold_temp to: low=%s, is=%s, " cool_temp = temp + 10
"cool=%s, is=%s", heat_temp, isinstance( self.set_auto_temp_hold(heat_temp, cool_temp)
heat_temp, (int, float)), cool_temp,
isinstance(cool_temp, (int, float)))
self.update_without_throttle = True
def set_temperature(self, **kwargs): def set_temperature(self, **kwargs):
"""Set new target temperature.""" """Set new target temperature."""
@ -343,9 +350,9 @@ class Thermostat(ClimateDevice):
if self.current_operation == STATE_AUTO and low_temp is not None \ if self.current_operation == STATE_AUTO and low_temp is not None \
and high_temp is not None: and high_temp is not None:
self.set_auto_temp_hold(int(low_temp), int(high_temp)) self.set_auto_temp_hold(low_temp, high_temp)
elif temp is not None: elif temp is not None:
self.set_temp_hold(int(temp)) self.set_temp_hold(temp)
else: else:
_LOGGER.error( _LOGGER.error(
"Missing valid arguments for set_temperature in %s", kwargs) "Missing valid arguments for set_temperature in %s", kwargs)
@ -364,7 +371,7 @@ class Thermostat(ClimateDevice):
def resume_program(self, resume_all): def resume_program(self, resume_all):
"""Resume the thermostat schedule program.""" """Resume the thermostat schedule program."""
self.data.ecobee.resume_program( self.data.ecobee.resume_program(
self.thermostat_index, str(resume_all).lower()) self.thermostat_index, 'true' if resume_all else 'false')
self.update_without_throttle = True self.update_without_throttle = True
def hold_preference(self): def hold_preference(self):

View File

@ -0,0 +1,483 @@
"""
Support for MQTT climate devices.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/climate.mqtt/
"""
import asyncio
import logging
import voluptuous as vol
from homeassistant.core import callback
import homeassistant.components.mqtt as mqtt
from homeassistant.components.climate import (
STATE_HEAT, STATE_COOL, STATE_DRY, STATE_FAN_ONLY, ClimateDevice,
PLATFORM_SCHEMA as CLIMATE_PLATFORM_SCHEMA, STATE_AUTO,
ATTR_OPERATION_MODE)
from homeassistant.const import (
STATE_ON, STATE_OFF, ATTR_TEMPERATURE, CONF_NAME)
from homeassistant.components.mqtt import (CONF_QOS, CONF_RETAIN)
import homeassistant.helpers.config_validation as cv
from homeassistant.components.fan import (SPEED_LOW, SPEED_MEDIUM,
SPEED_HIGH)
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['mqtt']
DEFAULT_NAME = 'MQTT HVAC'
CONF_POWER_COMMAND_TOPIC = 'power_command_topic'
CONF_POWER_STATE_TOPIC = 'power_state_topic'
CONF_MODE_COMMAND_TOPIC = 'mode_command_topic'
CONF_MODE_STATE_TOPIC = 'mode_state_topic'
CONF_TEMPERATURE_COMMAND_TOPIC = 'temperature_command_topic'
CONF_TEMPERATURE_STATE_TOPIC = 'temperature_state_topic'
CONF_FAN_MODE_COMMAND_TOPIC = 'fan_mode_command_topic'
CONF_FAN_MODE_STATE_TOPIC = 'fan_mode_state_topic'
CONF_SWING_MODE_COMMAND_TOPIC = 'swing_mode_command_topic'
CONF_SWING_MODE_STATE_TOPIC = 'swing_mode_state_topic'
CONF_AWAY_MODE_COMMAND_TOPIC = 'away_mode_command_topic'
CONF_AWAY_MODE_STATE_TOPIC = 'away_mode_state_topic'
CONF_HOLD_COMMAND_TOPIC = 'hold_command_topic'
CONF_HOLD_STATE_TOPIC = 'hold_state_topic'
CONF_AUX_COMMAND_TOPIC = 'aux_command_topic'
CONF_AUX_STATE_TOPIC = 'aux_state_topic'
CONF_CURRENT_TEMPERATURE_TOPIC = 'current_temperature_topic'
CONF_PAYLOAD_ON = 'payload_on'
CONF_PAYLOAD_OFF = 'payload_off'
CONF_FAN_MODE_LIST = 'fan_modes'
CONF_MODE_LIST = 'modes'
CONF_SWING_MODE_LIST = 'swing_modes'
CONF_INITIAL = 'initial'
CONF_SEND_IF_OFF = 'send_if_off'
PLATFORM_SCHEMA = CLIMATE_PLATFORM_SCHEMA.extend({
vol.Optional(CONF_POWER_COMMAND_TOPIC): mqtt.valid_publish_topic,
vol.Optional(CONF_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic,
vol.Optional(CONF_TEMPERATURE_COMMAND_TOPIC): mqtt.valid_publish_topic,
vol.Optional(CONF_FAN_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic,
vol.Optional(CONF_SWING_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic,
vol.Optional(CONF_AWAY_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic,
vol.Optional(CONF_HOLD_COMMAND_TOPIC): mqtt.valid_publish_topic,
vol.Optional(CONF_AUX_COMMAND_TOPIC): mqtt.valid_publish_topic,
vol.Optional(CONF_POWER_STATE_TOPIC): mqtt.valid_subscribe_topic,
vol.Optional(CONF_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic,
vol.Optional(CONF_TEMPERATURE_STATE_TOPIC): mqtt.valid_subscribe_topic,
vol.Optional(CONF_FAN_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic,
vol.Optional(CONF_SWING_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic,
vol.Optional(CONF_AWAY_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic,
vol.Optional(CONF_HOLD_STATE_TOPIC): mqtt.valid_subscribe_topic,
vol.Optional(CONF_AUX_STATE_TOPIC): mqtt.valid_subscribe_topic,
vol.Optional(CONF_CURRENT_TEMPERATURE_TOPIC):
mqtt.valid_subscribe_topic,
vol.Optional(CONF_FAN_MODE_LIST,
default=[STATE_AUTO, SPEED_LOW,
SPEED_MEDIUM, SPEED_HIGH]): cv.ensure_list,
vol.Optional(CONF_SWING_MODE_LIST,
default=[STATE_ON, STATE_OFF]): cv.ensure_list,
vol.Optional(CONF_MODE_LIST,
default=[STATE_AUTO, STATE_OFF, STATE_COOL, STATE_HEAT,
STATE_DRY, STATE_FAN_ONLY]): cv.ensure_list,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_INITIAL, default=21): cv.positive_int,
vol.Optional(CONF_SEND_IF_OFF, default=True): cv.boolean,
vol.Optional(CONF_PAYLOAD_ON, default="ON"): cv.string,
vol.Optional(CONF_PAYLOAD_OFF, default="OFF"): cv.string,
})
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the MQTT climate devices."""
async_add_devices([
MqttClimate(
hass,
config.get(CONF_NAME),
{
key: config.get(key) for key in (
CONF_POWER_COMMAND_TOPIC,
CONF_MODE_COMMAND_TOPIC,
CONF_TEMPERATURE_COMMAND_TOPIC,
CONF_FAN_MODE_COMMAND_TOPIC,
CONF_SWING_MODE_COMMAND_TOPIC,
CONF_AWAY_MODE_COMMAND_TOPIC,
CONF_HOLD_COMMAND_TOPIC,
CONF_AUX_COMMAND_TOPIC,
CONF_POWER_STATE_TOPIC,
CONF_MODE_STATE_TOPIC,
CONF_TEMPERATURE_STATE_TOPIC,
CONF_FAN_MODE_STATE_TOPIC,
CONF_SWING_MODE_STATE_TOPIC,
CONF_AWAY_MODE_STATE_TOPIC,
CONF_HOLD_STATE_TOPIC,
CONF_AUX_STATE_TOPIC,
CONF_CURRENT_TEMPERATURE_TOPIC
)
},
config.get(CONF_QOS),
config.get(CONF_RETAIN),
config.get(CONF_MODE_LIST),
config.get(CONF_FAN_MODE_LIST),
config.get(CONF_SWING_MODE_LIST),
config.get(CONF_INITIAL),
False, None, SPEED_LOW,
STATE_OFF, STATE_OFF, False,
config.get(CONF_SEND_IF_OFF),
config.get(CONF_PAYLOAD_ON),
config.get(CONF_PAYLOAD_OFF))
])
class MqttClimate(ClimateDevice):
"""Representation of a demo climate device."""
def __init__(self, hass, name, topic, qos, retain, mode_list,
fan_mode_list, swing_mode_list, target_temperature, away,
hold, current_fan_mode, current_swing_mode,
current_operation, aux, send_if_off, payload_on,
payload_off):
"""Initialize the climate device."""
self.hass = hass
self._name = name
self._topic = topic
self._qos = qos
self._retain = retain
self._target_temperature = target_temperature
self._unit_of_measurement = hass.config.units.temperature_unit
self._away = away
self._hold = hold
self._current_temperature = None
self._current_fan_mode = current_fan_mode
self._current_operation = current_operation
self._aux = aux
self._current_swing_mode = current_swing_mode
self._fan_list = fan_mode_list
self._operation_list = mode_list
self._swing_list = swing_mode_list
self._target_temperature_step = 1
self._send_if_off = send_if_off
self._payload_on = payload_on
self._payload_off = payload_off
def async_added_to_hass(self):
"""Handle being added to home assistant."""
@callback
def handle_current_temp_received(topic, payload, qos):
"""Handle current temperature coming via MQTT."""
try:
self._current_temperature = float(payload)
self.async_schedule_update_ha_state()
except ValueError:
_LOGGER.error("Could not parse temperature from %s", payload)
if self._topic[CONF_CURRENT_TEMPERATURE_TOPIC] is not None:
yield from mqtt.async_subscribe(
self.hass, self._topic[CONF_CURRENT_TEMPERATURE_TOPIC],
handle_current_temp_received, self._qos)
@callback
def handle_mode_received(topic, payload, qos):
"""Handle receiving mode via MQTT."""
if payload not in self._operation_list:
_LOGGER.error("Invalid mode: %s", payload)
else:
self._current_operation = payload
self.async_schedule_update_ha_state()
if self._topic[CONF_MODE_STATE_TOPIC] is not None:
yield from mqtt.async_subscribe(
self.hass, self._topic[CONF_MODE_STATE_TOPIC],
handle_mode_received, self._qos)
@callback
def handle_temperature_received(topic, payload, qos):
"""Handle target temperature coming via MQTT."""
try:
self._target_temperature = float(payload)
self.async_schedule_update_ha_state()
except ValueError:
_LOGGER.error("Could not parse temperature from %s", payload)
if self._topic[CONF_TEMPERATURE_STATE_TOPIC] is not None:
yield from mqtt.async_subscribe(
self.hass, self._topic[CONF_TEMPERATURE_STATE_TOPIC],
handle_temperature_received, self._qos)
@callback
def handle_fan_mode_received(topic, payload, qos):
"""Handle receiving fan mode via MQTT."""
if payload not in self._fan_list:
_LOGGER.error("Invalid fan mode: %s", payload)
else:
self._current_fan_mode = payload
self.async_schedule_update_ha_state()
if self._topic[CONF_FAN_MODE_STATE_TOPIC] is not None:
yield from mqtt.async_subscribe(
self.hass, self._topic[CONF_FAN_MODE_STATE_TOPIC],
handle_fan_mode_received, self._qos)
@callback
def handle_swing_mode_received(topic, payload, qos):
"""Handle receiving swing mode via MQTT."""
if payload not in self._swing_list:
_LOGGER.error("Invalid swing mode: %s", payload)
else:
self._current_swing_mode = payload
self.async_schedule_update_ha_state()
if self._topic[CONF_SWING_MODE_STATE_TOPIC] is not None:
yield from mqtt.async_subscribe(
self.hass, self._topic[CONF_SWING_MODE_STATE_TOPIC],
handle_swing_mode_received, self._qos)
@callback
def handle_away_mode_received(topic, payload, qos):
"""Handle receiving away mode via MQTT."""
if payload == self._payload_on:
self._away = True
elif payload == self._payload_off:
self._away = False
else:
_LOGGER.error("Invalid away mode: %s", payload)
self.async_schedule_update_ha_state()
if self._topic[CONF_AWAY_MODE_STATE_TOPIC] is not None:
yield from mqtt.async_subscribe(
self.hass, self._topic[CONF_AWAY_MODE_STATE_TOPIC],
handle_away_mode_received, self._qos)
@callback
def handle_aux_mode_received(topic, payload, qos):
"""Handle receiving aux mode via MQTT."""
if payload == self._payload_on:
self._aux = True
elif payload == self._payload_off:
self._aux = False
else:
_LOGGER.error("Invalid aux mode: %s", payload)
self.async_schedule_update_ha_state()
if self._topic[CONF_AUX_STATE_TOPIC] is not None:
yield from mqtt.async_subscribe(
self.hass, self._topic[CONF_AUX_STATE_TOPIC],
handle_aux_mode_received, self._qos)
@callback
def handle_hold_mode_received(topic, payload, qos):
"""Handle receiving hold mode via MQTT."""
self._hold = payload
self.async_schedule_update_ha_state()
if self._topic[CONF_HOLD_STATE_TOPIC] is not None:
yield from mqtt.async_subscribe(
self.hass, self._topic[CONF_HOLD_STATE_TOPIC],
handle_hold_mode_received, self._qos)
@property
def should_poll(self):
"""Return the polling state."""
return False
@property
def name(self):
"""Return the name of the climate device."""
return self._name
@property
def temperature_unit(self):
"""Return the unit of measurement."""
return self._unit_of_measurement
@property
def current_temperature(self):
"""Return the current temperature."""
return self._current_temperature
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
return self._target_temperature
@property
def current_operation(self):
"""Return current operation ie. heat, cool, idle."""
return self._current_operation
@property
def operation_list(self):
"""Return the list of available operation modes."""
return self._operation_list
@property
def target_temperature_step(self):
"""Return the supported step of target temperature."""
return self._target_temperature_step
@property
def is_away_mode_on(self):
"""Return if away mode is on."""
return self._away
@property
def current_hold_mode(self):
"""Return hold mode setting."""
return self._hold
@property
def is_aux_heat_on(self):
"""Return true if away mode is on."""
return self._aux
@property
def current_fan_mode(self):
"""Return the fan setting."""
return self._current_fan_mode
@property
def fan_list(self):
"""Return the list of available fan modes."""
return self._fan_list
@asyncio.coroutine
def async_set_temperature(self, **kwargs):
"""Set new target temperatures."""
if kwargs.get(ATTR_OPERATION_MODE) is not None:
operation_mode = kwargs.get(ATTR_OPERATION_MODE)
yield from self.async_set_operation_mode(operation_mode)
if kwargs.get(ATTR_TEMPERATURE) is not None:
if self._topic[CONF_TEMPERATURE_STATE_TOPIC] is None:
# optimistic mode
self._target_temperature = kwargs.get(ATTR_TEMPERATURE)
if self._send_if_off or self._current_operation != STATE_OFF:
mqtt.async_publish(
self.hass, self._topic[CONF_TEMPERATURE_COMMAND_TOPIC],
kwargs.get(ATTR_TEMPERATURE), self._qos, self._retain)
self.async_schedule_update_ha_state()
@asyncio.coroutine
def async_set_swing_mode(self, swing_mode):
"""Set new swing mode."""
if self._send_if_off or self._current_operation != STATE_OFF:
mqtt.async_publish(
self.hass, self._topic[CONF_SWING_MODE_COMMAND_TOPIC],
swing_mode, self._qos, self._retain)
if self._topic[CONF_SWING_MODE_STATE_TOPIC] is None:
self._current_swing_mode = swing_mode
self.async_schedule_update_ha_state()
@asyncio.coroutine
def async_set_fan_mode(self, fan):
"""Set new target temperature."""
if self._send_if_off or self._current_operation != STATE_OFF:
mqtt.async_publish(
self.hass, self._topic[CONF_FAN_MODE_COMMAND_TOPIC],
fan, self._qos, self._retain)
if self._topic[CONF_FAN_MODE_STATE_TOPIC] is None:
self._current_fan_mode = fan
self.async_schedule_update_ha_state()
@asyncio.coroutine
def async_set_operation_mode(self, operation_mode) -> None:
"""Set new operation mode."""
if self._topic[CONF_POWER_COMMAND_TOPIC] is not None:
if (self._current_operation == STATE_OFF and
operation_mode != STATE_OFF):
mqtt.async_publish(
self.hass, self._topic[CONF_POWER_COMMAND_TOPIC],
self._payload_on, self._qos, self._retain)
elif (self._current_operation != STATE_OFF and
operation_mode == STATE_OFF):
mqtt.async_publish(
self.hass, self._topic[CONF_POWER_COMMAND_TOPIC],
self._payload_off, self._qos, self._retain)
if self._topic[CONF_MODE_COMMAND_TOPIC] is not None:
mqtt.async_publish(
self.hass, self._topic[CONF_MODE_COMMAND_TOPIC],
operation_mode, self._qos, self._retain)
if self._topic[CONF_MODE_STATE_TOPIC] is None:
self._current_operation = operation_mode
self.async_schedule_update_ha_state()
@property
def current_swing_mode(self):
"""Return the swing setting."""
return self._current_swing_mode
@property
def swing_list(self):
"""List of available swing modes."""
return self._swing_list
@asyncio.coroutine
def async_turn_away_mode_on(self):
"""Turn away mode on."""
if self._topic[CONF_AWAY_MODE_COMMAND_TOPIC] is not None:
mqtt.async_publish(self.hass,
self._topic[CONF_AWAY_MODE_COMMAND_TOPIC],
self._payload_on, self._qos, self._retain)
if self._topic[CONF_AWAY_MODE_STATE_TOPIC] is None:
self._away = True
self.async_schedule_update_ha_state()
@asyncio.coroutine
def async_turn_away_mode_off(self):
"""Turn away mode off."""
if self._topic[CONF_AWAY_MODE_COMMAND_TOPIC] is not None:
mqtt.async_publish(self.hass,
self._topic[CONF_AWAY_MODE_COMMAND_TOPIC],
self._payload_off, self._qos, self._retain)
if self._topic[CONF_AWAY_MODE_STATE_TOPIC] is None:
self._away = False
self.async_schedule_update_ha_state()
@asyncio.coroutine
def async_set_hold_mode(self, hold):
"""Update hold mode on."""
if self._topic[CONF_HOLD_COMMAND_TOPIC] is not None:
mqtt.async_publish(self.hass,
self._topic[CONF_HOLD_COMMAND_TOPIC],
hold, self._qos, self._retain)
if self._topic[CONF_HOLD_STATE_TOPIC] is None:
self._hold = hold
self.async_schedule_update_ha_state()
@asyncio.coroutine
def async_turn_aux_heat_on(self):
"""Turn auxillary heater on."""
if self._topic[CONF_AUX_COMMAND_TOPIC] is not None:
mqtt.async_publish(self.hass, self._topic[CONF_AUX_COMMAND_TOPIC],
self._payload_on, self._qos, self._retain)
if self._topic[CONF_AUX_STATE_TOPIC] is None:
self._aux = True
self.async_schedule_update_ha_state()
@asyncio.coroutine
def async_turn_aux_heat_off(self):
"""Turn auxillary heater off."""
if self._topic[CONF_AUX_COMMAND_TOPIC] is not None:
mqtt.async_publish(self.hass, self._topic[CONF_AUX_COMMAND_TOPIC],
self._payload_off, self._qos, self._retain)
if self._topic[CONF_AUX_STATE_TOPIC] is None:
self._aux = False
self.async_schedule_update_ha_state()

View File

@ -1,5 +1,5 @@
set_aux_heat: set_aux_heat:
description: Turn auxillary heater on/off for climate device description: Turn auxiliary heater on/off for climate device
fields: fields:
entity_id: entity_id:

View File

@ -1,30 +1,45 @@
""" """
Support for Wink thermostats. Support for Wink thermostats, Air Conditioners, and Water Heaters.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/climate.wink/ https://home-assistant.io/components/climate.wink/
""" """
import logging
import asyncio import asyncio
from homeassistant.components.wink import WinkDevice, DOMAIN from homeassistant.components.wink import WinkDevice, DOMAIN
from homeassistant.components.climate import ( from homeassistant.components.climate import (
STATE_AUTO, STATE_COOL, STATE_HEAT, ClimateDevice, STATE_AUTO, STATE_COOL, STATE_HEAT, ClimateDevice,
ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW,
ATTR_TEMPERATURE, ATTR_TEMPERATURE, STATE_FAN_ONLY,
ATTR_CURRENT_HUMIDITY) ATTR_CURRENT_HUMIDITY, STATE_ECO, STATE_ELECTRIC,
STATE_PERFORMANCE, STATE_HIGH_DEMAND,
STATE_HEAT_PUMP, STATE_GAS)
from homeassistant.const import ( from homeassistant.const import (
TEMP_CELSIUS, STATE_ON, TEMP_CELSIUS, STATE_ON,
STATE_OFF, STATE_UNKNOWN) STATE_OFF, STATE_UNKNOWN)
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['wink'] DEPENDENCIES = ['wink']
STATE_AUX = 'aux'
STATE_ECO = 'eco'
STATE_FAN = 'fan'
SPEED_LOW = 'low' SPEED_LOW = 'low'
SPEED_MEDIUM = 'medium' SPEED_MEDIUM = 'medium'
SPEED_HIGH = 'high' SPEED_HIGH = 'high'
HA_STATE_TO_WINK = {STATE_AUTO: 'auto',
STATE_ECO: 'eco',
STATE_FAN_ONLY: 'fan_only',
STATE_HEAT: 'heat_only',
STATE_COOL: 'cool_only',
STATE_PERFORMANCE: 'performance',
STATE_HIGH_DEMAND: 'high_demand',
STATE_HEAT_PUMP: 'heat_pump',
STATE_ELECTRIC: 'electric_only',
STATE_GAS: 'gas',
STATE_OFF: 'off'}
WINK_STATE_TO_HA = {value: key for key, value in HA_STATE_TO_WINK.items()}
ATTR_EXTERNAL_TEMPERATURE = "external_temperature" ATTR_EXTERNAL_TEMPERATURE = "external_temperature"
ATTR_SMART_TEMPERATURE = "smart_temperature" ATTR_SMART_TEMPERATURE = "smart_temperature"
ATTR_ECO_TARGET = "eco_target" ATTR_ECO_TARGET = "eco_target"
@ -32,28 +47,26 @@ ATTR_OCCUPIED = "occupied"
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Wink thermostat.""" """Set up the Wink climate devices."""
import pywink import pywink
temp_unit = hass.config.units.temperature_unit
for climate in pywink.get_thermostats(): for climate in pywink.get_thermostats():
_id = climate.object_id() + climate.name() _id = climate.object_id() + climate.name()
if _id not in hass.data[DOMAIN]['unique_ids']: if _id not in hass.data[DOMAIN]['unique_ids']:
add_devices([WinkThermostat(climate, hass, temp_unit)]) add_devices([WinkThermostat(climate, hass)])
for climate in pywink.get_air_conditioners(): for climate in pywink.get_air_conditioners():
_id = climate.object_id() + climate.name() _id = climate.object_id() + climate.name()
if _id not in hass.data[DOMAIN]['unique_ids']: if _id not in hass.data[DOMAIN]['unique_ids']:
add_devices([WinkAC(climate, hass, temp_unit)]) add_devices([WinkAC(climate, hass)])
for water_heater in pywink.get_water_heaters():
_id = water_heater.object_id() + water_heater.name()
if _id not in hass.data[DOMAIN]['unique_ids']:
add_devices([WinkWaterHeater(water_heater, hass)])
# pylint: disable=abstract-method # pylint: disable=abstract-method
class WinkThermostat(WinkDevice, ClimateDevice): class WinkThermostat(WinkDevice, ClimateDevice):
"""Representation of a Wink thermostat.""" """Representation of a Wink thermostat."""
def __init__(self, wink, hass, temp_unit):
"""Initialize the Wink device."""
super().__init__(wink, hass)
self._config_temp_unit = temp_unit
@asyncio.coroutine @asyncio.coroutine
def async_added_to_hass(self): def async_added_to_hass(self):
"""Callback when entity is added to hass.""" """Callback when entity is added to hass."""
@ -139,17 +152,11 @@ class WinkThermostat(WinkDevice, ClimateDevice):
"""Return current operation ie. heat, cool, idle.""" """Return current operation ie. heat, cool, idle."""
if not self.wink.is_on(): if not self.wink.is_on():
current_op = STATE_OFF current_op = STATE_OFF
elif self.wink.current_hvac_mode() == 'cool_only':
current_op = STATE_COOL
elif self.wink.current_hvac_mode() == 'heat_only':
current_op = STATE_HEAT
elif self.wink.current_hvac_mode() == 'aux':
current_op = STATE_HEAT
elif self.wink.current_hvac_mode() == 'auto':
current_op = STATE_AUTO
elif self.wink.current_hvac_mode() == 'eco':
current_op = STATE_ECO
else: else:
current_op = WINK_STATE_TO_HA.get(self.wink.current_hvac_mode())
if current_op == 'aux':
return STATE_HEAT
if current_op is None:
current_op = STATE_UNKNOWN current_op = STATE_UNKNOWN
return current_op return current_op
@ -199,12 +206,13 @@ class WinkThermostat(WinkDevice, ClimateDevice):
@property @property
def is_aux_heat_on(self): def is_aux_heat_on(self):
"""Return true if aux heater.""" """Return true if aux heater."""
if self.wink.current_hvac_mode() == 'aux' and self.wink.is_on(): if 'aux' not in self.wink.hvac_modes():
return True
elif self.wink.current_hvac_mode() == 'aux' and not self.wink.is_on():
return False
return None return None
if self.wink.current_hvac_mode() == 'aux':
return True
return False
def set_temperature(self, **kwargs): def set_temperature(self, **kwargs):
"""Set new target temperature.""" """Set new target temperature."""
target_temp = kwargs.get(ATTR_TEMPERATURE) target_temp = kwargs.get(ATTR_TEMPERATURE)
@ -223,32 +231,27 @@ class WinkThermostat(WinkDevice, ClimateDevice):
def set_operation_mode(self, operation_mode): def set_operation_mode(self, operation_mode):
"""Set operation mode.""" """Set operation mode."""
if operation_mode == STATE_HEAT: op_mode_to_set = HA_STATE_TO_WINK.get(operation_mode)
self.wink.set_operation_mode('heat_only') # The only way to disable aux heat is with the toggle
elif operation_mode == STATE_COOL: if self.is_aux_heat_on and op_mode_to_set == STATE_HEAT:
self.wink.set_operation_mode('cool_only') return
elif operation_mode == STATE_AUTO: self.wink.set_operation_mode(op_mode_to_set)
self.wink.set_operation_mode('auto')
elif operation_mode == STATE_OFF:
self.wink.set_operation_mode('off')
elif operation_mode == STATE_AUX:
self.wink.set_operation_mode('aux')
elif operation_mode == STATE_ECO:
self.wink.set_operation_mode('eco')
@property @property
def operation_list(self): def operation_list(self):
"""List of available operation modes.""" """List of available operation modes."""
op_list = ['off'] op_list = ['off']
modes = self.wink.hvac_modes() modes = self.wink.hvac_modes()
if 'cool_only' in modes: for mode in modes:
op_list.append(STATE_COOL) if mode == 'aux':
if 'heat_only' in modes or 'aux' in modes: continue
op_list.append(STATE_HEAT) ha_mode = WINK_STATE_TO_HA.get(mode)
if 'auto' in modes: if ha_mode is not None:
op_list.append(STATE_AUTO) op_list.append(ha_mode)
if 'eco' in modes: else:
op_list.append(STATE_ECO) error = "Invaid operation mode mapping. " + mode + \
" doesn't map. Please report this."
_LOGGER.error(error)
return op_list return op_list
def turn_away_mode_on(self): def turn_away_mode_on(self):
@ -281,12 +284,12 @@ class WinkThermostat(WinkDevice, ClimateDevice):
self.wink.set_fan_mode(fan.lower()) self.wink.set_fan_mode(fan.lower())
def turn_aux_heat_on(self): def turn_aux_heat_on(self):
"""Turn auxillary heater on.""" """Turn auxiliary heater on."""
self.set_operation_mode(STATE_AUX) self.wink.set_operation_mode('aux')
def turn_aux_heat_off(self): def turn_aux_heat_off(self):
"""Turn auxillary heater off.""" """Turn auxiliary heater off."""
self.set_operation_mode(STATE_AUTO) self.set_operation_mode(STATE_HEAT)
@property @property
def min_temp(self): def min_temp(self):
@ -344,11 +347,6 @@ class WinkThermostat(WinkDevice, ClimateDevice):
class WinkAC(WinkDevice, ClimateDevice): class WinkAC(WinkDevice, ClimateDevice):
"""Representation of a Wink air conditioner.""" """Representation of a Wink air conditioner."""
def __init__(self, wink, hass, temp_unit):
"""Initialize the Wink device."""
super().__init__(wink, hass)
self._config_temp_unit = temp_unit
@property @property
def temperature_unit(self): def temperature_unit(self):
"""Return the unit of measurement.""" """Return the unit of measurement."""
@ -382,13 +380,9 @@ class WinkAC(WinkDevice, ClimateDevice):
"""Return current operation ie. heat, cool, idle.""" """Return current operation ie. heat, cool, idle."""
if not self.wink.is_on(): if not self.wink.is_on():
current_op = STATE_OFF current_op = STATE_OFF
elif self.wink.current_mode() == 'cool_only':
current_op = STATE_COOL
elif self.wink.current_mode() == 'auto_eco':
current_op = STATE_ECO
elif self.wink.current_mode() == 'fan_only':
current_op = STATE_FAN
else: else:
current_op = WINK_STATE_TO_HA.get(self.wink.current_hvac_mode())
if current_op is None:
current_op = STATE_UNKNOWN current_op = STATE_UNKNOWN
return current_op return current_op
@ -397,12 +391,14 @@ class WinkAC(WinkDevice, ClimateDevice):
"""List of available operation modes.""" """List of available operation modes."""
op_list = ['off'] op_list = ['off']
modes = self.wink.modes() modes = self.wink.modes()
if 'cool_only' in modes: for mode in modes:
op_list.append(STATE_COOL) ha_mode = WINK_STATE_TO_HA.get(mode)
if 'auto_eco' in modes: if ha_mode is not None:
op_list.append(STATE_ECO) op_list.append(ha_mode)
if 'fan_only' in modes: else:
op_list.append(STATE_FAN) error = "Invaid operation mode mapping. " + mode + \
" doesn't map. Please report this."
_LOGGER.error(error)
return op_list return op_list
def set_temperature(self, **kwargs): def set_temperature(self, **kwargs):
@ -412,30 +408,16 @@ class WinkAC(WinkDevice, ClimateDevice):
def set_operation_mode(self, operation_mode): def set_operation_mode(self, operation_mode):
"""Set operation mode.""" """Set operation mode."""
if operation_mode == STATE_COOL: op_mode_to_set = HA_STATE_TO_WINK.get(operation_mode)
self.wink.set_operation_mode('cool_only') if op_mode_to_set == 'eco':
elif operation_mode == STATE_ECO: op_mode_to_set = 'auto_eco'
self.wink.set_operation_mode('auto_eco') self.wink.set_operation_mode(op_mode_to_set)
elif operation_mode == STATE_OFF:
self.wink.set_operation_mode('off')
elif operation_mode == STATE_FAN:
self.wink.set_operation_mode('fan_only')
@property @property
def target_temperature(self): def target_temperature(self):
"""Return the temperature we try to reach.""" """Return the temperature we try to reach."""
return self.wink.current_max_set_point() return self.wink.current_max_set_point()
@property
def target_temperature_low(self):
"""Only supports cool."""
return None
@property
def target_temperature_high(self):
"""Only supports cool."""
return None
@property @property
def current_fan_mode(self): def current_fan_mode(self):
"""Return the current fan mode.""" """Return the current fan mode."""
@ -453,12 +435,97 @@ class WinkAC(WinkDevice, ClimateDevice):
"""Return a list of available fan modes.""" """Return a list of available fan modes."""
return [SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH] return [SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
def set_fan_mode(self, mode): def set_fan_mode(self, fan):
"""Set fan speed.""" """Set fan speed."""
if mode == SPEED_LOW: if fan == SPEED_LOW:
speed = 0.4 speed = 0.4
elif mode == SPEED_MEDIUM: elif fan == SPEED_MEDIUM:
speed = 0.8 speed = 0.8
elif mode == SPEED_HIGH: elif fan == SPEED_HIGH:
speed = 1.0 speed = 1.0
self.wink.set_ac_fan_speed(speed) self.wink.set_ac_fan_speed(speed)
class WinkWaterHeater(WinkDevice, ClimateDevice):
"""Representation of a Wink water heater."""
@property
def temperature_unit(self):
"""Return the unit of measurement."""
# The Wink API always returns temp in Celsius
return TEMP_CELSIUS
@property
def device_state_attributes(self):
"""Return the optional state attributes."""
data = {}
data["vacation_mode"] = self.wink.vacation_mode_enabled()
data["rheem_type"] = self.wink.rheem_type()
return data
@property
def current_operation(self):
"""
Return current operation one of the following.
["eco", "performance", "heat_pump",
"high_demand", "electric_only", "gas]
"""
if not self.wink.is_on():
current_op = STATE_OFF
else:
current_op = WINK_STATE_TO_HA.get(self.wink.current_mode())
if current_op is None:
current_op = STATE_UNKNOWN
return current_op
@property
def operation_list(self):
"""List of available operation modes."""
op_list = ['off']
modes = self.wink.modes()
for mode in modes:
if mode == 'aux':
continue
ha_mode = WINK_STATE_TO_HA.get(mode)
if ha_mode is not None:
op_list.append(ha_mode)
else:
error = "Invaid operation mode mapping. " + mode + \
" doesn't map. Please report this."
_LOGGER.error(error)
return op_list
def set_temperature(self, **kwargs):
"""Set new target temperature."""
target_temp = kwargs.get(ATTR_TEMPERATURE)
self.wink.set_temperature(target_temp)
def set_operation_mode(self, operation_mode):
"""Set operation mode."""
op_mode_to_set = HA_STATE_TO_WINK.get(operation_mode)
self.wink.set_operation_mode(op_mode_to_set)
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
return self.wink.current_set_point()
def turn_away_mode_on(self):
"""Turn away on."""
self.wink.set_vacation_mode(True)
def turn_away_mode_off(self):
"""Turn away off."""
self.wink.set_vacation_mode(False)
@property
def min_temp(self):
"""Return the minimum temperature."""
return self.wink.min_set_point()
@property
def max_temp(self):
"""Return the maximum temperature."""
return self.wink.max_set_point()

View File

@ -21,8 +21,8 @@ from homeassistant.const import (
CONF_NAME, CONF_VALUE_TEMPLATE, CONF_OPTIMISTIC, STATE_OPEN, CONF_NAME, CONF_VALUE_TEMPLATE, CONF_OPTIMISTIC, STATE_OPEN,
STATE_CLOSED, STATE_UNKNOWN) STATE_CLOSED, STATE_UNKNOWN)
from homeassistant.components.mqtt import ( from homeassistant.components.mqtt import (
CONF_STATE_TOPIC, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC, CONF_COMMAND_TOPIC, CONF_AVAILABILITY_TOPIC,
valid_publish_topic, valid_subscribe_topic) CONF_QOS, CONF_RETAIN, valid_publish_topic, valid_subscribe_topic)
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -37,6 +37,8 @@ CONF_SET_POSITION_TEMPLATE = 'set_position_template'
CONF_PAYLOAD_OPEN = 'payload_open' CONF_PAYLOAD_OPEN = 'payload_open'
CONF_PAYLOAD_CLOSE = 'payload_close' CONF_PAYLOAD_CLOSE = 'payload_close'
CONF_PAYLOAD_STOP = 'payload_stop' CONF_PAYLOAD_STOP = 'payload_stop'
CONF_PAYLOAD_AVAILABLE = 'payload_available'
CONF_PAYLOAD_NOT_AVAILABLE = 'payload_not_available'
CONF_STATE_OPEN = 'state_open' CONF_STATE_OPEN = 'state_open'
CONF_STATE_CLOSED = 'state_closed' CONF_STATE_CLOSED = 'state_closed'
CONF_TILT_CLOSED_POSITION = 'tilt_closed_value' CONF_TILT_CLOSED_POSITION = 'tilt_closed_value'
@ -50,6 +52,8 @@ DEFAULT_NAME = 'MQTT Cover'
DEFAULT_PAYLOAD_OPEN = 'OPEN' DEFAULT_PAYLOAD_OPEN = 'OPEN'
DEFAULT_PAYLOAD_CLOSE = 'CLOSE' DEFAULT_PAYLOAD_CLOSE = 'CLOSE'
DEFAULT_PAYLOAD_STOP = 'STOP' DEFAULT_PAYLOAD_STOP = 'STOP'
DEFAULT_PAYLOAD_AVAILABLE = 'online'
DEFAULT_PAYLOAD_NOT_AVAILABLE = 'offline'
DEFAULT_OPTIMISTIC = False DEFAULT_OPTIMISTIC = False
DEFAULT_RETAIN = False DEFAULT_RETAIN = False
DEFAULT_TILT_CLOSED_POSITION = 0 DEFAULT_TILT_CLOSED_POSITION = 0
@ -69,11 +73,16 @@ PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({
vol.Optional(CONF_SET_POSITION_TEMPLATE, default=None): cv.template, vol.Optional(CONF_SET_POSITION_TEMPLATE, default=None): cv.template,
vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
vol.Optional(CONF_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_STATE_TOPIC): valid_subscribe_topic,
vol.Optional(CONF_AVAILABILITY_TOPIC, default=None): valid_subscribe_topic,
vol.Optional(CONF_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PAYLOAD_OPEN, default=DEFAULT_PAYLOAD_OPEN): cv.string, vol.Optional(CONF_PAYLOAD_OPEN, default=DEFAULT_PAYLOAD_OPEN): cv.string,
vol.Optional(CONF_PAYLOAD_CLOSE, default=DEFAULT_PAYLOAD_CLOSE): cv.string, vol.Optional(CONF_PAYLOAD_CLOSE, default=DEFAULT_PAYLOAD_CLOSE): cv.string,
vol.Optional(CONF_PAYLOAD_STOP, default=DEFAULT_PAYLOAD_STOP): cv.string, vol.Optional(CONF_PAYLOAD_STOP, default=DEFAULT_PAYLOAD_STOP): cv.string,
vol.Optional(CONF_PAYLOAD_AVAILABLE,
default=DEFAULT_PAYLOAD_AVAILABLE): cv.string,
vol.Optional(CONF_PAYLOAD_NOT_AVAILABLE,
default=DEFAULT_PAYLOAD_NOT_AVAILABLE): cv.string,
vol.Optional(CONF_STATE_OPEN, default=STATE_OPEN): cv.string, vol.Optional(CONF_STATE_OPEN, default=STATE_OPEN): cv.string,
vol.Optional(CONF_STATE_CLOSED, default=STATE_CLOSED): cv.string, vol.Optional(CONF_STATE_CLOSED, default=STATE_CLOSED): cv.string,
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
@ -106,6 +115,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
config.get(CONF_NAME), config.get(CONF_NAME),
config.get(CONF_STATE_TOPIC), config.get(CONF_STATE_TOPIC),
config.get(CONF_COMMAND_TOPIC), config.get(CONF_COMMAND_TOPIC),
config.get(CONF_AVAILABILITY_TOPIC),
config.get(CONF_TILT_COMMAND_TOPIC), config.get(CONF_TILT_COMMAND_TOPIC),
config.get(CONF_TILT_STATUS_TOPIC), config.get(CONF_TILT_STATUS_TOPIC),
config.get(CONF_QOS), config.get(CONF_QOS),
@ -115,6 +125,8 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
config.get(CONF_PAYLOAD_OPEN), config.get(CONF_PAYLOAD_OPEN),
config.get(CONF_PAYLOAD_CLOSE), config.get(CONF_PAYLOAD_CLOSE),
config.get(CONF_PAYLOAD_STOP), config.get(CONF_PAYLOAD_STOP),
config.get(CONF_PAYLOAD_AVAILABLE),
config.get(CONF_PAYLOAD_NOT_AVAILABLE),
config.get(CONF_OPTIMISTIC), config.get(CONF_OPTIMISTIC),
value_template, value_template,
config.get(CONF_TILT_OPEN_POSITION), config.get(CONF_TILT_OPEN_POSITION),
@ -131,9 +143,10 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
class MqttCover(CoverDevice): class MqttCover(CoverDevice):
"""Representation of a cover that can be controlled using MQTT.""" """Representation of a cover that can be controlled using MQTT."""
def __init__(self, name, state_topic, command_topic, tilt_command_topic, def __init__(self, name, state_topic, command_topic, availability_topic,
tilt_status_topic, qos, retain, state_open, state_closed, tilt_command_topic, tilt_status_topic, qos, retain,
payload_open, payload_close, payload_stop, state_open, state_closed, payload_open, payload_close,
payload_stop, payload_available, payload_not_available,
optimistic, value_template, tilt_open_position, optimistic, value_template, tilt_open_position,
tilt_closed_position, tilt_min, tilt_max, tilt_optimistic, tilt_closed_position, tilt_min, tilt_max, tilt_optimistic,
tilt_invert, position_topic, set_position_template): tilt_invert, position_topic, set_position_template):
@ -143,12 +156,16 @@ class MqttCover(CoverDevice):
self._name = name self._name = name
self._state_topic = state_topic self._state_topic = state_topic
self._command_topic = command_topic self._command_topic = command_topic
self._availability_topic = availability_topic
self._available = True if availability_topic is None else False
self._tilt_command_topic = tilt_command_topic self._tilt_command_topic = tilt_command_topic
self._tilt_status_topic = tilt_status_topic self._tilt_status_topic = tilt_status_topic
self._qos = qos self._qos = qos
self._payload_open = payload_open self._payload_open = payload_open
self._payload_close = payload_close self._payload_close = payload_close
self._payload_stop = payload_stop self._payload_stop = payload_stop
self._payload_available = payload_available
self._payload_not_available = payload_not_available
self._state_open = state_open self._state_open = state_open
self._state_closed = state_closed self._state_closed = state_closed
self._retain = retain self._retain = retain
@ -181,8 +198,8 @@ class MqttCover(CoverDevice):
self.async_schedule_update_ha_state() self.async_schedule_update_ha_state()
@callback @callback
def message_received(topic, payload, qos): def state_message_received(topic, payload, qos):
"""Handle new MQTT message.""" """Handle new MQTT state messages."""
if self._template is not None: if self._template is not None:
payload = self._template.async_render_with_possible_json_value( payload = self._template.async_render_with_possible_json_value(
payload) payload)
@ -205,12 +222,28 @@ class MqttCover(CoverDevice):
self.async_schedule_update_ha_state() self.async_schedule_update_ha_state()
@callback
def availability_message_received(topic, payload, qos):
"""Handle new MQTT availability messages."""
if payload == self._payload_available:
self._available = True
elif payload == self._payload_not_available:
self._available = False
self.async_schedule_update_ha_state()
if self._state_topic is None: if self._state_topic is None:
# Force into optimistic mode. # Force into optimistic mode.
self._optimistic = True self._optimistic = True
else: else:
yield from mqtt.async_subscribe( yield from mqtt.async_subscribe(
self.hass, self._state_topic, message_received, self._qos) self.hass, self._state_topic,
state_message_received, self._qos)
if self._availability_topic is not None:
yield from mqtt.async_subscribe(
self.hass, self._availability_topic,
availability_message_received, self._qos)
if self._tilt_status_topic is None: if self._tilt_status_topic is None:
self._tilt_optimistic = True self._tilt_optimistic = True
@ -230,6 +263,11 @@ class MqttCover(CoverDevice):
"""Return the name of the cover.""" """Return the name of the cover."""
return self._name return self._name
@property
def available(self) -> bool:
"""Return if cover is available."""
return self._available
@property @property
def is_closed(self): def is_closed(self):
"""Return if the cover is closed.""" """Return if the cover is closed."""

View File

@ -0,0 +1,116 @@
"""
Support for Rflink Cover devices.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/cover.rflink/
"""
import asyncio
import logging
import voluptuous as vol
from homeassistant.components.rflink import (
DATA_ENTITY_GROUP_LOOKUP, DATA_ENTITY_LOOKUP,
DEVICE_DEFAULTS_SCHEMA, EVENT_KEY_COMMAND, RflinkCommand)
from homeassistant.components.cover import (
CoverDevice, PLATFORM_SCHEMA)
import homeassistant.helpers.config_validation as cv
from homeassistant.const import CONF_NAME
DEPENDENCIES = ['rflink']
_LOGGER = logging.getLogger(__name__)
CONF_ALIASES = 'aliases'
CONF_GROUP_ALIASES = 'group_aliases'
CONF_GROUP = 'group'
CONF_NOGROUP_ALIASES = 'nogroup_aliases'
CONF_DEVICE_DEFAULTS = 'device_defaults'
CONF_DEVICES = 'devices'
CONF_AUTOMATIC_ADD = 'automatic_add'
CONF_FIRE_EVENT = 'fire_event'
CONF_IGNORE_DEVICES = 'ignore_devices'
CONF_RECONNECT_INTERVAL = 'reconnect_interval'
CONF_SIGNAL_REPETITIONS = 'signal_repetitions'
CONF_WAIT_FOR_ACK = 'wait_for_ack'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_DEVICE_DEFAULTS, default=DEVICE_DEFAULTS_SCHEMA({})):
DEVICE_DEFAULTS_SCHEMA,
vol.Optional(CONF_DEVICES, default={}): vol.Schema({
cv.string: {
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_ALIASES, default=[]):
vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_GROUP_ALIASES, default=[]):
vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_NOGROUP_ALIASES, default=[]):
vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_FIRE_EVENT, default=False): cv.boolean,
vol.Optional(CONF_SIGNAL_REPETITIONS): vol.Coerce(int),
vol.Optional(CONF_GROUP, default=True): cv.boolean,
},
}),
})
def devices_from_config(domain_config, hass=None):
"""Parse configuration and add Rflink cover devices."""
devices = []
for device_id, config in domain_config[CONF_DEVICES].items():
device_config = dict(domain_config[CONF_DEVICE_DEFAULTS], **config)
device = RflinkCover(device_id, hass, **device_config)
devices.append(device)
# Register entity (and aliases) to listen to incoming rflink events
# Device id and normal aliases respond to normal and group command
hass.data[DATA_ENTITY_LOOKUP][
EVENT_KEY_COMMAND][device_id].append(device)
if config[CONF_GROUP]:
hass.data[DATA_ENTITY_GROUP_LOOKUP][
EVENT_KEY_COMMAND][device_id].append(device)
for _id in config[CONF_ALIASES]:
hass.data[DATA_ENTITY_LOOKUP][
EVENT_KEY_COMMAND][_id].append(device)
hass.data[DATA_ENTITY_GROUP_LOOKUP][
EVENT_KEY_COMMAND][_id].append(device)
return devices
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the Rflink cover platform."""
async_add_devices(devices_from_config(config, hass))
class RflinkCover(RflinkCommand, CoverDevice):
"""Rflink entity which can switch on/stop/off (eg: cover)."""
def _handle_event(self, event):
"""Adjust state if Rflink picks up a remote command for this device."""
self.cancel_queued_send_commands()
command = event['command']
if command in ['on', 'allon']:
self._state = True
elif command in ['off', 'alloff']:
self._state = False
@property
def should_poll(self):
"""No polling available in RFlink cover."""
return False
def async_close_cover(self, **kwargs):
"""Turn the device close."""
return self._async_handle_command("close_cover")
def async_open_cover(self, **kwargs):
"""Turn the device open."""
return self._async_handle_command("open_cover")
def async_stop_cover(self, **kwargs):
"""Turn the device stop."""
return self._async_handle_command("stop_cover")

View File

@ -87,8 +87,8 @@ def async_setup(hass, config):
# Set up input boolean # Set up input boolean
tasks.append(bootstrap.async_setup_component( tasks.append(bootstrap.async_setup_component(
hass, 'input_slider', hass, 'input_number',
{'input_slider': { {'input_number': {
'noise_allowance': {'icon': 'mdi:bell-ring', 'noise_allowance': {'icon': 'mdi:bell-ring',
'min': 0, 'min': 0,
'max': 10, 'max': 10,
@ -163,7 +163,7 @@ def async_setup(hass, config):
'scene.romantic_lights'])) 'scene.romantic_lights']))
tasks2.append(group.Group.async_create_group(hass, 'Bedroom', [ tasks2.append(group.Group.async_create_group(hass, 'Bedroom', [
lights[0], switches[1], media_players[0], lights[0], switches[1], media_players[0],
'input_slider.noise_allowance'])) 'input_number.noise_allowance']))
tasks2.append(group.Group.async_create_group(hass, 'Kitchen', [ tasks2.append(group.Group.async_create_group(hass, 'Kitchen', [
lights[2], 'cover.kitchen_window', 'lock.kitchen_door'])) lights[2], 'cover.kitchen_window', 'lock.kitchen_door']))
tasks2.append(group.Group.async_create_group(hass, 'Doors', [ tasks2.append(group.Group.async_create_group(hass, 'Doors', [

View File

@ -248,7 +248,7 @@ class Icloud(DeviceScanner):
self._trusted_device, self._verification_code): self._trusted_device, self._verification_code):
raise PyiCloudException('Unknown failure') raise PyiCloudException('Unknown failure')
except PyiCloudException as error: except PyiCloudException as error:
# Reset to the inital 2FA state to allow the user to retry # Reset to the initial 2FA state to allow the user to retry
_LOGGER.error("Failed to verify verification code: %s", error) _LOGGER.error("Failed to verify verification code: %s", error)
self._trusted_device = None self._trusted_device = None
self._verification_code = None self._verification_code = None

View File

@ -1,5 +1,5 @@
""" """
Support the OwnTracks platform. Device tracker platform that adds support for OwnTracks over MQTT.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.owntracks/ https://home-assistant.io/components/device_tracker.owntracks/
@ -16,7 +16,7 @@ from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
import homeassistant.components.mqtt as mqtt import homeassistant.components.mqtt as mqtt
from homeassistant.const import STATE_HOME from homeassistant.const import STATE_HOME
from homeassistant.util import convert, slugify from homeassistant.util import slugify, decorator
from homeassistant.components import zone as zone_comp from homeassistant.components import zone as zone_comp
from homeassistant.components.device_tracker import PLATFORM_SCHEMA from homeassistant.components.device_tracker import PLATFORM_SCHEMA
@ -25,6 +25,8 @@ REQUIREMENTS = ['libnacl==1.5.2']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
HANDLERS = decorator.Registry()
BEACON_DEV_ID = 'beacon' BEACON_DEV_ID = 'beacon'
CONF_MAX_GPS_ACCURACY = 'max_gps_accuracy' CONF_MAX_GPS_ACCURACY = 'max_gps_accuracy'
@ -32,17 +34,7 @@ CONF_SECRET = 'secret'
CONF_WAYPOINT_IMPORT = 'waypoints' CONF_WAYPOINT_IMPORT = 'waypoints'
CONF_WAYPOINT_WHITELIST = 'waypoint_whitelist' CONF_WAYPOINT_WHITELIST = 'waypoint_whitelist'
EVENT_TOPIC = 'owntracks/+/+/event' OWNTRACKS_TOPIC = 'owntracks/#'
LOCATION_TOPIC = 'owntracks/+/+'
VALIDATE_LOCATION = 'location'
VALIDATE_TRANSITION = 'transition'
VALIDATE_WAYPOINTS = 'waypoints'
WAYPOINT_LAT_KEY = 'lat'
WAYPOINT_LON_KEY = 'lon'
WAYPOINT_TOPIC = 'owntracks/{}/{}/waypoints'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_MAX_GPS_ACCURACY): vol.Coerce(float), vol.Optional(CONF_MAX_GPS_ACCURACY): vol.Coerce(float),
@ -72,15 +64,79 @@ def get_cipher():
@asyncio.coroutine @asyncio.coroutine
def async_setup_scanner(hass, config, async_see, discovery_info=None): def async_setup_scanner(hass, config, async_see, discovery_info=None):
"""Set up an OwnTracks tracker.""" """Set up an OwnTracks tracker."""
max_gps_accuracy = config.get(CONF_MAX_GPS_ACCURACY) context = context_from_config(async_see, config)
waypoint_import = config.get(CONF_WAYPOINT_IMPORT)
waypoint_whitelist = config.get(CONF_WAYPOINT_WHITELIST)
secret = config.get(CONF_SECRET)
mobile_beacons_active = defaultdict(list) @asyncio.coroutine
regions_entered = defaultdict(list) def async_handle_mqtt_message(topic, payload, qos):
"""Handle incoming OwnTracks message."""
try:
message = json.loads(payload)
except ValueError:
# If invalid JSON
_LOGGER.error("Unable to parse payload as JSON: %s", payload)
def decrypt_payload(topic, ciphertext): message['topic'] = topic
yield from async_handle_message(hass, context, message)
yield from mqtt.async_subscribe(
hass, OWNTRACKS_TOPIC, async_handle_mqtt_message, 1)
return True
def _parse_topic(topic):
"""Parse an MQTT topic owntracks/user/dev, return (user, dev) tuple.
Async friendly.
"""
_, user, device, *_ = topic.split('/', 3)
return user, device
def _parse_see_args(message):
"""Parse the OwnTracks location parameters, into the format see expects.
Async friendly.
"""
user, device = _parse_topic(message['topic'])
dev_id = slugify('{}_{}'.format(user, device))
kwargs = {
'dev_id': dev_id,
'host_name': user,
'gps': (message['lat'], message['lon']),
'attributes': {}
}
if 'acc' in message:
kwargs['gps_accuracy'] = message['acc']
if 'batt' in message:
kwargs['battery'] = message['batt']
if 'vel' in message:
kwargs['attributes']['velocity'] = message['vel']
if 'tid' in message:
kwargs['attributes']['tid'] = message['tid']
if 'addr' in message:
kwargs['attributes']['address'] = message['addr']
return dev_id, kwargs
def _set_gps_from_zone(kwargs, location, zone):
"""Set the see parameters from the zone parameters.
Async friendly.
"""
if zone is not None:
kwargs['gps'] = (
zone.attributes['latitude'],
zone.attributes['longitude'])
kwargs['gps_accuracy'] = zone.attributes['radius']
kwargs['location_name'] = location
return kwargs
def _decrypt_payload(secret, topic, ciphertext):
"""Decrypt encrypted payload.""" """Decrypt encrypted payload."""
try: try:
keylen, decrypt = get_cipher() keylen, decrypt = get_cipher()
@ -116,111 +172,125 @@ def async_setup_scanner(hass, config, async_see, discovery_info=None):
"key for topic %s", topic) "key for topic %s", topic)
return None return None
def validate_payload(topic, payload, data_type):
"""Validate the OwnTracks payload.""" def context_from_config(async_see, config):
"""Create an async context from Home Assistant config."""
max_gps_accuracy = config.get(CONF_MAX_GPS_ACCURACY)
waypoint_import = config.get(CONF_WAYPOINT_IMPORT)
waypoint_whitelist = config.get(CONF_WAYPOINT_WHITELIST)
secret = config.get(CONF_SECRET)
return OwnTracksContext(async_see, secret, max_gps_accuracy,
waypoint_import, waypoint_whitelist)
class OwnTracksContext:
"""Hold the current OwnTracks context."""
def __init__(self, async_see, secret, max_gps_accuracy, import_waypoints,
waypoint_whitelist):
"""Initialize an OwnTracks context."""
self.async_see = async_see
self.secret = secret
self.max_gps_accuracy = max_gps_accuracy
self.mobile_beacons_active = defaultdict(list)
self.regions_entered = defaultdict(list)
self.import_waypoints = import_waypoints
self.waypoint_whitelist = waypoint_whitelist
@callback
def async_valid_accuracy(self, message):
"""Check if we should ignore this message."""
acc = message.get('acc')
if acc is None:
return False
try: try:
data = json.loads(payload) acc = float(acc)
except ValueError: except ValueError:
# If invalid JSON return False
_LOGGER.error("Unable to parse payload as JSON: %s", payload)
return None
if isinstance(data, dict) and \ if acc == 0:
data.get('_type') == 'encrypted' and \
'data' in data:
plaintext_payload = decrypt_payload(topic, data['data'])
if plaintext_payload is None:
return None
return validate_payload(topic, plaintext_payload, data_type)
if not isinstance(data, dict) or data.get('_type') != data_type:
_LOGGER.debug("Skipping %s update for following data "
"because of missing or malformatted data: %s",
data_type, data)
return None
if data_type == VALIDATE_TRANSITION or data_type == VALIDATE_WAYPOINTS:
return data
if max_gps_accuracy is not None and \
convert(data.get('acc'), float, 0.0) > max_gps_accuracy:
_LOGGER.info("Ignoring %s update because expected GPS "
"accuracy %s is not met: %s",
data_type, max_gps_accuracy, payload)
return None
if convert(data.get('acc'), float, 1.0) == 0.0:
_LOGGER.warning( _LOGGER.warning(
"Ignoring %s update because GPS accuracy is zero: %s", "Ignoring %s update because GPS accuracy is zero: %s",
data_type, payload) message['_type'], message)
return None return False
return data if self.max_gps_accuracy is not None and \
acc > self.max_gps_accuracy:
_LOGGER.info("Ignoring %s update because expected GPS "
"accuracy %s is not met: %s",
message['_type'], self.max_gps_accuracy,
message)
return False
@callback return True
def async_owntracks_location_update(topic, payload, qos):
"""MQTT message received.""" @asyncio.coroutine
# Docs on available data: def async_see_beacons(self, dev_id, kwargs_param):
# http://owntracks.org/booklet/tech/json/#_typelocation """Set active beacons to the current location."""
data = validate_payload(topic, payload, VALIDATE_LOCATION) kwargs = kwargs_param.copy()
if not data: # the battery state applies to the tracking device, not the beacon
kwargs.pop('battery', None)
for beacon in self.mobile_beacons_active[dev_id]:
kwargs['dev_id'] = "{}_{}".format(BEACON_DEV_ID, beacon)
kwargs['host_name'] = beacon
yield from self.async_see(**kwargs)
@HANDLERS.register('location')
@asyncio.coroutine
def async_handle_location_message(hass, context, message):
"""Handle a location message."""
if not context.async_valid_accuracy(message):
return return
dev_id, kwargs = _parse_see_args(topic, data) dev_id, kwargs = _parse_see_args(message)
if regions_entered[dev_id]: if context.regions_entered[dev_id]:
_LOGGER.debug( _LOGGER.debug(
"Location update ignored, inside region %s", "Location update ignored, inside region %s",
regions_entered[-1]) context.regions_entered[-1])
return return
hass.async_add_job(async_see(**kwargs)) yield from context.async_see(**kwargs)
async_see_beacons(dev_id, kwargs) yield from context.async_see_beacons(dev_id, kwargs)
@callback
def async_owntracks_event_update(topic, payload, qos):
"""Handle MQTT event (geofences)."""
# Docs on available data:
# http://owntracks.org/booklet/tech/json/#_typetransition
data = validate_payload(topic, payload, VALIDATE_TRANSITION)
if not data:
return
if data.get('desc') is None: @asyncio.coroutine
_LOGGER.error( def _async_transition_message_enter(hass, context, message, location):
"Location missing from `Entering/Leaving` message - "
"please turn `Share` on in OwnTracks app")
return
# OwnTracks uses - at the start of a beacon zone
# to switch on 'hold mode' - ignore this
location = data['desc'].lstrip("-")
if location.lower() == 'home':
location = STATE_HOME
dev_id, kwargs = _parse_see_args(topic, data)
def enter_event():
"""Execute enter event.""" """Execute enter event."""
zone = hass.states.get("zone.{}".format(slugify(location))) zone = hass.states.get("zone.{}".format(slugify(location)))
if zone is None and data.get('t') == 'b': dev_id, kwargs = _parse_see_args(message)
if zone is None and message.get('t') == 'b':
# Not a HA zone, and a beacon so assume mobile # Not a HA zone, and a beacon so assume mobile
beacons = mobile_beacons_active[dev_id] beacons = context.mobile_beacons_active[dev_id]
if location not in beacons: if location not in beacons:
beacons.append(location) beacons.append(location)
_LOGGER.info("Added beacon %s", location) _LOGGER.info("Added beacon %s", location)
else: else:
# Normal region # Normal region
regions = regions_entered[dev_id] regions = context.regions_entered[dev_id]
if location not in regions: if location not in regions:
regions.append(location) regions.append(location)
_LOGGER.info("Enter region %s", location) _LOGGER.info("Enter region %s", location)
_set_gps_from_zone(kwargs, location, zone) _set_gps_from_zone(kwargs, location, zone)
hass.async_add_job(async_see(**kwargs)) yield from context.async_see(**kwargs)
async_see_beacons(dev_id, kwargs) yield from context.async_see_beacons(dev_id, kwargs)
def leave_event():
@asyncio.coroutine
def _async_transition_message_leave(hass, context, message, location):
"""Execute leave event.""" """Execute leave event."""
regions = regions_entered[dev_id] dev_id, kwargs = _parse_see_args(message)
regions = context.regions_entered[dev_id]
if location in regions: if location in regions:
regions.remove(location) regions.remove(location)
new_region = regions[-1] if regions else None new_region = regions[-1] if regions else None
if new_region: if new_region:
@ -229,61 +299,75 @@ def async_setup_scanner(hass, config, async_see, discovery_info=None):
"zone.{}".format(slugify(new_region))) "zone.{}".format(slugify(new_region)))
_set_gps_from_zone(kwargs, new_region, zone) _set_gps_from_zone(kwargs, new_region, zone)
_LOGGER.info("Exit to %s", new_region) _LOGGER.info("Exit to %s", new_region)
hass.async_add_job(async_see(**kwargs)) yield from context.async_see(**kwargs)
async_see_beacons(dev_id, kwargs) yield from context.async_see_beacons(dev_id, kwargs)
return
else: else:
_LOGGER.info("Exit to GPS") _LOGGER.info("Exit to GPS")
# Check for GPS accuracy
valid_gps = True
if 'acc' in data:
if data['acc'] == 0.0:
valid_gps = False
_LOGGER.warning(
"Ignoring GPS in region exit because accuracy"
"is zero: %s", payload)
if (max_gps_accuracy is not None and
data['acc'] > max_gps_accuracy):
valid_gps = False
_LOGGER.info(
"Ignoring GPS in region exit because expected "
"GPS accuracy %s is not met: %s",
max_gps_accuracy, payload)
if valid_gps:
hass.async_add_job(async_see(**kwargs))
async_see_beacons(dev_id, kwargs)
beacons = mobile_beacons_active[dev_id] # Check for GPS accuracy
if context.async_valid_accuracy(message):
yield from context.async_see(**kwargs)
yield from context.async_see_beacons(dev_id, kwargs)
beacons = context.mobile_beacons_active[dev_id]
if location in beacons: if location in beacons:
beacons.remove(location) beacons.remove(location)
_LOGGER.info("Remove beacon %s", location) _LOGGER.info("Remove beacon %s", location)
if data['event'] == 'enter':
enter_event() @HANDLERS.register('transition')
elif data['event'] == 'leave': @asyncio.coroutine
leave_event() def async_handle_transition_message(hass, context, message):
"""Handle a transition message."""
if message.get('desc') is None:
_LOGGER.error(
"Location missing from `Entering/Leaving` message - "
"please turn `Share` on in OwnTracks app")
return
# OwnTracks uses - at the start of a beacon zone
# to switch on 'hold mode' - ignore this
location = message['desc'].lstrip("-")
if location.lower() == 'home':
location = STATE_HOME
if message['event'] == 'enter':
yield from _async_transition_message_enter(
hass, context, message, location)
elif message['event'] == 'leave':
yield from _async_transition_message_leave(
hass, context, message, location)
else: else:
_LOGGER.error( _LOGGER.error(
"Misformatted mqtt msgs, _type=transition, event=%s", "Misformatted mqtt msgs, _type=transition, event=%s",
data['event']) message['event'])
@HANDLERS.register('waypoints')
@asyncio.coroutine
def async_handle_waypoints_message(hass, context, message):
"""Handle a waypoints message."""
if not context.import_waypoints:
return return
@callback if context.waypoint_whitelist is not None:
def async_owntracks_waypoint_update(topic, payload, qos): user = _parse_topic(message['topic'])[0]
"""List of waypoints published by a user."""
# Docs on available data: if user not in context.waypoint_whitelist:
# http://owntracks.org/booklet/tech/json/#_typewaypoints
data = validate_payload(topic, payload, VALIDATE_WAYPOINTS)
if not data:
return return
wayps = data['waypoints'] wayps = message['waypoints']
_LOGGER.info("Got %d waypoints from %s", len(wayps), topic)
_LOGGER.info("Got %d waypoints from %s", len(wayps), message['topic'])
name_base = ' '.join(_parse_topic(message['topic']))
for wayp in wayps: for wayp in wayps:
name = wayp['desc'] name = wayp['desc']
pretty_name = parse_topic(topic, True)[1] + ' - ' + name pretty_name = '{} - {}'.format(name_base, name)
lat = wayp[WAYPOINT_LAT_KEY] lat = wayp['lat']
lon = wayp[WAYPOINT_LON_KEY] lon = wayp['lon']
rad = wayp['rad'] rad = wayp['rad']
# check zone exists # check zone exists
@ -296,89 +380,34 @@ def async_setup_scanner(hass, config, async_see, discovery_info=None):
zone = zone_comp.Zone(hass, pretty_name, lat, lon, rad, zone = zone_comp.Zone(hass, pretty_name, lat, lon, rad,
zone_comp.ICON_IMPORT, False) zone_comp.ICON_IMPORT, False)
zone.entity_id = entity_id zone.entity_id = entity_id
hass.async_add_job(zone.async_update_ha_state()) yield from zone.async_update_ha_state()
@callback
def async_see_beacons(dev_id, kwargs_param):
"""Set active beacons to the current location."""
kwargs = kwargs_param.copy()
# the battery state applies to the tracking device, not the beacon
kwargs.pop('battery', None)
for beacon in mobile_beacons_active[dev_id]:
kwargs['dev_id'] = "{}_{}".format(BEACON_DEV_ID, beacon)
kwargs['host_name'] = beacon
hass.async_add_job(async_see(**kwargs))
yield from mqtt.async_subscribe(
hass, LOCATION_TOPIC, async_owntracks_location_update, 1)
yield from mqtt.async_subscribe(
hass, EVENT_TOPIC, async_owntracks_event_update, 1)
if waypoint_import:
if waypoint_whitelist is None:
yield from mqtt.async_subscribe(
hass, WAYPOINT_TOPIC.format('+', '+'),
async_owntracks_waypoint_update, 1)
else:
for whitelist_user in waypoint_whitelist:
yield from mqtt.async_subscribe(
hass, WAYPOINT_TOPIC.format(whitelist_user, '+'),
async_owntracks_waypoint_update, 1)
return True
def parse_topic(topic, pretty=False): @HANDLERS.register('encrypted')
"""Parse an MQTT topic owntracks/user/dev, return (user, dev) tuple. @asyncio.coroutine
def async_handle_encrypted_message(hass, context, message):
"""Handle an encrypted message."""
plaintext_payload = _decrypt_payload(context.secret, message['topic'],
message['data'])
Async friendly. if plaintext_payload is None:
""" return
parts = topic.split('/')
dev_id_format = '' decrypted = json.loads(plaintext_payload)
if pretty: decrypted['topic'] = message['topic']
dev_id_format = '{} {}'
else: yield from async_handle_message(hass, context, decrypted)
dev_id_format = '{}_{}'
dev_id = slugify(dev_id_format.format(parts[1], parts[2]))
host_name = parts[1]
return (host_name, dev_id)
def _parse_see_args(topic, data): @asyncio.coroutine
"""Parse the OwnTracks location parameters, into the format see expects. def async_handle_message(hass, context, message):
"""Handle an OwnTracks message."""
msgtype = message.get('_type')
Async friendly. handler = HANDLERS.get(msgtype)
"""
(host_name, dev_id) = parse_topic(topic, False)
kwargs = {
'dev_id': dev_id,
'host_name': host_name,
'gps': (data[WAYPOINT_LAT_KEY], data[WAYPOINT_LON_KEY]),
'attributes': {}
}
if 'acc' in data:
kwargs['gps_accuracy'] = data['acc']
if 'batt' in data:
kwargs['battery'] = data['batt']
if 'vel' in data:
kwargs['attributes']['velocity'] = data['vel']
if 'tid' in data:
kwargs['attributes']['tid'] = data['tid']
if 'addr' in data:
kwargs['attributes']['address'] = data['addr']
return dev_id, kwargs if handler is None:
error = 'Received unsupported message type: {}.'.format(msgtype)
_LOGGER.warning(error)
yield from handler(hass, context, message)
def _set_gps_from_zone(kwargs, location, zone):
"""Set the see parameters from the zone parameters.
Async friendly.
"""
if zone is not None:
kwargs['gps'] = (
zone.attributes['latitude'],
zone.attributes['longitude'])
kwargs['gps_accuracy'] = zone.attributes['radius']
kwargs['location_name'] = location
return kwargs

View File

@ -0,0 +1,54 @@
"""
Device tracker platform that adds support for OwnTracks over HTTP.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.owntracks_http/
"""
import asyncio
from aiohttp.web_exceptions import HTTPInternalServerError
from homeassistant.components.http import HomeAssistantView
# pylint: disable=unused-import
from .owntracks import ( # NOQA
REQUIREMENTS, PLATFORM_SCHEMA, context_from_config, async_handle_message)
DEPENDENCIES = ['http']
@asyncio.coroutine
def async_setup_scanner(hass, config, async_see, discovery_info=None):
"""Set up an OwnTracks tracker."""
context = context_from_config(async_see, config)
hass.http.register_view(OwnTracksView(context))
return True
class OwnTracksView(HomeAssistantView):
"""View to handle OwnTracks HTTP requests."""
url = '/api/owntracks/{user}/{device}'
name = 'api:owntracks'
def __init__(self, context):
"""Initialize OwnTracks URL endpoints."""
self.context = context
@asyncio.coroutine
def post(self, request, user, device):
"""Handle an OwnTracks message."""
hass = request.app['hass']
message = yield from request.json()
message['topic'] = 'owntracks/{}/{}'.format(user, device)
try:
yield from async_handle_message(hass, self.context, message)
return self.json([])
except ValueError:
raise HTTPInternalServerError

View File

@ -75,7 +75,7 @@ class SnmpScanner(DeviceScanner):
return [client['mac'] for client in self.last_results return [client['mac'] for client in self.last_results
if client.get('mac')] if client.get('mac')]
# Supressing no-self-use warning # Suppressing no-self-use warning
# pylint: disable=R0201 # pylint: disable=R0201
def get_device_name(self, device): def get_device_name(self, device):
"""Return the name of the given device or None if we don't know.""" """Return the name of the given device or None if we don't know."""

View File

@ -69,7 +69,7 @@ class XiaomiDeviceScanner(DeviceScanner):
return self.mac2name.get(device.upper(), None) return self.mac2name.get(device.upper(), None)
def _update_info(self): def _update_info(self):
"""Ensure the informations from the router are up to date. """Ensure the information from the router are up to date.
Returns true if scanning successful. Returns true if scanning successful.
""" """

View File

@ -21,7 +21,7 @@ from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.helpers.discovery import async_load_platform, async_discover from homeassistant.helpers.discovery import async_load_platform, async_discover
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
REQUIREMENTS = ['netdisco==1.2.0'] REQUIREMENTS = ['netdisco==1.2.2']
DOMAIN = 'discovery' DOMAIN = 'discovery'

View File

@ -122,7 +122,7 @@ def setup(hass, config):
_LOGGER.info("Downloading of %s done", url) _LOGGER.info("Downloading of %s done", url)
except requests.exceptions.ConnectionError: except requests.exceptions.ConnectionError:
_LOGGER.exception("ConnectionError occured for %s", url) _LOGGER.exception("ConnectionError occurred for %s", url)
# Remove file if we started downloading but failed # Remove file if we started downloading but failed
if final_path and os.path.isfile(final_path): if final_path and os.path.isfile(final_path):

View File

@ -0,0 +1,102 @@
"""Integrate with DuckDNS."""
import asyncio
from datetime import timedelta
import logging
import voluptuous as vol
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_DOMAIN
from homeassistant.loader import bind_hass
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.aiohttp_client import async_get_clientsession
DOMAIN = 'duckdns'
UPDATE_URL = 'https://www.duckdns.org/update'
INTERVAL = timedelta(minutes=5)
_LOGGER = logging.getLogger(__name__)
SERVICE_SET_TXT = 'set_txt'
ATTR_TXT = 'txt'
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_DOMAIN): cv.string,
vol.Required(CONF_ACCESS_TOKEN): cv.string,
})
}, extra=vol.ALLOW_EXTRA)
SERVICE_TXT_SCHEMA = vol.Schema({
vol.Required(ATTR_TXT): vol.Any(None, cv.string)
})
@bind_hass
@asyncio.coroutine
def async_set_txt(hass, txt):
"""Set the txt record. Pass in None to remove it."""
yield from hass.services.async_call(DOMAIN, SERVICE_SET_TXT, {
ATTR_TXT: txt
}, blocking=True)
@asyncio.coroutine
def async_setup(hass, config):
"""Initialize the DuckDNS component."""
domain = config[DOMAIN][CONF_DOMAIN]
token = config[DOMAIN][CONF_ACCESS_TOKEN]
session = async_get_clientsession(hass)
result = yield from _update_duckdns(session, domain, token)
if not result:
return False
@asyncio.coroutine
def update_domain_interval(now):
"""Update the DuckDNS entry."""
yield from _update_duckdns(session, domain, token)
@asyncio.coroutine
def update_domain_service(call):
"""Update the DuckDNS entry."""
yield from _update_duckdns(session, domain, token,
txt=call.data[ATTR_TXT])
async_track_time_interval(hass, update_domain_interval, INTERVAL)
hass.services.async_register(
DOMAIN, SERVICE_SET_TXT, update_domain_service,
schema=SERVICE_TXT_SCHEMA)
return result
_SENTINEL = object()
@asyncio.coroutine
def _update_duckdns(session, domain, token, *, txt=_SENTINEL, clear=False):
"""Update DuckDNS."""
params = {
'domains': domain,
'token': token,
}
if txt is not _SENTINEL:
if txt is None:
# Pass in empty txt value to indicate it's clearing txt record
params['txt'] = ''
clear = True
else:
params['txt'] = txt
if clear:
params['clear'] = 'true'
resp = yield from session.get(UPDATE_URL, params=params)
body = yield from resp.text()
if body != 'OK':
_LOGGER.warning('Updating DuckDNS domain %s failed', domain)
return False
return True

View File

@ -15,7 +15,7 @@ from homeassistant.helpers import discovery
from homeassistant.const import CONF_API_KEY from homeassistant.const import CONF_API_KEY
from homeassistant.util import Throttle from homeassistant.util import Throttle
REQUIREMENTS = ['python-ecobee-api==0.0.9'] REQUIREMENTS = ['python-ecobee-api==0.0.10']
_CONFIGURING = {} _CONFIGURING = {}
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@ -16,6 +16,7 @@ from homeassistant.const import (
) )
from homeassistant.components.http import REQUIREMENTS # NOQA from homeassistant.components.http import REQUIREMENTS # NOQA
from homeassistant.components.http import HomeAssistantWSGI from homeassistant.components.http import HomeAssistantWSGI
from homeassistant.helpers.deprecation import get_deprecated
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from .hue_api import ( from .hue_api import (
HueUsernameView, HueAllLightsStateView, HueOneLightStateView, HueUsernameView, HueAllLightsStateView, HueOneLightStateView,
@ -66,6 +67,7 @@ CONFIG_SCHEMA = vol.Schema({
}, extra=vol.ALLOW_EXTRA) }, extra=vol.ALLOW_EXTRA)
ATTR_EMULATED_HUE = 'emulated_hue' ATTR_EMULATED_HUE = 'emulated_hue'
ATTR_EMULATED_HUE_HIDDEN = 'emulated_hue_hidden'
def setup(hass, yaml_config): def setup(hass, yaml_config):
@ -148,7 +150,7 @@ class Config(object):
self.listen_port) self.listen_port)
if self.type == TYPE_GOOGLE and self.listen_port != 80: if self.type == TYPE_GOOGLE and self.listen_port != 80:
_LOGGER.warning("When targetting Google Home, listening port has " _LOGGER.warning("When targeting Google Home, listening port has "
"to be port 80") "to be port 80")
# Get whether or not UPNP binds to multicast address (239.255.255.250) # Get whether or not UPNP binds to multicast address (239.255.255.250)
@ -223,7 +225,15 @@ class Config(object):
domain = entity.domain.lower() domain = entity.domain.lower()
explicit_expose = entity.attributes.get(ATTR_EMULATED_HUE, None) explicit_expose = entity.attributes.get(ATTR_EMULATED_HUE, None)
explicit_hidden = entity.attributes.get(ATTR_EMULATED_HUE_HIDDEN, None)
if explicit_expose is True or explicit_hidden is False:
expose = True
elif explicit_expose is False or explicit_hidden is True:
expose = False
else:
expose = None
get_deprecated(entity.attributes, ATTR_EMULATED_HUE_HIDDEN,
ATTR_EMULATED_HUE, None)
domain_exposed_by_default = \ domain_exposed_by_default = \
self.expose_by_default and domain in self.exposed_domains self.expose_by_default and domain in self.exposed_domains
@ -231,9 +241,9 @@ class Config(object):
# the configuration doesn't explicitly exclude it from being # the configuration doesn't explicitly exclude it from being
# exposed, or if the entity is explicitly exposed # exposed, or if the entity is explicitly exposed
is_default_exposed = \ is_default_exposed = \
domain_exposed_by_default and explicit_expose is not False domain_exposed_by_default and expose is not False
return is_default_exposed or explicit_expose return is_default_exposed or expose
def _load_numbers_json(self): def _load_numbers_json(self):
"""Set up helper method to load numbers json.""" """Set up helper method to load numbers json."""

View File

@ -1,4 +1,4 @@
"""Provides a UPNP discovery method that mimicks Hue hubs.""" """Provides a UPNP discovery method that mimics Hue hubs."""
import threading import threading
import socket import socket
import logging import logging
@ -123,14 +123,14 @@ USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1
if ssdp_socket in read: if ssdp_socket in read:
data, addr = ssdp_socket.recvfrom(1024) data, addr = ssdp_socket.recvfrom(1024)
else: else:
# most likely the timeout, so check for interupt # most likely the timeout, so check for interrupt
continue continue
except socket.error as ex: except socket.error as ex:
if self._interrupted: if self._interrupted:
clean_socket_close(ssdp_socket) clean_socket_close(ssdp_socket)
return return
_LOGGER.error("UPNP Responder socket exception occured: %s", _LOGGER.error("UPNP Responder socket exception occurred: %s",
ex.__str__) ex.__str__)
# without the following continue, a second exception occurs # without the following continue, a second exception occurs
# because the data object has not been initialized # because the data object has not been initialized

View File

@ -137,7 +137,7 @@ class InsteonLocalFanDevice(FanEntity):
@property @property
def name(self): def name(self):
"""Return the the name of the node.""" """Return the name of the node."""
return self.node.deviceName return self.node.deviceName
@property @property

View File

@ -3,10 +3,10 @@
FINGERPRINTS = { FINGERPRINTS = {
"compatibility.js": "1686167ff210e001f063f5c606b2e74b", "compatibility.js": "1686167ff210e001f063f5c606b2e74b",
"core.js": "2a7d01e45187c7d4635da05065b5e54e", "core.js": "2a7d01e45187c7d4635da05065b5e54e",
"frontend.html": "7e13ce36d3141182a62a5b061e87e77a", "frontend.html": "2de1bde3b4a6c6c47dd95504fc098906",
"mdi.html": "89074face5529f5fe6fbae49ecb3e88b", "mdi.html": "2e848b4da029bf73d426d5ba058a088d",
"micromarkdown-js.html": "93b5ec4016f0bba585521cf4d18dec1a", "micromarkdown-js.html": "93b5ec4016f0bba585521cf4d18dec1a",
"panels/ha-panel-config.html": "61f65e75e39368e07441d7d6a4e36ae3", "panels/ha-panel-config.html": "52e2e1d477bfd6dc3708d65b8337f0af",
"panels/ha-panel-dev-event.html": "d409e7ab537d9fe629126d122345279c", "panels/ha-panel-dev-event.html": "d409e7ab537d9fe629126d122345279c",
"panels/ha-panel-dev-info.html": "b0e55eb657fd75f21aba2426ac0cedc0", "panels/ha-panel-dev-info.html": "b0e55eb657fd75f21aba2426ac0cedc0",
"panels/ha-panel-dev-mqtt.html": "94b222b013a98583842de3e72d5888c6", "panels/ha-panel-dev-mqtt.html": "94b222b013a98583842de3e72d5888c6",

File diff suppressed because one or more lines are too long

@ -1 +1 @@
Subproject commit a46e6b4cfa24d99011a9755e2588d761d78af152 Subproject commit b0791abb9a61216476cb3a637c410cdddef7e91c

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -37,7 +37,7 @@
/* eslint-disable indent, no-unused-vars, no-multiple-empty-lines, max-nested-callbacks, space-before-function-paren, quotes, comma-spacing */ /* eslint-disable indent, no-unused-vars, no-multiple-empty-lines, max-nested-callbacks, space-before-function-paren, quotes, comma-spacing */
'use strict'; 'use strict';
var precacheConfig = [["/","da6c72cc82251a0456b2e678ebb6795c"],["/frontend/panels/dev-event-d409e7ab537d9fe629126d122345279c.html","936814991f2a5e23d61d29f0d40f81b8"],["/frontend/panels/dev-info-b0e55eb657fd75f21aba2426ac0cedc0.html","1fa953b0224470f70d4e87bbe4dff191"],["/frontend/panels/dev-mqtt-94b222b013a98583842de3e72d5888c6.html","dc3ddfac58397feda97317358f0aecbb"],["/frontend/panels/dev-service-422b2c181ee0713fa31d45a64e605baf.html","ae7d26b1c8c3309fd3c65944f89ea03f"],["/frontend/panels/dev-state-7948d3dba058f31517d880df8ed0e857.html","ff8156bb1a52490fcc07466556fce0e1"],["/frontend/panels/dev-template-928e7b81b9c113b70edc9f4a1d051827.html","312c8313800b44c83bcb8dc2df30c759"],["/frontend/panels/map-565db019147162080c21af962afc097f.html","a1a360042395682335e2f471dddad309"],["/static/compatibility-1686167ff210e001f063f5c606b2e74b.js","6ee7b5e2dd82b510c3bd92f7e215988e"],["/static/core-2a7d01e45187c7d4635da05065b5e54e.js","90a0a8a6a6dd0ca41b16f40e7d23924d"],["/static/frontend-7e13ce36d3141182a62a5b061e87e77a.html","73f53a9b597e1e69e0b3e56f4fc8f020"],["/static/mdi-89074face5529f5fe6fbae49ecb3e88b.html","97754e463f9e56a95c813d4d8e792347"],["static/fonts/roboto/Roboto-Bold.ttf","d329cc8b34667f114a95422aaad1b063"],["static/fonts/roboto/Roboto-Light.ttf","7b5fb88f12bec8143f00e21bc3222124"],["static/fonts/roboto/Roboto-Medium.ttf","fe13e4170719c2fc586501e777bde143"],["static/fonts/roboto/Roboto-Regular.ttf","ac3f799d5bbaf5196fab15ab8de8431c"],["static/icons/favicon-192x192.png","419903b8422586a7e28021bbe9011175"],["static/icons/favicon.ico","04235bda7843ec2fceb1cbe2bc696cf4"],["static/images/card_media_player_bg.png","a34281d1c1835d338a642e90930e61aa"]]; var precacheConfig = [["/","517bbe13d945f188a0896870cd3055d0"],["/frontend/panels/dev-event-d409e7ab537d9fe629126d122345279c.html","936814991f2a5e23d61d29f0d40f81b8"],["/frontend/panels/dev-info-b0e55eb657fd75f21aba2426ac0cedc0.html","1fa953b0224470f70d4e87bbe4dff191"],["/frontend/panels/dev-mqtt-94b222b013a98583842de3e72d5888c6.html","dc3ddfac58397feda97317358f0aecbb"],["/frontend/panels/dev-service-422b2c181ee0713fa31d45a64e605baf.html","ae7d26b1c8c3309fd3c65944f89ea03f"],["/frontend/panels/dev-state-7948d3dba058f31517d880df8ed0e857.html","ff8156bb1a52490fcc07466556fce0e1"],["/frontend/panels/dev-template-928e7b81b9c113b70edc9f4a1d051827.html","312c8313800b44c83bcb8dc2df30c759"],["/frontend/panels/map-565db019147162080c21af962afc097f.html","a1a360042395682335e2f471dddad309"],["/static/compatibility-1686167ff210e001f063f5c606b2e74b.js","6ee7b5e2dd82b510c3bd92f7e215988e"],["/static/core-2a7d01e45187c7d4635da05065b5e54e.js","90a0a8a6a6dd0ca41b16f40e7d23924d"],["/static/frontend-2de1bde3b4a6c6c47dd95504fc098906.html","51faf83c8a2d86529bc76a67fe6b5234"],["/static/mdi-89074face5529f5fe6fbae49ecb3e88b.html","97754e463f9e56a95c813d4d8e792347"],["static/fonts/roboto/Roboto-Bold.ttf","d329cc8b34667f114a95422aaad1b063"],["static/fonts/roboto/Roboto-Light.ttf","7b5fb88f12bec8143f00e21bc3222124"],["static/fonts/roboto/Roboto-Medium.ttf","fe13e4170719c2fc586501e777bde143"],["static/fonts/roboto/Roboto-Regular.ttf","ac3f799d5bbaf5196fab15ab8de8431c"],["static/icons/favicon-192x192.png","419903b8422586a7e28021bbe9011175"],["static/icons/favicon.ico","04235bda7843ec2fceb1cbe2bc696cf4"],["static/images/card_media_player_bg.png","a34281d1c1835d338a642e90930e61aa"]];
var cacheName = 'sw-precache-v3--' + (self.registration ? self.registration.scope : ''); var cacheName = 'sw-precache-v3--' + (self.registration ? self.registration.scope : '');

View File

@ -7,6 +7,13 @@
Code distributed by Google as part of the polymer project is also Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
Copyright (c) 2014 The Polymer Project Authors. All rights reserved. Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
@ -20,174 +27,170 @@ The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/ */
'use strict';var Jb="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global&&null!=global?global:this; 'use strict';var N="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global&&null!=global?global:this,Oa="function"==typeof Object.defineProperties?Object.defineProperty:function(g,t,R){g!=Array.prototype&&g!=Object.prototype&&(g[t]=R.value)};function Pb(){Pb=function(){};N.Symbol||(N.Symbol=Qb)}var Qb=function(){var g=0;return function(t){return"jscomp_symbol_"+(t||"")+g++}}();
(function(){function k(){var a=this;this.s={};this.g=document.documentElement;var b=new Ka;b.rules=[];this.h=t.set(this.g,new t(b));this.i=!1;this.b=this.a=null;Kb(function(){a.c()})}function F(){this.customStyles=[];this.enqueued=!1}function Lb(){}function pa(a){this.cache={};this.c=void 0===a?100:a}function p(){}function t(a,b,c,d,e){this.G=a||null;this.b=b||null;this.ra=c||[];this.P=null;this.Z=e||"";this.a=this.B=this.K=null}function r(){}function Ka(){this.end=this.start=0;this.rules=this.parent= function Od(){Pb();var g=N.Symbol.iterator;g||(g=N.Symbol.iterator=N.Symbol("iterator"));"function"!=typeof Array.prototype[g]&&Oa(Array.prototype,g,{configurable:!0,writable:!0,value:function(){return Pd(this)}});Od=function(){}}function Pd(g){var t=0;return Qd(function(){return t<g.length?{done:!1,value:g[t++]}:{done:!0}})}function Qd(g){Od();g={next:g};g[N.Symbol.iterator]=function(){return this};return g}function Rd(g){Od();var t=g[Symbol.iterator];return t?t.call(g):Pd(g)}
this.previous=null;this.cssText=this.parsedCssText="";this.atRule=!1;this.type=0;this.parsedSelector=this.selector=this.keyframesName=""}function Id(a){function b(b,c){Object.defineProperty(b,"innerHTML",{enumerable:c.enumerable,configurable:!0,get:c.get,set:function(b){var d=this,e=void 0;n(this)&&(e=[],P(this,function(a){a!==d&&e.push(a)}));c.set.call(this,b);if(e)for(var f=0;f<e.length;f++){var g=e[f];1===g.__CE_state&&a.disconnectedCallback(g)}this.ownerDocument.__CE_hasRegistry?a.f(this):a.l(this); function Sd(g){for(var t,R=[];!(t=g.next()).done;)R.push(t.value);return R}
return b}})}function c(b,c){u(b,"insertAdjacentElement",function(b,d){var e=n(d);b=c.call(this,b,d);e&&a.a(d);n(b)&&a.b(d);return b})}Mb?u(Element.prototype,"attachShadow",function(a){return this.__CE_shadowRoot=a=Mb.call(this,a)}):console.warn("Custom Elements: `Element#attachShadow` was not patched.");if(La&&La.get)b(Element.prototype,La);else if(Ma&&Ma.get)b(HTMLElement.prototype,Ma);else{var d=Na.call(document,"div");a.v(function(a){b(a,{enumerable:!0,configurable:!0,get:function(){return Nb.call(this, (function(){function g(){var a=this;this.s={};this.g=document.documentElement;var b=new Pa;b.rules=[];this.h=w.set(this.g,new w(b));this.i=!1;this.b=this.a=null;Rb(function(){a.c()})}function t(){this.customStyles=[];this.enqueued=!1}function R(){}function ta(a){this.cache={};this.c=void 0===a?100:a}function p(){}function w(a,b,c,d,e){this.G=a||null;this.b=b||null;this.ua=c||[];this.S=null;this.$=e||"";this.a=this.C=this.M=null}function v(){}function Pa(){this.end=this.start=0;this.rules=this.parent=
!0).innerHTML},set:function(a){var b="template"===this.localName?this.content:this;for(d.innerHTML=a;0<b.childNodes.length;)Oa.call(b,b.childNodes[0]);for(;0<d.childNodes.length;)qa.call(b,d.childNodes[0])}})})}u(Element.prototype,"setAttribute",function(b,c){if(1!==this.__CE_state)return Ob.call(this,b,c);var d=Pa.call(this,b);Ob.call(this,b,c);c=Pa.call(this,b);a.attributeChangedCallback(this,b,d,c,null)});u(Element.prototype,"setAttributeNS",function(b,c,d){if(1!==this.__CE_state)return Pb.call(this, this.previous=null;this.cssText=this.parsedCssText="";this.atRule=!1;this.type=0;this.parsedSelector=this.selector=this.keyframesName=""}function Td(a){function b(b,c){Object.defineProperty(b,"innerHTML",{enumerable:c.enumerable,configurable:!0,get:c.get,set:function(b){var d=this,e=void 0;u(this)&&(e=[],S(this,function(a){a!==d&&e.push(a)}));c.set.call(this,b);if(e)for(var f=0;f<e.length;f++){var h=e[f];1===h.__CE_state&&a.disconnectedCallback(h)}this.ownerDocument.__CE_hasRegistry?a.f(this):a.l(this);
b,c,d);var e=ra.call(this,b,c);Pb.call(this,b,c,d);d=ra.call(this,b,c);a.attributeChangedCallback(this,c,e,d,b)});u(Element.prototype,"removeAttribute",function(b){if(1!==this.__CE_state)return Qb.call(this,b);var c=Pa.call(this,b);Qb.call(this,b);null!==c&&a.attributeChangedCallback(this,b,c,null,null)});u(Element.prototype,"removeAttributeNS",function(b,c){if(1!==this.__CE_state)return Rb.call(this,b,c);var d=ra.call(this,b,c);Rb.call(this,b,c);var e=ra.call(this,b,c);d!==e&&a.attributeChangedCallback(this, return b}})}function c(b,c){x(b,"insertAdjacentElement",function(b,d){var e=u(d);b=c.call(this,b,d);e&&a.a(d);u(b)&&a.b(d);return b})}Sb&&x(Element.prototype,"attachShadow",function(a){return this.__CE_shadowRoot=a=Sb.call(this,a)});if(Qa&&Qa.get)b(Element.prototype,Qa);else if(Ra&&Ra.get)b(HTMLElement.prototype,Ra);else{var d=Sa.call(document,"div");a.v(function(a){b(a,{enumerable:!0,configurable:!0,get:function(){return Tb.call(this,!0).innerHTML},set:function(a){var b="template"===this.localName?
c,d,e,b)});Sb?c(HTMLElement.prototype,Sb):Tb?c(Element.prototype,Tb):console.warn("Custom Elements: `Element#insertAdjacentElement` was not patched.");Ub(a,Element.prototype,{Ka:Jd,append:Kd});Ld(a,{kb:Md,jb:Nd,replaceWith:Od,remove:Pd})}function Ld(a,b){var c=Element.prototype;c.before=function(c){for(var d=[],f=0;f<arguments.length;++f)d[f-0]=arguments[f];f=d.filter(function(a){return a instanceof Node&&n(a)});b.kb.apply(this,d);for(var g=0;g<f.length;g++)a.a(f[g]);if(n(this))for(f=0;f<d.length;f++)g= this.content:this;for(d.innerHTML=a;0<b.childNodes.length;)Ta.call(b,b.childNodes[0]);for(;0<d.childNodes.length;)ua.call(b,d.childNodes[0])}})})}x(Element.prototype,"setAttribute",function(b,c){if(1!==this.__CE_state)return Ub.call(this,b,c);var d=Ua.call(this,b);Ub.call(this,b,c);c=Ua.call(this,b);a.attributeChangedCallback(this,b,d,c,null)});x(Element.prototype,"setAttributeNS",function(b,c,d){if(1!==this.__CE_state)return Vb.call(this,b,c,d);var e=va.call(this,b,c);Vb.call(this,b,c,d);d=va.call(this,
d[f],g instanceof Element&&a.b(g)};c.after=function(c){for(var d=[],f=0;f<arguments.length;++f)d[f-0]=arguments[f];f=d.filter(function(a){return a instanceof Node&&n(a)});b.jb.apply(this,d);for(var g=0;g<f.length;g++)a.a(f[g]);if(n(this))for(f=0;f<d.length;f++)g=d[f],g instanceof Element&&a.b(g)};c.replaceWith=function(c){for(var d=[],f=0;f<arguments.length;++f)d[f-0]=arguments[f];f=d.filter(function(a){return a instanceof Node&&n(a)});var g=n(this);b.replaceWith.apply(this,d);for(var h=0;h<f.length;h++)a.a(f[h]); b,c);a.attributeChangedCallback(this,c,e,d,b)});x(Element.prototype,"removeAttribute",function(b){if(1!==this.__CE_state)return Wb.call(this,b);var c=Ua.call(this,b);Wb.call(this,b);null!==c&&a.attributeChangedCallback(this,b,c,null,null)});x(Element.prototype,"removeAttributeNS",function(b,c){if(1!==this.__CE_state)return Xb.call(this,b,c);var d=va.call(this,b,c);Xb.call(this,b,c);var e=va.call(this,b,c);d!==e&&a.attributeChangedCallback(this,c,d,e,b)});Yb?c(HTMLElement.prototype,Yb):Zb?c(Element.prototype,
if(g)for(a.a(this),f=0;f<d.length;f++)g=d[f],g instanceof Element&&a.b(g)};c.remove=function(){var c=n(this);b.remove.call(this);c&&a.a(this)}}function Qd(a){function b(b,d){Object.defineProperty(b,"textContent",{enumerable:d.enumerable,configurable:!0,get:d.get,set:function(b){if(this.nodeType===Node.TEXT_NODE)d.set.call(this,b);else{var c=void 0;if(this.firstChild){var e=this.childNodes,h=e.length;if(0<h&&n(this)){c=Array(h);for(var m=0;m<h;m++)c[m]=e[m]}}d.set.call(this,b);if(c)for(b=0;b<c.length;b++)a.a(c[b])}}})} Zb):console.warn("Custom Elements: `Element#insertAdjacentElement` was not patched.");Va(a,Element.prototype,{ea:Ud,append:Vd});Wd(a,{ra:Xd,jb:Yd,replaceWith:Zd,remove:$d})}function Wd(a,b){var c=Element.prototype;function d(b){return function(c){for(var d=[],e=0;e<arguments.length;++e)d[e-0]=arguments[e];e=[];for(var f=[],q=0;q<d.length;q++){var g=d[q];g instanceof Element&&u(g)&&f.push(g);if(g instanceof DocumentFragment)for(g=g.firstChild;g;g=g.nextSibling)e.push(g);else e.push(g)}b.apply(this,
u(Node.prototype,"insertBefore",function(b,d){if(b instanceof DocumentFragment){var c=Array.prototype.slice.apply(b.childNodes);b=Vb.call(this,b,d);if(n(this))for(d=0;d<c.length;d++)a.b(c[d]);return b}c=n(b);d=Vb.call(this,b,d);c&&a.a(b);n(this)&&a.b(b);return d});u(Node.prototype,"appendChild",function(b){if(b instanceof DocumentFragment){var c=Array.prototype.slice.apply(b.childNodes);b=qa.call(this,b);if(n(this))for(var e=0;e<c.length;e++)a.b(c[e]);return b}c=n(b);e=qa.call(this,b);c&&a.a(b);n(this)&& d);for(d=0;d<f.length;d++)a.a(f[d]);if(u(this))for(d=0;d<e.length;d++)f=e[d],f instanceof Element&&a.b(f)}}void 0!==b.ra&&(c.before=d(b.ra));void 0!==b.ra&&(c.after=d(b.jb));void 0!==b.replaceWith&&x(c,"replaceWith",function(c){for(var d=[],e=0;e<arguments.length;++e)d[e-0]=arguments[e];e=[];for(var k=[],m=0;m<d.length;m++){var g=d[m];g instanceof Element&&u(g)&&k.push(g);if(g instanceof DocumentFragment)for(g=g.firstChild;g;g=g.nextSibling)e.push(g);else e.push(g)}m=u(this);b.replaceWith.apply(this,
a.b(b);return e});u(Node.prototype,"cloneNode",function(b){b=Nb.call(this,b);this.ownerDocument.__CE_hasRegistry?a.f(b):a.l(b);return b});u(Node.prototype,"removeChild",function(b){var c=n(b),e=Oa.call(this,b);c&&a.a(b);return e});u(Node.prototype,"replaceChild",function(b,d){if(b instanceof DocumentFragment){var c=Array.prototype.slice.apply(b.childNodes);b=Wb.call(this,b,d);if(n(this))for(a.a(d),d=0;d<c.length;d++)a.b(c[d]);return b}c=n(b);var f=Wb.call(this,b,d),g=n(this);g&&a.a(d);c&&a.a(b);g&& d);for(d=0;d<k.length;d++)a.a(k[d]);if(m)for(a.a(this),d=0;d<e.length;d++)k=e[d],k instanceof Element&&a.b(k)});void 0!==b.remove&&x(c,"remove",function(){var c=u(this);b.remove.call(this);c&&a.a(this)})}function ae(a){function b(b,d){Object.defineProperty(b,"textContent",{enumerable:d.enumerable,configurable:!0,get:d.get,set:function(b){if(this.nodeType===Node.TEXT_NODE)d.set.call(this,b);else{var c=void 0;if(this.firstChild){var e=this.childNodes,k=e.length;if(0<k&&u(this)){c=Array(k);for(var m=
a.b(b);return f});Qa&&Qa.get?b(Node.prototype,Qa):a.v(function(a){b(a,{enumerable:!0,configurable:!0,get:function(){for(var a=[],b=0;b<this.childNodes.length;b++)a.push(this.childNodes[b].textContent);return a.join("")},set:function(a){for(;this.firstChild;)Oa.call(this,this.firstChild);qa.call(this,document.createTextNode(a))}})})}function Rd(a){u(Document.prototype,"createElement",function(b){if(this.__CE_hasRegistry){var c=a.c(b);if(c)return new c.constructor}b=Na.call(this,b);a.g(b);return b}); 0;m<k;m++)c[m]=e[m]}}d.set.call(this,b);if(c)for(b=0;b<c.length;b++)a.a(c[b])}}})}x(Node.prototype,"insertBefore",function(b,d){if(b instanceof DocumentFragment){var c=Array.prototype.slice.apply(b.childNodes);b=$b.call(this,b,d);if(u(this))for(d=0;d<c.length;d++)a.b(c[d]);return b}c=u(b);d=$b.call(this,b,d);c&&a.a(b);u(this)&&a.b(b);return d});x(Node.prototype,"appendChild",function(b){if(b instanceof DocumentFragment){var c=Array.prototype.slice.apply(b.childNodes);b=ua.call(this,b);if(u(this))for(var e=
u(Document.prototype,"importNode",function(b,c){b=Sd.call(this,b,c);this.__CE_hasRegistry?a.f(b):a.l(b);return b});u(Document.prototype,"createElementNS",function(b,c){if(this.__CE_hasRegistry&&(null===b||"http://www.w3.org/1999/xhtml"===b)){var d=a.c(c);if(d)return new d.constructor}b=Td.call(this,b,c);a.g(b);return b});Ub(a,Document.prototype,{Ka:Ud,append:Vd})}function Ub(a,b,c){b.prepend=function(b){for(var d=[],f=0;f<arguments.length;++f)d[f-0]=arguments[f];f=d.filter(function(a){return a instanceof 0;e<c.length;e++)a.b(c[e]);return b}c=u(b);e=ua.call(this,b);c&&a.a(b);u(this)&&a.b(b);return e});x(Node.prototype,"cloneNode",function(b){b=Tb.call(this,b);this.ownerDocument.__CE_hasRegistry?a.f(b):a.l(b);return b});x(Node.prototype,"removeChild",function(b){var c=u(b),e=Ta.call(this,b);c&&a.a(b);return e});x(Node.prototype,"replaceChild",function(b,d){if(b instanceof DocumentFragment){var c=Array.prototype.slice.apply(b.childNodes);b=ac.call(this,b,d);if(u(this))for(a.a(d),d=0;d<c.length;d++)a.b(c[d]);
Node&&n(a)});c.Ka.apply(this,d);for(var g=0;g<f.length;g++)a.a(f[g]);if(n(this))for(f=0;f<d.length;f++)g=d[f],g instanceof Element&&a.b(g)};b.append=function(b){for(var d=[],f=0;f<arguments.length;++f)d[f-0]=arguments[f];f=d.filter(function(a){return a instanceof Node&&n(a)});c.append.apply(this,d);for(var g=0;g<f.length;g++)a.a(f[g]);if(n(this))for(f=0;f<d.length;f++)g=d[f],g instanceof Element&&a.b(g)}}function Wd(a){window.HTMLElement=function(){function b(){var b=this.constructor,d=a.C(b);if(!d)throw Error("The custom element being constructed was not registered with `customElements`."); return b}c=u(b);var f=ac.call(this,b,d),h=u(this);h&&a.a(d);c&&a.a(b);h&&a.b(b);return f});Wa&&Wa.get?b(Node.prototype,Wa):a.v(function(a){b(a,{enumerable:!0,configurable:!0,get:function(){for(var a=[],b=0;b<this.childNodes.length;b++)a.push(this.childNodes[b].textContent);return a.join("")},set:function(a){for(;this.firstChild;)Ta.call(this,this.firstChild);ua.call(this,document.createTextNode(a))}})})}function be(a){x(Document.prototype,"createElement",function(b){if(this.__CE_hasRegistry){var c=
var e=d.constructionStack;if(0===e.length)return e=Na.call(document,d.localName),Object.setPrototypeOf(e,b.prototype),e.__CE_state=1,e.__CE_definition=d,a.g(e),e;d=e.length-1;var f=e[d];if(f===Xb)throw Error("The HTMLElement constructor was either called reentrantly for this constructor or called multiple times.");e[d]=Xb;Object.setPrototypeOf(f,b.prototype);a.g(f);return f}b.prototype=Xd.prototype;return b}()}function x(a){this.c=!1;this.a=a;this.h=new Map;this.f=function(a){return a()};this.b=!1; a.c(b);if(c)return new c.constructor}b=Sa.call(this,b);a.g(b);return b});x(Document.prototype,"importNode",function(b,c){b=ce.call(this,b,c);this.__CE_hasRegistry?a.f(b):a.l(b);return b});x(Document.prototype,"createElementNS",function(b,c){if(this.__CE_hasRegistry&&(null===b||"http://www.w3.org/1999/xhtml"===b)){var d=a.c(c);if(d)return new d.constructor}b=de.call(this,b,c);a.g(b);return b});Va(a,Document.prototype,{ea:ee,append:fe})}function Va(a,b,c){function d(b){return function(c){for(var d=
this.g=[];this.i=new Ra(a,document)}function Yb(){var a=this;this.b=this.a=void 0;this.c=new Promise(function(b){a.b=b;a.a&&b(a.a)})}function Ra(a,b){this.b=a;this.a=b;this.N=void 0;this.b.f(this.a);"loading"===this.a.readyState&&(this.N=new MutationObserver(this.f.bind(this)),this.N.observe(this.a,{childList:!0,subtree:!0}))}function z(){this.u=new Map;this.s=new Map;this.j=[];this.h=!1}function l(a,b,c){if(a!==Zb)throw new TypeError("Illegal constructor");a=document.createDocumentFragment();a.__proto__= [],e=0;e<arguments.length;++e)d[e-0]=arguments[e];e=[];for(var f=[],g=0;g<d.length;g++){var n=d[g];n instanceof Element&&u(n)&&f.push(n);if(n instanceof DocumentFragment)for(n=n.firstChild;n;n=n.nextSibling)e.push(n);else e.push(n)}b.apply(this,d);for(d=0;d<f.length;d++)a.a(f[d]);if(u(this))for(d=0;d<e.length;d++)f=e[d],f instanceof Element&&a.b(f)}}void 0!==c.ea&&(b.prepend=d(c.ea));void 0!==c.append&&(b.append=d(c.append))}function ge(a){window.HTMLElement=function(){function b(){var b=this.constructor,
l.prototype;a.D(b,c);return a}function sa(a){if(!a.__shady||void 0===a.__shady.firstChild){a.__shady=a.__shady||{};a.__shady.firstChild=Sa(a);a.__shady.lastChild=Ta(a);$b(a);for(var b=a.__shady.childNodes=U(a),c=0,d;c<b.length&&(d=b[c]);c++)d.__shady=d.__shady||{},d.__shady.parentNode=a,d.__shady.nextSibling=b[c+1]||null,d.__shady.previousSibling=b[c-1]||null,ac(d)}}function Yd(a){var b=a&&a.N;b&&(b.ba.delete(a.ab),b.ba.size||(a.fb.__shady.X=null))}function Zd(a,b){a.__shady=a.__shady||{};a.__shady.X|| d=a.B(b);if(!d)throw Error("The custom element being constructed was not registered with `customElements`.");var e=d.constructionStack;if(0===e.length)return e=Sa.call(document,d.localName),Object.setPrototypeOf(e,b.prototype),e.__CE_state=1,e.__CE_definition=d,a.g(e),e;d=e.length-1;var f=e[d];if(f===bc)throw Error("The HTMLElement constructor was either called reentrantly for this constructor or called multiple times.");e[d]=bc;Object.setPrototypeOf(f,b.prototype);a.g(f);return f}b.prototype=he.prototype;
(a.__shady.X=new ta);a.__shady.X.ba.add(b);var c=a.__shady.X;return{ab:b,N:c,fb:a,takeRecords:function(){return c.takeRecords()}}}function ta(){this.a=!1;this.addedNodes=[];this.removedNodes=[];this.ba=new Set}function Q(a,b){V[W]=a;V[W+1]=b;W+=2;2===W&&(Ua?Ua(X):$d())}function ae(){return function(){return process.Gb(X)}}function be(){return"undefined"!==typeof Va?function(){Va(X)}:Wa()}function ce(){var a=0,b=new bc(X),c=document.createTextNode("");b.observe(c,{characterData:!0});return function(){c.data= return b}()}function y(a){this.c=!1;this.a=a;this.h=new Map;this.f=function(a){return a()};this.b=!1;this.g=[];this.i=new Xa(a,document)}function cc(){var a=this;this.b=this.a=void 0;this.c=new Promise(function(b){a.b=b;a.a&&b(a.a)})}function Xa(a,b){this.b=a;this.a=b;this.P=void 0;this.b.f(this.a);"loading"===this.a.readyState&&(this.P=new MutationObserver(this.f.bind(this)),this.P.observe(this.a,{childList:!0,subtree:!0}))}function B(){this.u=new Map;this.s=new Map;this.j=[];this.h=!1}function l(a,
a=++a%2}}function de(){var a=new MessageChannel;a.port1.onmessage=X;return function(){return a.port2.postMessage(0)}}function Wa(){var a=setTimeout;return function(){return a(X,1)}}function X(){for(var a=0;a<W;a+=2)(0,V[a])(V[a+1]),V[a]=void 0,V[a+1]=void 0;W=0}function ee(){try{var a=require("vertx");Va=a.Ib||a.Hb;return be()}catch(b){return Wa()}}function Xa(a,b){var c=this,d=new this.constructor(Y);void 0===d[ua]&&cc(d);var e=c.o;if(e){var f=arguments[e-1];Q(function(){return dc(e,d,f,c.m)})}else Ya(c, b,c){if(a!==dc)throw new TypeError("Illegal constructor");a=document.createDocumentFragment();a.__proto__=l.prototype;a.H(b,c);return a}function wa(a){if(!a.__shady||void 0===a.__shady.firstChild){a.__shady=a.__shady||{};a.__shady.firstChild=Ya(a);a.__shady.lastChild=Za(a);ec(a);for(var b=a.__shady.childNodes=X(a),c=0,d;c<b.length&&(d=b[c]);c++)d.__shady=d.__shady||{},d.__shady.parentNode=a,d.__shady.nextSibling=b[c+1]||null,d.__shady.previousSibling=b[c-1]||null,fc(d)}}function ie(a){var b=a&&a.P;
d,a,b);return d}function Za(a){if(a&&"object"===typeof a&&a.constructor===this)return a;var b=new this(Y);ea(b,a);return b}function Y(){}function ec(a){try{return a.then}catch(b){return fa.error=b,fa}}function fe(a,b,c,d){try{a.call(b,c,d)}catch(e){return e}}function ge(a,b,c){Q(function(a){var d=!1,f=fe(c,b,function(c){d||(d=!0,b!==c?ea(a,c):K(a,c))},function(b){d||(d=!0,A(a,b))});!d&&f&&(d=!0,A(a,f))},a)}function he(a,b){1===b.o?K(a,b.m):2===b.o?A(a,b.m):Ya(b,void 0,function(b){return ea(a,b)}, b&&(b.ca.delete(a.ab),b.ca.size||(a.fb.__shady.Y=null))}function je(a,b){a.__shady=a.__shady||{};a.__shady.Y||(a.__shady.Y=new xa);a.__shady.Y.ca.add(b);var c=a.__shady.Y;return{ab:b,P:c,fb:a,takeRecords:function(){return c.takeRecords()}}}function xa(){this.a=!1;this.addedNodes=[];this.removedNodes=[];this.ca=new Set}function T(a,b){Y[Z]=a;Y[Z+1]=b;Z+=2;2===Z&&($a?$a(aa):ke())}function le(){return function(){return process.Hb(aa)}}function me(){return"undefined"!==typeof ab?function(){ab(aa)}:bb()}
function(b){return A(a,b)})}function fc(a,b,c){b.constructor===a.constructor&&c===Xa&&b.constructor.resolve===Za?he(a,b):c===fa?(A(a,fa.error),fa.error=null):void 0===c?K(a,b):"function"===typeof c?ge(a,b,c):K(a,b)}function ea(a,b){if(a===b)A(a,new TypeError("You cannot resolve a promise with itself"));else{var c=typeof b;null===b||"object"!==c&&"function"!==c?K(a,b):fc(a,b,ec(b))}}function ie(a){a.Ba&&a.Ba(a.m);$a(a)}function K(a,b){void 0===a.o&&(a.m=b,a.o=1,0!==a.U.length&&Q($a,a))}function A(a, function ne(){var a=0,b=new gc(aa),c=document.createTextNode("");b.observe(c,{characterData:!0});return function(){c.data=a=++a%2}}function oe(){var a=new MessageChannel;a.port1.onmessage=aa;return function(){return a.port2.postMessage(0)}}function bb(){var a=setTimeout;return function(){return a(aa,1)}}function aa(){for(var a=0;a<Z;a+=2)(0,Y[a])(Y[a+1]),Y[a]=void 0,Y[a+1]=void 0;Z=0}function pe(){try{var a=require("vertx");ab=a.Jb||a.Ib;return me()}catch(b){return bb()}}function cb(a,b){var c=this,
b){void 0===a.o&&(a.o=2,a.m=b,Q(ie,a))}function Ya(a,b,c,d){var e=a.U,f=e.length;a.Ba=null;e[f]=b;e[f+1]=c;e[f+2]=d;0===f&&a.o&&Q($a,a)}function $a(a){var b=a.U,c=a.o;if(0!==b.length){for(var d,e,f=a.m,g=0;g<b.length;g+=3)d=b[g],e=b[g+c],d?dc(c,d,e,f):e(f);a.U.length=0}}function gc(){this.error=null}function dc(a,b,c,d){var e="function"===typeof c;if(e){try{var f=c(d)}catch(H){ab.error=H,f=ab}if(f===ab){var g=!0;var h=f.error;f.error=null}else var m=!0;if(b===f){A(b,new TypeError("A promises callback cannot return that same promise.")); d=new this.constructor(ba);void 0===d[ya]&&hc(d);var e=c.o;if(e){var f=arguments[e-1];T(function(){return ic(e,d,f,c.m)})}else db(c,d,a,b);return d}function eb(a){if(a&&"object"===typeof a&&a.constructor===this)return a;var b=new this(ba);ja(b,a);return b}function ba(){}function jc(a){try{return a.then}catch(b){return ka.error=b,ka}}function qe(a,b,c,d){try{a.call(b,c,d)}catch(e){return e}}function re(a,b,c){T(function(a){var d=!1,f=qe(c,b,function(c){d||(d=!0,b!==c?ja(a,c):L(a,c))},function(b){d||
return}}else f=d,m=!0;void 0===b.o&&(e&&m?ea(b,f):g?A(b,h):1===a?K(b,f):2===a&&A(b,f))}function je(a,b){try{b(function(b){ea(a,b)},function(b){A(a,b)})}catch(c){A(a,c)}}function cc(a){a[ua]=hc++;a.o=void 0;a.m=void 0;a.U=[]}function ha(a,b){this.eb=a;this.J=new a(Y);this.J[ua]||cc(this.J);ic(b)?(this.$=this.length=b.length,this.m=Array(this.length),0===this.length?K(this.J,this.m):(this.length=this.length||0,this.cb(b),0===this.$&&K(this.J,this.m))):A(this.J,Error("Array Methods must be provided an Array"))} (d=!0,C(a,b))});!d&&f&&(d=!0,C(a,f))},a)}function se(a,b){1===b.o?L(a,b.m):2===b.o?C(a,b.m):db(b,void 0,function(b){return ja(a,b)},function(b){return C(a,b)})}function kc(a,b,c){b.constructor===a.constructor&&c===cb&&b.constructor.resolve===eb?se(a,b):c===ka?(C(a,ka.error),ka.error=null):void 0===c?L(a,b):"function"===typeof c?re(a,b,c):L(a,b)}function ja(a,b){if(a===b)C(a,new TypeError("You cannot resolve a promise with itself"));else{var c=typeof b;null===b||"object"!==c&&"function"!==c?L(a,b):
function y(a){this[ua]=hc++;this.m=this.o=void 0;this.U=[];if(Y!==a){if("function"!==typeof a)throw new TypeError("You must pass a resolver function as the first argument to the promise constructor");if(this instanceof y)je(this,a);else throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");}}function Z(a){return a.__shady&&void 0!==a.__shady.firstChild}function I(a){return"ShadyRoot"===a.Wa}function ia(a){a=a.getRootNode(); kc(a,b,jc(b))}}function te(a){a.Da&&a.Da(a.m);fb(a)}function L(a,b){void 0===a.o&&(a.m=b,a.o=1,0!==a.V.length&&T(fb,a))}function C(a,b){void 0===a.o&&(a.o=2,a.m=b,T(te,a))}function db(a,b,c,d){var e=a.V,f=e.length;a.Da=null;e[f]=b;e[f+1]=c;e[f+2]=d;0===f&&a.o&&T(fb,a)}function fb(a){var b=a.V,c=a.o;if(0!==b.length){for(var d,e,f=a.m,h=0;h<b.length;h+=3)d=b[h],e=b[h+c],d?ic(c,d,e,f):e(f);a.V.length=0}}function lc(){this.error=null}function ic(a,b,c,d){var e="function"===typeof c;if(e){try{var f=c(d)}catch(q){gb.error=
if(I(a))return a}function bb(a,b){if(a&&b)for(var c=Object.getOwnPropertyNames(b),d=0,e;d<c.length&&(e=c[d]);d++){var f=Object.getOwnPropertyDescriptor(b,e);f&&Object.defineProperty(a,e,f)}}function cb(a,b){for(var c=[],d=1;d<arguments.length;++d)c[d-1]=arguments[d];for(d=0;d<c.length;d++)bb(a,c[d]);return a}function ke(a,b){for(var c in b)a[c]=b[c]}function jc(a){db.push(a);eb.textContent=kc++}function lc(a){fb||(fb=!0,jc(va));ja.push(a)}function va(){fb=!1;for(var a=!!ja.length;ja.length;)ja.shift()(); q,f=gb}if(f===gb){var h=!0;var k=f.error;f.error=null}else var m=!0;if(b===f){C(b,new TypeError("A promises callback cannot return that same promise."));return}}else f=d,m=!0;void 0===b.o&&(e&&m?ja(b,f):h?C(b,k):1===a?L(b,f):2===a&&C(b,f))}function ue(a,b){try{b(function(b){ja(a,b)},function(b){C(a,b)})}catch(c){C(a,c)}}function hc(a){a[ya]=mc++;a.o=void 0;a.m=void 0;a.V=[]}function la(a,b){this.eb=a;this.L=new a(ba);this.L[ya]||hc(this.L);nc(b)?(this.aa=this.length=b.length,this.m=Array(this.length),
return a}function le(a,b){var c=b.getRootNode();return a.map(function(a){var b=c===a.target.getRootNode();if(b&&a.addedNodes){if(b=Array.from(a.addedNodes).filter(function(a){return c===a.getRootNode()}),b.length)return a=Object.create(a),Object.defineProperty(a,"addedNodes",{value:b,configurable:!0}),a}else if(b)return a}).filter(function(a){return a})}function mc(a){switch(a){case "&":return"&amp;";case "<":return"&lt;";case ">":return"&gt;";case '"':return"&quot;";case "\u00a0":return"&nbsp;"}} 0===this.length?L(this.L,this.m):(this.length=this.length||0,this.cb(b),0===this.aa&&L(this.L,this.m))):C(this.L,Error("Array Methods must be provided an Array"))}function D(a){this[ya]=mc++;this.m=this.o=void 0;this.V=[];if(ba!==a){if("function"!==typeof a)throw new TypeError("You must pass a resolver function as the first argument to the promise constructor");if(this instanceof D)ue(this,a);else throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
function nc(a){for(var b={},c=0;c<a.length;c++)b[a[c]]=!0;return b}function gb(a,b){"template"===a.localName&&(a=a.content);for(var c="",d=b?b(a):a.childNodes,e=0,f=d.length,g;e<f&&(g=d[e]);e++){a:{var h=g;var m=a;var H=b;switch(h.nodeType){case Node.ELEMENT_NODE:for(var k=h.localName,l="<"+k,n=h.attributes,p=0;m=n[p];p++)l+=" "+m.name+'="'+m.value.replace(me,mc)+'"';l+=">";h=ne[k]?l:l+gb(h,H)+"</"+k+">";break a;case Node.TEXT_NODE:h=h.data;h=m&&oe[m.localName]?h:h.replace(pe,mc);break a;case Node.COMMENT_NODE:h= }}function ca(a){return a.__shady&&void 0!==a.__shady.firstChild}function J(a){return"ShadyRoot"===a.Wa}function ma(a){a=a.getRootNode();if(J(a))return a}function hb(a,b){if(a&&b)for(var c=Object.getOwnPropertyNames(b),d=0,e;d<c.length&&(e=c[d]);d++){var f=Object.getOwnPropertyDescriptor(b,e);f&&Object.defineProperty(a,e,f)}}function ib(a,b){for(var c=[],d=1;d<arguments.length;++d)c[d-1]=arguments[d];for(d=0;d<c.length;d++)hb(a,c[d]);return a}function ve(a,b){for(var c in b)a[c]=b[c]}function oc(a){jb.push(a);
"\x3c!--"+h.data+"--\x3e";break a;default:throw window.console.error(h),Error("not implemented");}}c+=h}return c}function aa(a){B.currentNode=a;return B.parentNode()}function Sa(a){B.currentNode=a;return B.firstChild()}function Ta(a){B.currentNode=a;return B.lastChild()}function oc(a){B.currentNode=a;return B.previousSibling()}function pc(a){B.currentNode=a;return B.nextSibling()}function U(a){var b=[];B.currentNode=a;for(a=B.firstChild();a;)b.push(a),a=B.nextSibling();return b}function qc(a){C.currentNode= kb.textContent=pc++}function qc(a,b){for(;b;){if(b==a)return!0;b=b.parentNode}return!1}function rc(a){lb||(lb=!0,oc(za));na.push(a)}function za(){lb=!1;for(var a=!!na.length;na.length;)na.shift()();return a}function we(a,b){var c=b.getRootNode();return a.map(function(a){var b=c===a.target.getRootNode();if(b&&a.addedNodes){if(b=Array.from(a.addedNodes).filter(function(a){return c===a.getRootNode()}),b.length)return a=Object.create(a),Object.defineProperty(a,"addedNodes",{value:b,configurable:!0}),
a;return C.parentNode()}function rc(a){C.currentNode=a;return C.firstChild()}function sc(a){C.currentNode=a;return C.lastChild()}function tc(a){C.currentNode=a;return C.previousSibling()}function uc(a){C.currentNode=a;return C.nextSibling()}function vc(a){var b=[];C.currentNode=a;for(a=C.firstChild();a;)b.push(a),a=C.nextSibling();return b}function wc(a){return gb(a,function(a){return U(a)})}function xc(a){switch(a.nodeType){case Node.ELEMENT_NODE:case Node.DOCUMENT_FRAGMENT_NODE:a=document.createTreeWalker(a, a}else if(b)return a}).filter(function(a){return a})}function sc(a){switch(a){case "&":return"&amp;";case "<":return"&lt;";case ">":return"&gt;";case '"':return"&quot;";case "\u00a0":return"&nbsp;"}}function tc(a){for(var b={},c=0;c<a.length;c++)b[a[c]]=!0;return b}function mb(a,b){"template"===a.localName&&(a=a.content);for(var c="",d=b?b(a):a.childNodes,e=0,f=d.length,h;e<f&&(h=d[e]);e++){a:{var k=h;var m=a;var g=b;switch(k.nodeType){case Node.ELEMENT_NODE:for(var n=k.localName,l="<"+n,t=k.attributes,
NodeFilter.SHOW_TEXT,null,!1);for(var b="",c;c=a.nextNode();)b+=c.nodeValue;return b;default:return a.nodeValue}}function M(a,b,c){for(var d in b){var e=Object.getOwnPropertyDescriptor(a,d);e&&e.configurable||!e&&c?Object.defineProperty(a,d,b[d]):c&&console.warn("Could not define",d,"on",a)}}function R(a){M(a,yc);M(a,hb);M(a,ib)}function zc(a,b,c){ac(a);c=c||null;a.__shady=a.__shady||{};b.__shady=b.__shady||{};c&&(c.__shady=c.__shady||{});a.__shady.previousSibling=c?c.__shady.previousSibling:b.lastChild; p=0;m=t[p];p++)l+=" "+m.name+'="'+m.value.replace(xe,sc)+'"';l+=">";k=ye[n]?l:l+mb(k,g)+"</"+n+">";break a;case Node.TEXT_NODE:k=k.data;k=m&&ze[m.localName]?k:k.replace(Ae,sc);break a;case Node.COMMENT_NODE:k="\x3c!--"+k.data+"--\x3e";break a;default:throw window.console.error(k),Error("not implemented");}}c+=k}return c}function da(a){E.currentNode=a;return E.parentNode()}function Ya(a){E.currentNode=a;return E.firstChild()}function Za(a){E.currentNode=a;return E.lastChild()}function uc(a){E.currentNode=
var d=a.__shady.previousSibling;d&&d.__shady&&(d.__shady.nextSibling=a);(d=a.__shady.nextSibling=c)&&d.__shady&&(d.__shady.previousSibling=a);a.__shady.parentNode=b;c?c===b.__shady.firstChild&&(b.__shady.firstChild=a):(b.__shady.lastChild=a,b.__shady.firstChild||(b.__shady.firstChild=a));b.__shady.childNodes=null}function jb(a,b,c){if(b===a)throw Error("Failed to execute 'appendChild' on 'Node': The new child element contains the parent.");if(c){var d=c.__shady&&c.__shady.parentNode;if(void 0!==d&& a;return E.previousSibling()}function vc(a){E.currentNode=a;return E.nextSibling()}function X(a){var b=[];E.currentNode=a;for(a=E.firstChild();a;)b.push(a),a=E.nextSibling();return b}function wc(a){F.currentNode=a;return F.parentNode()}function xc(a){F.currentNode=a;return F.firstChild()}function yc(a){F.currentNode=a;return F.lastChild()}function zc(a){F.currentNode=a;return F.previousSibling()}function Ac(a){F.currentNode=a;return F.nextSibling()}function Bc(a){var b=[];F.currentNode=a;for(a=F.firstChild();a;)b.push(a),
d!==a||void 0===d&&aa(c)!==a)throw Error("Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.");}if(c===b)return b;b.parentNode&&kb(b.parentNode,b);d=ia(a);var e;if(e=d)a:{if(!b.__noInsertionPoint){var f;"slot"===b.localName?f=[b]:b.querySelectorAll&&(f=b.querySelectorAll("slot"));if(f&&f.length){e=f;break a}}e=void 0}f=e;d&&("slot"===a.localName||f)&&d.M();if(Z(a)){e=c;$b(a);a.__shady=a.__shady||{};void 0!==a.__shady.firstChild&& a=F.nextSibling();return b}function Cc(a){return mb(a,function(a){return X(a)})}function Dc(a){switch(a.nodeType){case Node.ELEMENT_NODE:case Node.DOCUMENT_FRAGMENT_NODE:a=document.createTreeWalker(a,NodeFilter.SHOW_TEXT,null,!1);for(var b="",c;c=a.nextNode();)b+=c.nodeValue;return b;default:return a.nodeValue}}function O(a,b,c){for(var d in b){var e=Object.getOwnPropertyDescriptor(a,d);e&&e.configurable||!e&&c?Object.defineProperty(a,d,b[d]):c&&console.warn("Could not define",d,"on",a)}}function U(a){O(a,
(a.__shady.childNodes=null);if(b.nodeType===Node.DOCUMENT_FRAGMENT_NODE){for(var g=b.childNodes,h=0;h<g.length;h++)zc(g[h],a,e);b.__shady=b.__shady||{};e=void 0!==b.__shady.firstChild?null:void 0;b.__shady.firstChild=b.__shady.lastChild=e;b.__shady.childNodes=e}else zc(b,a,e);if(lb(a)){a.__shady.root.M();var m=!0}else a.__shady.root&&(m=!0)}m||(m=I(a)?a.host:a,c?(c=Ac(c),mb.call(m,b,c)):Bc.call(m,b));Cc(a,b);f&&d.$a(f);return b}function kb(a,b){if(b.parentNode!==a)throw Error("The node to be removed is not a child of this node: "+ Ec);O(a,nb);O(a,ob)}function Fc(a,b,c){fc(a);c=c||null;a.__shady=a.__shady||{};b.__shady=b.__shady||{};c&&(c.__shady=c.__shady||{});a.__shady.previousSibling=c?c.__shady.previousSibling:b.lastChild;var d=a.__shady.previousSibling;d&&d.__shady&&(d.__shady.nextSibling=a);(d=a.__shady.nextSibling=c)&&d.__shady&&(d.__shady.previousSibling=a);a.__shady.parentNode=b;c?c===b.__shady.firstChild&&(b.__shady.firstChild=a):(b.__shady.lastChild=a,b.__shady.firstChild||(b.__shady.firstChild=a));b.__shady.childNodes=
b);var c=ia(b);if(Z(a)){b.__shady=b.__shady||{};a.__shady=a.__shady||{};b===a.__shady.firstChild&&(a.__shady.firstChild=b.__shady.nextSibling);b===a.__shady.lastChild&&(a.__shady.lastChild=b.__shady.previousSibling);var d=b.__shady.previousSibling;var e=b.__shady.nextSibling;d&&(d.__shady=d.__shady||{},d.__shady.nextSibling=e);e&&(e.__shady=e.__shady||{},e.__shady.previousSibling=d);b.__shady.parentNode=b.__shady.previousSibling=b.__shady.nextSibling=void 0;void 0!==a.__shady.childNodes&&(a.__shady.childNodes= null}function pb(a,b,c){if(b===a)throw Error("Failed to execute 'appendChild' on 'Node': The new child element contains the parent.");if(c){var d=c.__shady&&c.__shady.parentNode;if(void 0!==d&&d!==a||void 0===d&&da(c)!==a)throw Error("Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.");}if(c===b)return b;b.parentNode&&qb(b.parentNode,b);d=ma(a);var e;if(e=d)a:{if(!b.__noInsertionPoint){var f;"slot"===b.localName?f=[b]:b.querySelectorAll&&
null);if(lb(a)){a.__shady.root.M();var f=!0}}Dc(b);c&&((e=a&&"slot"===a.localName)&&(f=!0),((d=c.gb(b))||e)&&c.M());f||(f=I(a)?a.host:a,(!a.__shady.root&&"slot"!==b.localName||f===aa(b))&&ka.call(f,b));Cc(a,null,b);return b}function Dc(a){if(a.__shady&&void 0!==a.__shady.sa)for(var b=a.childNodes,c=0,d=b.length,e;c<d&&(e=b[c]);c++)Dc(e);a.__shady&&(a.__shady.sa=void 0)}function Ac(a){var b=a;a&&"slot"===a.localName&&(b=(b=a.__shady&&a.__shady.V)&&b.length?b[0]:Ac(a.nextSibling));return b}function lb(a){return(a= (f=b.querySelectorAll("slot"));if(f&&f.length){e=f;break a}}e=void 0}(f=e)&&d.$a(f);d&&("slot"===a.localName||f)&&d.O();if(ca(a)){d=c;ec(a);a.__shady=a.__shady||{};void 0!==a.__shady.firstChild&&(a.__shady.childNodes=null);if(b.nodeType===Node.DOCUMENT_FRAGMENT_NODE){f=b.childNodes;for(e=0;e<f.length;e++)Fc(f[e],a,d);b.__shady=b.__shady||{};d=void 0!==b.__shady.firstChild?null:void 0;b.__shady.firstChild=b.__shady.lastChild=d;b.__shady.childNodes=d}else Fc(b,a,d);if(rb(a)){a.__shady.root.O();var h=
a&&a.__shady&&a.__shady.root)&&a.Aa()}function Ec(a,b){"slot"===b?(a=a.parentNode,lb(a)&&a.__shady.root.M()):"slot"===a.localName&&"name"===b&&(b=ia(a))&&(b.ib(a),b.M())}function Cc(a,b,c){if(a=a.__shady&&a.__shady.X)b&&a.addedNodes.push(b),c&&a.removedNodes.push(c),a.vb()}function Fc(a){if(a&&a.nodeType){a.__shady=a.__shady||{};var b=a.__shady.sa;void 0===b&&(I(a)?b=a:b=(b=a.parentNode)?Fc(b):a,document.documentElement.contains(a)&&(a.__shady.sa=b));return b}}function wa(a,b,c){var d=[];Gc(a.childNodes, !0}else a.__shady.root&&(h=!0)}h||(h=J(a)?a.host:a,c?(c=Gc(c),sb.call(h,b,c)):Hc.call(h,b));Ic(a,b);return b}function qb(a,b){if(b.parentNode!==a)throw Error("The node to be removed is not a child of this node: "+b);var c=ma(b);if(ca(a)){b.__shady=b.__shady||{};a.__shady=a.__shady||{};b===a.__shady.firstChild&&(a.__shady.firstChild=b.__shady.nextSibling);b===a.__shady.lastChild&&(a.__shady.lastChild=b.__shady.previousSibling);var d=b.__shady.previousSibling;var e=b.__shady.nextSibling;d&&(d.__shady=
b,c,d);return d}function Gc(a,b,c,d){for(var e=0,f=a.length,g;e<f&&(g=a[e]);e++){var h;if(h=g.nodeType===Node.ELEMENT_NODE){h=g;var m=b,H=c,k=d,l=m(h);l&&k.push(h);H&&H(l)?h=l:(Gc(h.childNodes,m,H,k),h=void 0)}if(h)break}}function Hc(a){a=a.getRootNode();I(a)&&a.Da()}function Ic(a,b,c){xa||(xa=window.ShadyCSS&&window.ShadyCSS.ScopingShim);xa&&"class"===b?xa.setElementClass(a,c):(Jc.call(a,b,c),Ec(a,b))}function Kc(a,b){if(a.ownerDocument!==document)return nb.call(document,a,b);var c=nb.call(document, d.__shady||{},d.__shady.nextSibling=e);e&&(e.__shady=e.__shady||{},e.__shady.previousSibling=d);b.__shady.parentNode=b.__shady.previousSibling=b.__shady.nextSibling=void 0;void 0!==a.__shady.childNodes&&(a.__shady.childNodes=null);if(rb(a)){a.__shady.root.O();var f=!0}}Jc(b);c&&((e=a&&"slot"===a.localName)&&(f=!0),((d=c.gb(b))||e)&&c.O());f||(f=J(a)?a.host:a,(!a.__shady.root&&"slot"!==b.localName||f===da(b))&&oa.call(f,b));Ic(a,null,b);return b}function Jc(a){if(a.__shady&&void 0!==a.__shady.va)for(var b=
a,!1);if(b){a=a.childNodes;b=0;for(var d;b<a.length;b++)d=Kc(a[b],!0),c.appendChild(d)}return c}function ob(a,b){var c=[],d=a;for(a=a===window?window:a.getRootNode();d;)c.push(d),d=d.assignedSlot?d.assignedSlot:d.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&d.host&&(b||d!==a)?d.host:d.parentNode;c[c.length-1]===document&&c.push(window);return c}function Lc(a,b){if(!I)return a;a=ob(a,!0);for(var c=0,d,e,f,g;c<b.length;c++)if(d=b[c],f=d===window?window:d.getRootNode(),f!==e&&(g=a.indexOf(f),e=f),!I(f)|| a.childNodes,c=0,d=b.length,e;c<d&&(e=b[c]);c++)Jc(e);a.__shady&&(a.__shady.va=void 0)}function Gc(a){var b=a;a&&"slot"===a.localName&&(b=(b=a.__shady&&a.__shady.W)&&b.length?b[0]:Gc(a.nextSibling));return b}function rb(a){return(a=a&&a.__shady&&a.__shady.root)&&a.Ca()}function Kc(a,b){"slot"===b?(a=a.parentNode,rb(a)&&a.__shady.root.O()):"slot"===a.localName&&"name"===b&&(b=ma(a))&&(b.ib(a),b.O())}function Ic(a,b,c){if(a=a.__shady&&a.__shady.Y)b&&a.addedNodes.push(b),c&&a.removedNodes.push(c),a.ub()}
-1<g)return d}function pb(a){function b(b,d){b=new a(b,d);b.ia=d&&!!d.composed;return b}ke(b,a);b.prototype=a.prototype;return b}function Mc(a,b,c){if(c=b.__handlers&&b.__handlers[a.type]&&b.__handlers[a.type][c])for(var d=0,e;(e=c[d])&&a.target!==a.relatedTarget&&(e.call(b,a),!a.Ua);d++);}function qe(a){var b=a.composedPath();Object.defineProperty(a,"currentTarget",{get:function(){return d},configurable:!0});for(var c=b.length-1;0<=c;c--){var d=b[c];Mc(a,d,"capture");if(a.ja)return}Object.defineProperty(a, function Lc(a){if(a&&a.nodeType){a.__shady=a.__shady||{};var b=a.__shady.va;void 0===b&&(J(a)?b=a:b=(b=a.parentNode)?Lc(b):a,pa.call(document.documentElement,a)&&(a.__shady.va=b));return b}}function Aa(a,b,c){var d=[];Mc(a.childNodes,b,c,d);return d}function Mc(a,b,c,d){for(var e=0,f=a.length,h;e<f&&(h=a[e]);e++){var k;if(k=h.nodeType===Node.ELEMENT_NODE){k=h;var m=b,g=c,n=d,l=m(k);l&&n.push(k);g&&g(l)?k=l:(Mc(k.childNodes,m,g,n),k=void 0)}if(k)break}}function Nc(a){a=a.getRootNode();J(a)&&a.Fa()}
"eventPhase",{get:function(){return Event.AT_TARGET}});var e;for(c=0;c<b.length;c++){d=b[c];var f=d.__shady&&d.__shady.root;if(0===c||f&&f===e)if(Mc(a,d,"bubble"),d!==window&&(e=d.getRootNode()),a.ja)break}}function Nc(a,b,c,d,e,f){for(var g=0;g<a.length;g++){var h=a[g],m=h.type,H=h.capture,k=h.once,l=h.passive;if(b===h.node&&c===m&&d===H&&e===k&&f===l)return g}return-1}function Oc(a,b,c){if(b){if("object"===typeof c){var d=!!c.capture;var e=!!c.once;var f=!!c.passive}else d=!!c,f=e=!1;var g=c&&c.ka|| function Oc(a,b,c){Ba||(Ba=window.ShadyCSS&&window.ShadyCSS.ScopingShim);Ba&&"class"===b?Ba.setElementClass(a,c):(Pc.call(a,b,c),Kc(a,b))}function Qc(a,b){if(a.ownerDocument!==document)return tb.call(document,a,b);var c=tb.call(document,a,!1);if(b){a=a.childNodes;b=0;for(var d;b<a.length;b++)d=Qc(a[b],!0),c.appendChild(d)}return c}function ub(a,b){var c=[],d=a;for(a=a===window?window:a.getRootNode();d;)c.push(d),d=d.assignedSlot?d.assignedSlot:d.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&d.host&&(b||
this,h=b[la];if(h){if(-1<Nc(h,g,a,d,e,f))return}else b[la]=[];h=function(d){e&&this.removeEventListener(a,b,c);d.__target||Pc(d);if(g!==this){var f=Object.getOwnPropertyDescriptor(d,"currentTarget");Object.defineProperty(d,"currentTarget",{get:function(){return g},configurable:!0})}if(d.composed||-1<d.composedPath().indexOf(g))if(d.target===d.relatedTarget)d.eventPhase===Event.BUBBLING_PHASE&&d.stopImmediatePropagation();else if(d.eventPhase===Event.CAPTURING_PHASE||d.bubbles||d.target===g){var h= d!==a)?d.host:d.parentNode;c[c.length-1]===document&&c.push(window);return c}function Rc(a,b){if(!J)return a;a=ub(a,!0);for(var c=0,d,e,f,h;c<b.length;c++)if(d=b[c],f=d===window?window:d.getRootNode(),f!==e&&(h=a.indexOf(f),e=f),!J(f)||-1<h)return d}function vb(a){function b(b,d){b=new a(b,d);b.ka=d&&!!d.composed;return b}ve(b,a);b.prototype=a.prototype;return b}function Sc(a,b,c){if(c=b.__handlers&&b.__handlers[a.type]&&b.__handlers[a.type][c])for(var d=0,e;(e=c[d])&&a.target!==a.relatedTarget&&
"object"===typeof b&&b.handleEvent?b.handleEvent(d):b.call(g,d);g!==this&&(f?(Object.defineProperty(d,"currentTarget",f),f=null):delete d.currentTarget);return h}};b[la].push({node:this,type:a,capture:d,once:e,passive:f,zb:h});qb[a]?(this.__handlers=this.__handlers||{},this.__handlers[a]=this.__handlers[a]||{capture:[],bubble:[]},this.__handlers[a][d?"capture":"bubble"].push(h)):(this instanceof Window?Qc:Rc).call(this,a,h,c)}}function Sc(a,b,c){if(b){if("object"===typeof c){var d=!!c.capture;var e= (e.call(b,a),!a.Ua);d++);}function Be(a){var b=a.composedPath();Object.defineProperty(a,"currentTarget",{get:function(){return d},configurable:!0});for(var c=b.length-1;0<=c;c--){var d=b[c];Sc(a,d,"capture");if(a.la)return}Object.defineProperty(a,"eventPhase",{get:function(){return Event.AT_TARGET}});var e;for(c=0;c<b.length;c++){d=b[c];var f=d.__shady&&d.__shady.root;if(0===c||f&&f===e)if(Sc(a,d,"bubble"),d!==window&&(e=d.getRootNode()),a.la)break}}function Tc(a,b,c,d,e,f){for(var h=0;h<a.length;h++){var k=
!!c.once;var f=!!c.passive}else d=!!c,f=e=!1;var g=c&&c.ka||this,h=void 0;var m=null;try{m=b[la]}catch(H){}m&&(e=Nc(m,g,a,d,e,f),-1<e&&(h=m.splice(e,1)[0].zb,m.length||(b[la]=void 0)));(this instanceof Window?Tc:Uc).call(this,a,h||b,c);h&&qb[a]&&this.__handlers&&this.__handlers[a]&&(a=this.__handlers[a][d?"capture":"bubble"],h=a.indexOf(h),-1<h&&a.splice(h,1))}}function re(){for(var a in qb)window.addEventListener(a,function(a){a.__target||(Pc(a),qe(a))},!0)}function Pc(a){a.__target=a.target;a.ya= a[h],m=k.type,g=k.capture,n=k.once,l=k.passive;if(b===k.node&&c===m&&d===g&&e===n&&f===l)return h}return-1}function Uc(a,b,c){if(b){if("object"===typeof c){var d=!!c.capture;var e=!!c.once;var f=!!c.passive}else d=!!c,f=e=!1;var h=c&&c.ma||this,k=b[qa];if(k){if(-1<Tc(k,h,a,d,e,f))return}else b[qa]=[];k=function(d){e&&this.removeEventListener(a,b,c);d.__target||Vc(d);if(h!==this){var f=Object.getOwnPropertyDescriptor(d,"currentTarget");Object.defineProperty(d,"currentTarget",{get:function(){return h},
a.relatedTarget;if(D.W){var b=Vc,c=Object.getPrototypeOf(a);if(!c.hasOwnProperty("__patchProto")){var d=Object.create(c);d.Bb=c;bb(d,b);c.__patchProto=d}a.__proto__=c.__patchProto}else bb(a,Vc)}function ma(a,b){return{index:a,Y:[],aa:b}}function se(a,b,c,d){var e=0,f=0,g=0,h=0,m=Math.min(b-e,d-f);if(0==e&&0==f)a:{for(g=0;g<m;g++)if(a[g]!==c[g])break a;g=m}if(b==a.length&&d==c.length){h=a.length;for(var k=c.length,l=0;l<m-g&&te(a[--h],c[--k]);)l++;h=l}e+=g;f+=g;b-=h;d-=h;if(0==b-e&&0==d-f)return[]; configurable:!0})}if(d.composed||-1<d.composedPath().indexOf(h))if(d.target===d.relatedTarget)d.eventPhase===Event.BUBBLING_PHASE&&d.stopImmediatePropagation();else if(d.eventPhase===Event.CAPTURING_PHASE||d.bubbles||d.target===h){var k="object"===typeof b&&b.handleEvent?b.handleEvent(d):b.call(h,d);h!==this&&(f?(Object.defineProperty(d,"currentTarget",f),f=null):delete d.currentTarget);return k}};b[qa].push({node:this,type:a,capture:d,once:e,passive:f,yb:k});wb[a]?(this.__handlers=this.__handlers||
if(e==b){for(b=ma(e,0);f<d;)b.Y.push(c[f++]);return[b]}if(f==d)return[ma(e,b-e)];m=e;g=f;d=d-g+1;h=b-m+1;b=Array(d);for(k=0;k<d;k++)b[k]=Array(h),b[k][0]=k;for(k=0;k<h;k++)b[0][k]=k;for(k=1;k<d;k++)for(l=1;l<h;l++)if(a[m+l-1]===c[g+k-1])b[k][l]=b[k-1][l-1];else{var n=b[k-1][l]+1,p=b[k][l-1]+1;b[k][l]=n<p?n:p}m=b.length-1;g=b[0].length-1;d=b[m][g];for(a=[];0<m||0<g;)0==m?(a.push(2),g--):0==g?(a.push(3),m--):(h=b[m-1][g-1],k=b[m-1][g],l=b[m][g-1],n=k<l?k<h?k:h:l<h?l:h,n==h?(h==d?a.push(0):(a.push(1), {},this.__handlers[a]=this.__handlers[a]||{capture:[],bubble:[]},this.__handlers[a][d?"capture":"bubble"].push(k)):(this instanceof Window?Wc:Xc).call(this,a,k,c)}}function Yc(a,b,c){if(b){if("object"===typeof c){var d=!!c.capture;var e=!!c.once;var f=!!c.passive}else d=!!c,f=e=!1;var h=c&&c.ma||this,k=void 0;var g=null;try{g=b[qa]}catch(q){}g&&(e=Tc(g,h,a,d,e,f),-1<e&&(k=g.splice(e,1)[0].yb,g.length||(b[qa]=void 0)));(this instanceof Window?Zc:$c).call(this,a,k||b,c);k&&wb[a]&&this.__handlers&&this.__handlers[a]&&
d=h),m--,g--):n==k?(a.push(3),m--,d=k):(a.push(2),g--,d=l));a.reverse();b=void 0;m=[];for(g=0;g<a.length;g++)switch(a[g]){case 0:b&&(m.push(b),b=void 0);e++;f++;break;case 1:b||(b=ma(e,0));b.aa++;e++;b.Y.push(c[f]);f++;break;case 2:b||(b=ma(e,0));b.aa++;e++;break;case 3:b||(b=ma(e,0)),b.Y.push(c[f]),f++}b&&m.push(b);return m}function te(a,b){return a===b}function Wc(a){var b=[];do b.unshift(a);while(a=a.parentNode);return b}function Xc(a){Hc(a);return a.__shady&&a.__shady.assignedSlot||null}function N(a, (a=this.__handlers[a][d?"capture":"bubble"],k=a.indexOf(k),-1<k&&a.splice(k,1))}}function Ce(){for(var a in wb)window.addEventListener(a,function(a){a.__target||(Vc(a),Be(a))},!0)}function Vc(a){a.__target=a.target;a.Aa=a.relatedTarget;if(G.X){var b=ad,c=Object.getPrototypeOf(a);if(!c.hasOwnProperty("__patchProto")){var d=Object.create(c);d.Ab=c;hb(d,b);c.__patchProto=d}a.__proto__=c.__patchProto}else hb(a,ad)}function ra(a,b){return{index:a,Z:[],ba:b}}function De(a,b,c,d){var e=0,f=0,h=0,k=0,g=Math.min(b-
b){for(var c=Object.getOwnPropertyNames(b),d=0;d<c.length;d++){var e=c[d],f=Object.getOwnPropertyDescriptor(b,e);f.value?a[e]=f.value:Object.defineProperty(a,e,f)}}function ue(){var a=window.customElements&&window.customElements.nativeHTMLElement||HTMLElement;N(window.Node.prototype,ve);N(window.Window.prototype,we);N(window.Text.prototype,xe);N(window.DocumentFragment.prototype,rb);N(window.Element.prototype,Yc);N(window.Document.prototype,Zc);window.HTMLSlotElement&&N(window.HTMLSlotElement.prototype, e,d-f);if(0==e&&0==f)a:{for(h=0;h<g;h++)if(a[h]!==c[h])break a;h=g}if(b==a.length&&d==c.length){k=a.length;for(var q=c.length,n=0;n<g-h&&Ee(a[--k],c[--q]);)n++;k=n}e+=h;f+=h;b-=k;d-=k;if(0==b-e&&0==d-f)return[];if(e==b){for(b=ra(e,0);f<d;)b.Z.push(c[f++]);return[b]}if(f==d)return[ra(e,b-e)];g=e;h=f;d=d-h+1;k=b-g+1;b=Array(d);for(q=0;q<d;q++)b[q]=Array(k),b[q][0]=q;for(q=0;q<k;q++)b[0][q]=q;for(q=1;q<d;q++)for(n=1;n<k;n++)if(a[g+n-1]===c[h+q-1])b[q][n]=b[q-1][n-1];else{var l=b[q-1][n]+1,p=b[q][n-1]+
$c);N(a.prototype,ye);D.W&&(R(window.Node.prototype),R(window.Text.prototype),R(window.DocumentFragment.prototype),R(window.Element.prototype),R(a.prototype),R(window.Document.prototype),window.HTMLSlotElement&&R(window.HTMLSlotElement.prototype))}function ad(a){var b=ze.has(a);a=/^[a-z][.0-9_a-z]*-[\-.0-9_a-z]*$/.test(a);return!b&&a}function n(a){var b=a.isConnected;if(void 0!==b)return b;for(;a&&!(a.__CE_isImportDocument||a instanceof Document);)a=a.parentNode||(window.ShadowRoot&&a instanceof ShadowRoot? 1;b[q][n]=l<p?l:p}g=b.length-1;h=b[0].length-1;d=b[g][h];for(a=[];0<g||0<h;)0==g?(a.push(2),h--):0==h?(a.push(3),g--):(k=b[g-1][h-1],q=b[g-1][h],n=b[g][h-1],l=q<n?q<k?q:k:n<k?n:k,l==k?(k==d?a.push(0):(a.push(1),d=k),g--,h--):l==q?(a.push(3),g--,d=q):(a.push(2),h--,d=n));a.reverse();b=void 0;g=[];for(h=0;h<a.length;h++)switch(a[h]){case 0:b&&(g.push(b),b=void 0);e++;f++;break;case 1:b||(b=ra(e,0));b.ba++;e++;b.Z.push(c[f]);f++;break;case 2:b||(b=ra(e,0));b.ba++;e++;break;case 3:b||(b=ra(e,0)),b.Z.push(c[f]),
a.host:void 0);return!(!a||!(a.__CE_isImportDocument||a instanceof Document))}function sb(a,b){for(;b&&b!==a&&!b.nextSibling;)b=b.parentNode;return b&&b!==a?b.nextSibling:null}function P(a,b,c){c=c?c:new Set;for(var d=a;d;){if(d.nodeType===Node.ELEMENT_NODE){var e=d;b(e);var f=e.localName;if("link"===f&&"import"===e.getAttribute("rel")){d=e.import;if(d instanceof Node&&!c.has(d))for(c.add(d),d=d.firstChild;d;d=d.nextSibling)P(d,b,c);d=sb(a,e);continue}else if("template"===f){d=sb(a,e);continue}if(e= f++}b&&g.push(b);return g}function Ee(a,b){return a===b}function bd(a){var b=[];do b.unshift(a);while(a=a.parentNode);return b}function cd(a){Nc(a);return a.__shady&&a.__shady.assignedSlot||null}function P(a,b){for(var c=Object.getOwnPropertyNames(b),d=0;d<c.length;d++){var e=c[d],f=Object.getOwnPropertyDescriptor(b,e);f.value?a[e]=f.value:Object.defineProperty(a,e,f)}}function Fe(){var a=window.customElements&&window.customElements.nativeHTMLElement||HTMLElement;P(window.Node.prototype,Ge);P(window.Window.prototype,
e.__CE_shadowRoot)for(e=e.firstChild;e;e=e.nextSibling)P(e,b,c)}d=d.firstChild?d.firstChild:sb(a,d)}}function u(a,b,c){a[b]=c}function tb(a){a=a.replace(G.mb,"").replace(G.port,"");var b=bd,c=a,d=new Ka;d.start=0;d.end=c.length;for(var e=d,f=0,g=c.length;f<g;f++)if("{"===c[f]){e.rules||(e.rules=[]);var h=e,m=h.rules[h.rules.length-1]||null;e=new Ka;e.start=f+1;e.parent=h;e.previous=m;h.rules.push(e)}else"}"===c[f]&&(e.end=f+1,e=e.parent||d);return b(d,a)}function bd(a,b){var c=b.substring(a.start, He);P(window.Text.prototype,Ie);P(window.DocumentFragment.prototype,xb);P(window.Element.prototype,dd);P(window.Document.prototype,ed);window.HTMLSlotElement&&P(window.HTMLSlotElement.prototype,fd);P(a.prototype,Je);G.X&&(U(window.Node.prototype),U(window.Text.prototype),U(window.DocumentFragment.prototype),U(window.Element.prototype),U(a.prototype),U(window.Document.prototype),window.HTMLSlotElement&&U(window.HTMLSlotElement.prototype))}function gd(a){var b=Ke.has(a);a=/^[a-z][.0-9_a-z]*-[\-.0-9_a-z]*$/.test(a);
a.end-1);a.parsedCssText=a.cssText=c.trim();a.parent&&(c=b.substring(a.previous?a.previous.end:a.parent.start,a.start-1),c=Ae(c),c=c.replace(G.Ja," "),c=c.substring(c.lastIndexOf(";")+1),c=a.parsedSelector=a.selector=c.trim(),a.atRule=0===c.indexOf("@"),a.atRule?0===c.indexOf("@media")?a.type=L.MEDIA_RULE:c.match(G.rb)&&(a.type=L.ha,a.keyframesName=a.selector.split(G.Ja).pop()):a.type=0===c.indexOf("--")?L.ua:L.STYLE_RULE);if(c=a.rules)for(var d=0,e=c.length,f;d<e&&(f=c[d]);d++)bd(f,b);return a}function Ae(a){return a.replace(/\\([0-9a-f]{1,6})\s/gi, return!b&&a}function u(a){var b=a.isConnected;if(void 0!==b)return b;for(;a&&!(a.__CE_isImportDocument||a instanceof Document);)a=a.parentNode||(window.ShadowRoot&&a instanceof ShadowRoot?a.host:void 0);return!(!a||!(a.__CE_isImportDocument||a instanceof Document))}function yb(a,b){for(;b&&b!==a&&!b.nextSibling;)b=b.parentNode;return b&&b!==a?b.nextSibling:null}function S(a,b,c){c=c?c:new Set;for(var d=a;d;){if(d.nodeType===Node.ELEMENT_NODE){var e=d;b(e);var f=e.localName;if("link"===f&&"import"===
function(a,c){a=c;for(c=6-a.length;c--;)a="0"+a;return"\\"+a})}function cd(a,b,c){c=void 0===c?"":c;var d="";if(a.cssText||a.rules){var e=a.rules,f;if(f=e)f=e[0],f=!(f&&f.selector&&0===f.selector.indexOf("--"));if(f){f=0;for(var g=e.length,h;f<g&&(h=e[f]);f++)d=cd(h,b,d)}else b?b=a.cssText:(b=a.cssText,b=b.replace(G.Ea,"").replace(G.Ia,""),b=b.replace(G.sb,"").replace(G.xb,"")),(d=b.trim())&&(d=" "+d+"\n")}d&&(a.selector&&(c+=a.selector+" {\n"),c+=d,a.selector&&(c+="}\n\n"));return c}function dd(a){v= e.getAttribute("rel")){d=e.import;if(d instanceof Node&&!c.has(d))for(c.add(d),d=d.firstChild;d;d=d.nextSibling)S(d,b,c);d=yb(a,e);continue}else if("template"===f){d=yb(a,e);continue}if(e=e.__CE_shadowRoot)for(e=e.firstChild;e;e=e.nextSibling)S(e,b,c)}d=d.firstChild?d.firstChild:yb(a,d)}}function x(a,b,c){a[b]=c}function zb(a){a=a.replace(I.lb,"").replace(I.port,"");var b=hd,c=a,d=new Pa;d.start=0;d.end=c.length;for(var e=d,f=0,h=c.length;f<h;f++)if("{"===c[f]){e.rules||(e.rules=[]);var k=e,g=k.rules[k.rules.length-
a&&a.shimcssproperties?!1:q||!(navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/)||!window.CSS||!CSS.supports||!CSS.supports("box-shadow","0 0 0 var(--foo)"))}function ba(a,b){if(!a)return"";"string"===typeof a&&(a=tb(a));b&&ca(a,b);return cd(a,v)}function ya(a){!a.__cssRules&&a.textContent&&(a.__cssRules=tb(a.textContent));return a.__cssRules||null}function ed(a){return!!a.parent&&a.parent.type===L.ha}function ca(a,b,c,d){if(a){var e=!1,f=a.type;if(d&&f===L.MEDIA_RULE){var g=a.selector.match(Be); 1]||null;e=new Pa;e.start=f+1;e.parent=k;e.previous=g;k.rules.push(e)}else"}"===c[f]&&(e.end=f+1,e=e.parent||d);return b(d,a)}function hd(a,b){var c=b.substring(a.start,a.end-1);a.parsedCssText=a.cssText=c.trim();a.parent&&(c=b.substring(a.previous?a.previous.end:a.parent.start,a.start-1),c=Le(c),c=c.replace(I.La," "),c=c.substring(c.lastIndexOf(";")+1),c=a.parsedSelector=a.selector=c.trim(),a.atRule=0===c.indexOf("@"),a.atRule?0===c.indexOf("@media")?a.type=M.MEDIA_RULE:c.match(I.qb)&&(a.type=M.ja,
g&&(window.matchMedia(g[1]).matches||(e=!0))}f===L.STYLE_RULE?b(a):c&&f===L.ha?c(a):f===L.ua&&(e=!0);if((a=a.rules)&&!e){e=0;f=a.length;for(var h;e<f&&(h=a[e]);e++)ca(h,b,c,d)}}}function ub(a,b,c,d){var e=document.createElement("style");b&&e.setAttribute("scope",b);e.textContent=a;fd(e,c,d);return e}function fd(a,b,c){b=b||document.head;b.insertBefore(a,c&&c.nextSibling||b.firstChild);S?a.compareDocumentPosition(S)===Node.DOCUMENT_POSITION_PRECEDING&&(S=a):S=a}function gd(a,b){var c=a.indexOf("var("); a.keyframesName=a.selector.split(I.La).pop()):a.type=0===c.indexOf("--")?M.wa:M.STYLE_RULE);if(c=a.rules)for(var d=0,e=c.length,f;d<e&&(f=c[d]);d++)hd(f,b);return a}function Le(a){return a.replace(/\\([0-9a-f]{1,6})\s/gi,function(a,c){a=c;for(c=6-a.length;c--;)a="0"+a;return"\\"+a})}function id(a,b,c){c=void 0===c?"":c;var d="";if(a.cssText||a.rules){var e=a.rules,f;if(f=e)f=e[0],f=!(f&&f.selector&&0===f.selector.indexOf("--"));if(f){f=0;for(var h=e.length,k;f<h&&(k=e[f]);f++)d=id(k,b,d)}else b?b=
if(-1===c)return b(a,"","","");a:{var d=0;var e=c+3;for(var f=a.length;e<f;e++)if("("===a[e])d++;else if(")"===a[e]&&0===--d)break a;e=-1}d=a.substring(c+4,e);c=a.substring(0,c);a=gd(a.substring(e+1),b);e=d.indexOf(",");return-1===e?b(c,d.trim(),"",a):b(c,d.substring(0,e).trim(),d.substring(e+1).trim(),a)}function za(a,b){q?a.setAttribute("class",b):window.ShadyDOM.nativeMethods.setAttribute.call(a,"class",b)}function T(a){var b=a.localName,c="";b?-1<b.indexOf("-")||(c=b,b=a.getAttribute&&a.getAttribute("is")|| a.cssText:(b=a.cssText,b=b.replace(I.Ga,"").replace(I.Ka,""),b=b.replace(I.rb,"").replace(I.wb,"")),(d=b.trim())&&(d=" "+d+"\n")}d&&(a.selector&&(c+=a.selector+" {\n"),c+=d,a.selector&&(c+="}\n\n"));return c}function jd(a){A=a&&a.shimcssproperties?!1:z||!(navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/)||!window.CSS||!CSS.supports||!CSS.supports("box-shadow","0 0 0 var(--foo)"))}function ea(a,b){if(!a)return"";"string"===typeof a&&(a=zb(a));b&&fa(a,b);return id(a,A)}function Ca(a){!a.__cssRules&&
""):(b=a.is,c=a.extends);return{is:b,Z:c}}function hd(a){for(var b=0;b<a.length;b++){var c=a[b];if(c.target!==document.documentElement&&c.target!==document.head)for(var d=0;d<c.addedNodes.length;d++){var e=c.addedNodes[d];if(e.nodeType===Node.ELEMENT_NODE){var f=e.getRootNode();var g=e;var h=[];g.classList?h=Array.from(g.classList):g instanceof window.SVGElement&&g.hasAttribute("class")&&(h=g.getAttribute("class").split(/\s+/));g=h;h=g.indexOf(w.c);(g=-1<h?g[h+1]:"")&&f===e.ownerDocument?w.a(e,g, a.textContent&&(a.__cssRules=zb(a.textContent));return a.__cssRules||null}function kd(a){return!!a.parent&&a.parent.type===M.ja}function fa(a,b,c,d){if(a){var e=!1,f=a.type;if(d&&f===M.MEDIA_RULE){var h=a.selector.match(Me);h&&(window.matchMedia(h[1]).matches||(e=!0))}f===M.STYLE_RULE?b(a):c&&f===M.ja?c(a):f===M.wa&&(e=!0);if((a=a.rules)&&!e){e=0;f=a.length;for(var k;e<f&&(k=a[e]);e++)fa(k,b,c,d)}}}function Ab(a,b,c,d){var e=document.createElement("style");b&&e.setAttribute("scope",b);e.textContent=
!0):f.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&(f=f.host)&&(f=T(f).is,g!==f&&(g&&w.a(e,g,!0),w.a(e,f)))}}}}function Ce(a){if(a=Aa[a])a._applyShimCurrentVersion=a._applyShimCurrentVersion||0,a._applyShimValidatingVersion=a._applyShimValidatingVersion||0,a._applyShimNextVersion=(a._applyShimNextVersion||0)+1}function id(a){return a._applyShimCurrentVersion===a._applyShimNextVersion}function De(a){a._applyShimValidatingVersion=a._applyShimNextVersion;a.b||(a.b=!0,Ee.then(function(){a._applyShimCurrentVersion= a;ld(e,c,d);return e}function ld(a,b,c){b=b||document.head;b.insertBefore(a,c&&c.nextSibling||b.firstChild);V?a.compareDocumentPosition(V)===Node.DOCUMENT_POSITION_PRECEDING&&(V=a):V=a}function md(a,b){var c=a.indexOf("var(");if(-1===c)return b(a,"","","");a:{var d=0;var e=c+3;for(var f=a.length;e<f;e++)if("("===a[e])d++;else if(")"===a[e]&&0===--d)break a;e=-1}d=a.substring(c+4,e);c=a.substring(0,c);a=md(a.substring(e+1),b);e=d.indexOf(",");return-1===e?b(c,d.trim(),"",a):b(c,d.substring(0,e).trim(),
a._applyShimNextVersion;a.b=!1}))}function Kb(a){requestAnimationFrame(function(){jd?jd(a):(vb||(vb=new Promise(function(a){wb=a}),"complete"===document.readyState?wb():document.addEventListener("readystatechange",function(){"complete"===document.readyState&&wb()})),vb.then(function(){a&&a()}))})}(function(){if(!function(){var a=document.createEvent("Event");a.initEvent("foo",!0,!0);a.preventDefault();return a.defaultPrevented}()){var a=Event.prototype.preventDefault;Event.prototype.preventDefault= d.substring(e+1).trim(),a)}function Da(a,b){z?a.setAttribute("class",b):window.ShadyDOM.nativeMethods.setAttribute.call(a,"class",b)}function W(a){var b=a.localName,c="";b?-1<b.indexOf("-")||(c=b,b=a.getAttribute&&a.getAttribute("is")||""):(b=a.is,c=a.extends);return{is:b,$:c}}function nd(a){for(var b=0;b<a.length;b++){var c=a[b];if(c.target!==document.documentElement&&c.target!==document.head)for(var d=0;d<c.addedNodes.length;d++){var e=c.addedNodes[d];if(e.nodeType===Node.ELEMENT_NODE){var f=e.getRootNode();
function(){this.cancelable&&(a.call(this),Object.defineProperty(this,"defaultPrevented",{get:function(){return!0},configurable:!0}))}}var b=/Trident/.test(navigator.userAgent);if(!window.CustomEvent||b&&"function"!==typeof window.CustomEvent)window.CustomEvent=function(a,b){b=b||{};var c=document.createEvent("CustomEvent");c.initCustomEvent(a,!!b.bubbles,!!b.cancelable,b.detail);return c},window.CustomEvent.prototype=window.Event.prototype;if(!window.Event||b&&"function"!==typeof window.Event){var c= var h=e;var k=[];h.classList?k=Array.from(h.classList):h instanceof window.SVGElement&&h.hasAttribute("class")&&(k=h.getAttribute("class").split(/\s+/));h=k;k=h.indexOf(r.a);if((h=-1<k?h[k+1]:"")&&f===e.ownerDocument)r.b(e,h,!0);else if(f.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&(f=f.host))if(f=W(f).is,h===f)for(e=window.ShadyDOM.nativeMethods.querySelectorAll.call(e,":not(."+r.a+")"),f=0;f<e.length;f++)r.h(e[f],h);else h&&r.b(e,h,!0),r.b(e,f)}}}}function Ne(a){if(a=Ea[a])a._applyShimCurrentVersion=
window.Event;window.Event=function(a,b){b=b||{};var c=document.createEvent("Event");c.initEvent(a,!!b.bubbles,!!b.cancelable);return c};if(c)for(var d in c)window.Event[d]=c[d];window.Event.prototype=c.prototype}if(!window.MouseEvent||b&&"function"!==typeof window.MouseEvent){b=window.MouseEvent;window.MouseEvent=function(a,b){b=b||{};var c=document.createEvent("MouseEvent");c.initMouseEvent(a,!!b.bubbles,!!b.cancelable,b.view||window,b.detail,b.screenX,b.screenY,b.clientX,b.clientY,b.ctrlKey,b.altKey, a._applyShimCurrentVersion||0,a._applyShimValidatingVersion=a._applyShimValidatingVersion||0,a._applyShimNextVersion=(a._applyShimNextVersion||0)+1}function od(a){return a._applyShimCurrentVersion===a._applyShimNextVersion}function Oe(a){a._applyShimValidatingVersion=a._applyShimNextVersion;a.b||(a.b=!0,Pe.then(function(){a._applyShimCurrentVersion=a._applyShimNextVersion;a.b=!1}))}function Rb(a){requestAnimationFrame(function(){pd?pd(a):(Bb||(Bb=new Promise(function(a){Cb=a}),"complete"===document.readyState?
b.shiftKey,b.metaKey,b.button,b.relatedTarget);return c};if(b)for(d in b)window.MouseEvent[d]=b[d];window.MouseEvent.prototype=b.prototype}Array.from||(Array.from=function(a){return[].slice.call(a)});Object.assign||(Object.assign=function(a,b){for(var c=[].slice.call(arguments,1),d=0,e;d<c.length;d++)if(e=c[d])for(var f=a,k=e,l=Object.getOwnPropertyNames(k),n=0;n<l.length;n++)e=l[n],f[e]=k[e];return a})})(window.WebComponents);(function(){function a(){}var b="undefined"===typeof HTMLTemplateElement; Cb():document.addEventListener("readystatechange",function(){"complete"===document.readyState&&Cb()})),Bb.then(function(){a&&a()}))})}(function(){if(!function(){var a=document.createEvent("Event");a.initEvent("foo",!0,!0);a.preventDefault();return a.defaultPrevented}()){var a=Event.prototype.preventDefault;Event.prototype.preventDefault=function(){this.cancelable&&(a.call(this),Object.defineProperty(this,"defaultPrevented",{get:function(){return!0},configurable:!0}))}}var b=/Trident/.test(navigator.userAgent);
/Trident/.test(navigator.userAgent)&&function(){var a=Document.prototype.importNode;Document.prototype.importNode=function(){var b=a.apply(this,arguments);if(b.nodeType===Node.DOCUMENT_FRAGMENT_NODE){var c=this.createDocumentFragment();c.appendChild(b);return c}return b}}();var c=Node.prototype.cloneNode,d=Document.prototype.createElement,e=Document.prototype.importNode,f=function(){if(!b){var a=document.createElement("template"),c=document.createElement("template");c.content.appendChild(document.createElement("div")); if(!window.CustomEvent||b&&"function"!==typeof window.CustomEvent)window.CustomEvent=function(a,b){b=b||{};var c=document.createEvent("CustomEvent");c.initCustomEvent(a,!!b.bubbles,!!b.cancelable,b.detail);return c},window.CustomEvent.prototype=window.Event.prototype;if(!window.Event||b&&"function"!==typeof window.Event){var c=window.Event;window.Event=function(a,b){b=b||{};var c=document.createEvent("Event");c.initEvent(a,!!b.bubbles,!!b.cancelable);return c};if(c)for(var d in c)window.Event[d]=
a.content.appendChild(c);a=a.cloneNode(!0);return 0===a.content.childNodes.length||0===a.content.firstChild.content.childNodes.length||!(document.createDocumentFragment().cloneNode()instanceof DocumentFragment)}}();if(b){var g=function(a){switch(a){case "&":return"&amp;";case "<":return"&lt;";case ">":return"&gt;";case "\u00a0":return"&nbsp;"}},h=function(b){Object.defineProperty(b,"innerHTML",{get:function(){for(var a="",b=this.content.firstChild;b;b=b.nextSibling)a+=b.outerHTML||b.data.replace(r, c[d];window.Event.prototype=c.prototype}if(!window.MouseEvent||b&&"function"!==typeof window.MouseEvent){b=window.MouseEvent;window.MouseEvent=function(a,b){b=b||{};var c=document.createEvent("MouseEvent");c.initMouseEvent(a,!!b.bubbles,!!b.cancelable,b.view||window,b.detail,b.screenX,b.screenY,b.clientX,b.clientY,b.ctrlKey,b.altKey,b.shiftKey,b.metaKey,b.button,b.relatedTarget);return c};if(b)for(d in b)window.MouseEvent[d]=b[d];window.MouseEvent.prototype=b.prototype}Array.from||(Array.from=function(a){return[].slice.call(a)});
g);return a},set:function(b){m.body.innerHTML=b;for(a.b(m);this.content.firstChild;)this.content.removeChild(this.content.firstChild);for(;m.body.firstChild;)this.content.appendChild(m.body.firstChild)},configurable:!0})},m=document.implementation.createHTMLDocument("template"),k=!0,l=document.createElement("style");l.textContent="template{display:none;}";var n=document.head;n.insertBefore(l,n.firstElementChild);a.prototype=Object.create(HTMLElement.prototype);var p=!document.createElement("div").hasOwnProperty("innerHTML"); Object.assign||(Object.assign=function(a,b){for(var c=[].slice.call(arguments,1),d=0,e;d<c.length;d++)if(e=c[d])for(var f=a,g=e,l=Object.getOwnPropertyNames(g),p=0;p<l.length;p++)e=l[p],f[e]=g[e];return a})})(window.WebComponents);(function(){function a(){}var b="undefined"===typeof HTMLTemplateElement;/Trident/.test(navigator.userAgent)&&function(){var a=Document.prototype.importNode;Document.prototype.importNode=function(){var b=a.apply(this,arguments);if(b.nodeType===Node.DOCUMENT_FRAGMENT_NODE){var c=
a.O=function(b){if(!b.content){b.content=m.createDocumentFragment();for(var c;c=b.firstChild;)b.content.appendChild(c);if(p)b.__proto__=a.prototype;else if(b.cloneNode=function(b){return a.a(this,b)},k)try{h(b)}catch(df){k=!1}a.b(b.content)}};h(a.prototype);a.b=function(b){b=b.querySelectorAll("template");for(var c=0,d=b.length,e;c<d&&(e=b[c]);c++)a.O(e)};document.addEventListener("DOMContentLoaded",function(){a.b(document)});Document.prototype.createElement=function(){var b=d.apply(this,arguments); this.createDocumentFragment();c.appendChild(b);return c}return b}}();var c=Node.prototype.cloneNode,d=Document.prototype.createElement,e=Document.prototype.importNode,f=function(){if(!b){var a=document.createElement("template"),c=document.createElement("template");c.content.appendChild(document.createElement("div"));a.content.appendChild(c);a=a.cloneNode(!0);return 0===a.content.childNodes.length||0===a.content.firstChild.content.childNodes.length||!(document.createDocumentFragment().cloneNode()instanceof
"template"===b.localName&&a.O(b);return b};var r=/[&\u00A0<>]/g}if(b||f)a.a=function(a,b){var d=c.call(a,!1);this.O&&this.O(d);b&&(d.content.appendChild(c.call(a.content,!0)),this.qa(d.content,a.content));return d},a.prototype.cloneNode=function(b){return a.a(this,b)},a.qa=function(a,b){if(b.querySelectorAll){b=b.querySelectorAll("template");a=a.querySelectorAll("template");for(var c=0,d=a.length,e,f;c<d;c++)f=b[c],e=a[c],this.O&&this.O(f),e.parentNode.replaceChild(f.cloneNode(!0),e)}},Node.prototype.cloneNode= DocumentFragment)}}();if(b){var h=function(a){switch(a){case "&":return"&amp;";case "<":return"&lt;";case ">":return"&gt;";case "\u00a0":return"&nbsp;"}},k=function(b){Object.defineProperty(b,"innerHTML",{get:function(){for(var a="",b=this.content.firstChild;b;b=b.nextSibling)a+=b.outerHTML||b.data.replace(u,h);return a},set:function(b){g.body.innerHTML=b;for(a.b(g);this.content.firstChild;)this.content.removeChild(this.content.firstChild);for(;g.body.firstChild;)this.content.appendChild(g.body.firstChild)},
function(b){if(this instanceof DocumentFragment)if(b)var d=this.ownerDocument.importNode(this,!0);else return this.ownerDocument.createDocumentFragment();else d=c.call(this,b);b&&a.qa(d,this);return d},Document.prototype.importNode=function(b,c){if("template"===b.localName)return a.a(b,c);var d=e.call(this,b,c);c&&a.qa(d,b);return d},f&&(window.HTMLTemplateElement.prototype.cloneNode=function(b){return a.a(this,b)});b&&(window.HTMLTemplateElement=a)})();var xb;Array.isArray?xb=Array.isArray:xb=function(a){return"[object Array]"=== configurable:!0})},g=document.implementation.createHTMLDocument("template"),q=!0,l=document.createElement("style");l.textContent="template{display:none;}";var p=document.head;p.insertBefore(l,p.firstElementChild);a.prototype=Object.create(HTMLElement.prototype);var t=!document.createElement("div").hasOwnProperty("innerHTML");a.R=function(b){if(!b.content){b.content=g.createDocumentFragment();for(var c;c=b.firstChild;)b.content.appendChild(c);if(t)b.__proto__=a.prototype;else if(b.cloneNode=function(b){return a.a(this,
Object.prototype.toString.call(a)};var ic=xb,W=0,Va,Ua,kd="undefined"!==typeof window?window:void 0,ld=kd||{},bc=ld.MutationObserver||ld.WebKitMutationObserver,Fe="undefined"!==typeof Uint8ClampedArray&&"undefined"!==typeof importScripts&&"undefined"!==typeof MessageChannel,V=Array(1E3);var $d="undefined"===typeof self&&"undefined"!==typeof process&&"[object process]"==={}.toString.call(process)?ae():bc?ce():Fe?de():kd||"function"!==typeof require?Wa():ee();var ua=Math.random().toString(36).substring(16), b)},q)try{k(b)}catch(rf){q=!1}a.b(b.content)}};k(a.prototype);a.b=function(b){b=b.querySelectorAll("template");for(var c=0,d=b.length,e;c<d&&(e=b[c]);c++)a.R(e)};document.addEventListener("DOMContentLoaded",function(){a.b(document)});Document.prototype.createElement=function(){var b=d.apply(this,arguments);"template"===b.localName&&a.R(b);return b};var u=/[&\u00A0<>]/g}if(b||f)a.a=function(a,b){var d=c.call(a,!1);this.R&&this.R(d);b&&(d.content.appendChild(c.call(a.content,!0)),this.ta(d.content,
fa=new gc,ab=new gc,hc=0;ha.prototype.cb=function(a){for(var b=0;void 0===this.o&&b<a.length;b++)this.bb(a[b],b)};ha.prototype.bb=function(a,b){var c=this.eb,d=c.resolve;d===Za?(d=ec(a),d===Xa&&void 0!==a.o?this.na(a.o,b,a.m):"function"!==typeof d?(this.$--,this.m[b]=a):c===y?(c=new c(Y),fc(c,a,d),this.oa(c,b)):this.oa(new c(function(b){return b(a)}),b)):this.oa(d(a),b)};ha.prototype.na=function(a,b,c){var d=this.J;void 0===d.o&&(this.$--,2===a?A(d,c):this.m[b]=c);0===this.$&&K(d,this.m)};ha.prototype.oa= a.content));return d},a.prototype.cloneNode=function(b){return a.a(this,b)},a.ta=function(a,b){if(b.querySelectorAll){b=b.querySelectorAll("template");a=a.querySelectorAll("template");for(var c=0,d=a.length,e,f;c<d;c++)f=b[c],e=a[c],this.R&&this.R(f),e.parentNode.replaceChild(f.cloneNode(!0),e)}},Node.prototype.cloneNode=function(b){if(this instanceof DocumentFragment)if(b)var d=this.ownerDocument.importNode(this,!0);else return this.ownerDocument.createDocumentFragment();else d=c.call(this,b);b&&
function(a,b){var c=this;Ya(a,void 0,function(a){return c.na(1,b,a)},function(a){return c.na(2,b,a)})};y.g=function(a){return(new ha(this,a)).J};y.h=function(a){var b=this;return ic(a)?new b(function(c,d){for(var e=a.length,f=0;f<e;f++)b.resolve(a[f]).then(c,d)}):new b(function(a,b){return b(new TypeError("You must pass an array to race."))})};y.resolve=Za;y.i=function(a){var b=new this(Y);A(b,a);return b};y.f=function(a){Ua=a};y.c=function(a){Q=a};y.b=Q;y.prototype={constructor:y,then:Xa};y.a=function(){if("undefined"!== a.ta(d,this);return d},Document.prototype.importNode=function(b,c){if("template"===b.localName)return a.a(b,c);var d=e.call(this,b,c);c&&a.ta(d,b);return d},f&&(window.HTMLTemplateElement.prototype.cloneNode=function(b){return a.a(this,b)});b&&(window.HTMLTemplateElement=a)})();var Db;Array.isArray?Db=Array.isArray:Db=function(a){return"[object Array]"===Object.prototype.toString.call(a)};var nc=Db,Z=0,ab,$a,qd="undefined"!==typeof window?window:void 0,rd=qd||{},gc=rd.MutationObserver||rd.WebKitMutationObserver,
typeof global)var a=global;else if("undefined"!==typeof self)a=self;else try{a=Function("return this")()}catch(d){throw Error("polyfill failed because global object is unavailable in this environment");}var b=a.Promise;if(b){var c=null;try{c=Object.prototype.toString.call(b.resolve())}catch(d){}if("[object Promise]"===c&&!b.Eb)return}a.Promise=y};y.Promise=y;y.a();(function(a){function b(a,b){if("function"===typeof window.CustomEvent)return new CustomEvent(a,b);var c=document.createEvent("CustomEvent"); Qe="undefined"!==typeof Uint8ClampedArray&&"undefined"!==typeof importScripts&&"undefined"!==typeof MessageChannel,Y=Array(1E3);var ke="undefined"===typeof self&&"undefined"!==typeof process&&"[object process]"==={}.toString.call(process)?le():gc?ne():Qe?oe():qd||"function"!==typeof require?bb():pe();var ya=Math.random().toString(36).substring(16),ka=new lc,gb=new lc,mc=0;la.prototype.cb=function(a){for(var b=0;void 0===this.o&&b<a.length;b++)this.bb(a[b],b)};la.prototype.bb=function(a,b){var c=this.eb,
c.initCustomEvent(a,!!b.bubbles,!!b.cancelable,b.detail);return c}function c(a){if(l)return a.ownerDocument!==document?a.ownerDocument:null;var b=a.__importDoc;if(!b&&a.parentNode){b=a.parentNode;if("function"===typeof b.closest)b=b.closest("link[rel=import]");else for(;!h(b)&&(b=b.parentNode););a.__importDoc=b}return b}function d(a){var b=document.querySelectorAll("link[rel=import]:not(import-dependency)"),c=b.length;c?k(b,function(b){return g(b,function(){0===--c&&a()})}):a()}function e(a){function b(){"loading"!== d=c.resolve;d===eb?(d=jc(a),d===cb&&void 0!==a.o?this.pa(a.o,b,a.m):"function"!==typeof d?(this.aa--,this.m[b]=a):c===D?(c=new c(ba),kc(c,a,d),this.qa(c,b)):this.qa(new c(function(b){return b(a)}),b)):this.qa(d(a),b)};la.prototype.pa=function(a,b,c){var d=this.L;void 0===d.o&&(this.aa--,2===a?C(d,c):this.m[b]=c);0===this.aa&&L(d,this.m)};la.prototype.qa=function(a,b){var c=this;db(a,void 0,function(a){return c.pa(1,b,a)},function(a){return c.pa(2,b,a)})};D.all=function(a){return(new la(this,a)).L};
document.readyState&&document.body&&(document.removeEventListener("readystatechange",b),a())}document.addEventListener("readystatechange",b);b()}function f(a){e(function(){return d(function(){return a&&a()})})}function g(a,b){if(a.__loaded)b&&b();else if("script"===a.localName&&!a.src||"style"===a.localName&&!a.firstChild)a.__loaded=!0,b&&b();else{var c=function(d){a.removeEventListener(d.type,c);a.__loaded=!0;b&&b()};a.addEventListener("load",c);x&&"style"===a.localName||a.addEventListener("error", D.race=function(a){var b=this;return nc(a)?new b(function(c,d){for(var e=a.length,f=0;f<e;f++)b.resolve(a[f]).then(c,d)}):new b(function(a,b){return b(new TypeError("You must pass an array to race."))})};D.resolve=eb;D.reject=function(a){var b=new this(ba);C(b,a);return b};D.Fb=function(a){$a=a};D.Eb=function(a){T=a};D.Bb=T;D.prototype={constructor:D,then:cb,a:function(a){return this.then(null,a)}};window.Promise||(window.Promise=D,D.prototype["catch"]=D.prototype.a);(function(a){function b(a,b){if("function"===
c)}}function h(a){return a.nodeType===Node.ELEMENT_NODE&&"link"===a.localName&&"import"===a.rel}function m(){var a=this;this.a={};this.b=0;this.f=new MutationObserver(function(b){return a.l(b)});this.f.observe(document.head,{childList:!0,subtree:!0});this.c(document)}function k(a,b,c){var d=a?a.length:0,e=c?-1:1;for(c=c?d-1:0;c<d&&0<=c;c+=e)b(a[c],c)}var l="import"in document.createElement("link"),n=null;!1==="currentScript"in document&&Object.defineProperty(document,"currentScript",{get:function(){return n|| typeof window.CustomEvent)return new CustomEvent(a,b);var c=document.createEvent("CustomEvent");c.initCustomEvent(a,!!b.bubbles,!!b.cancelable,b.detail);return c}function c(a){if(n)return a.ownerDocument!==document?a.ownerDocument:null;var b=a.__importDoc;if(!b&&a.parentNode){b=a.parentNode;if("function"===typeof b.closest)b=b.closest("link[rel=import]");else for(;!g(b)&&(b=b.parentNode););a.__importDoc=b}return b}function d(a){var b=document.querySelectorAll("link[rel=import]:not([import-dependency])"),
("complete"!==document.readyState?document.scripts[document.scripts.length-1]:null)},configurable:!0});var p=/(^\/)|(^#)|(^[\w-\d]*:)/,r=/(url\()([^)]*)(\))/g,t=/(@import[\s]+(?!url\())([^;]*)(;)/g,w=/(<link[^>]*)(rel=['|"]?stylesheet['|"]?[^>]*>)/g,q={nb:function(a,b){a.href&&a.setAttribute("href",q.ta(a.getAttribute("href"),b));a.src&&a.setAttribute("src",q.ta(a.getAttribute("src"),b));if("style"===a.localName){var c=q.La(a.textContent,b,r);a.textContent=q.La(c,b,t)}},La:function(a,b,c){return a.replace(c, c=b.length;c?l(b,function(b){return h(b,function(){0===--c&&a()})}):a()}function e(a){function b(){"loading"!==document.readyState&&document.body&&(document.removeEventListener("readystatechange",b),a())}document.addEventListener("readystatechange",b);b()}function f(a){e(function(){return d(function(){return a&&a()})})}function h(a,b){if(a.__loaded)b&&b();else if("script"===a.localName&&!a.src||"style"===a.localName&&!a.firstChild)a.__loaded=!0,b&&b();else{var c=function(d){a.removeEventListener(d.type,
function(a,c,d,e){a=d.replace(/["']/g,"");b&&(a=q.Ma(a,b));return c+"'"+a+"'"+e})},ta:function(a,b){return a&&p.test(a)?a:q.Ma(a,b)},Ma:function(a,b){if(void 0===q.la){q.la=!1;try{var c=new URL("b","http://a");c.pathname="c%20d";q.la="http://a/c%20d"===c.href}catch(ef){}}if(q.la)return(new URL(a,b)).href;c=q.Za;c||(c=document.implementation.createHTMLDocument("temp"),q.Za=c,c.wa=c.createElement("base"),c.head.appendChild(c.wa),c.va=c.createElement("a"));c.wa.href=b;c.va.href=a;return c.va.href||a}}, c);a.__loaded=!0;b&&b()};a.addEventListener("load",c);z&&"style"===a.localName||a.addEventListener("error",c)}}function g(a){return a.nodeType===Node.ELEMENT_NODE&&"link"===a.localName&&"import"===a.rel}function m(){var a=this;this.a={};this.b=0;this.f=new MutationObserver(function(b){return a.l(b)});this.f.observe(document.head,{childList:!0,subtree:!0});this.c(document)}function l(a,b,c){var d=a?a.length:0,e=c?-1:1;for(c=c?d-1:0;c<d&&0<=c;c+=e)b(a[c],c)}var n="import"in document.createElement("link"),
y={async:!0,load:function(a,b,c){if(a)if(a.match(/^data:/)){a=a.split(",");var d=a[1];d=-1<a[0].indexOf(";base64")?atob(d):decodeURIComponent(d);b(d)}else{var e=new XMLHttpRequest;e.open("GET",a,y.async);e.onload=function(){var a=e.responseURL||e.getResponseHeader("Location");a&&0===a.indexOf("/")&&(a=(location.origin||location.protocol+"//"+location.host)+a);var d=e.response||e.responseText;304===e.status||0===e.status||200<=e.status&&300>e.status?b(d,a):c(d)};e.send()}else c("error: href must be specified")}}, p=null;!1==="currentScript"in document&&Object.defineProperty(document,"currentScript",{get:function(){return p||("complete"!==document.readyState?document.scripts[document.scripts.length-1]:null)},configurable:!0});var t=/(url\()([^)]*)(\))/g,u=/(@import[\s]+(?!url\())([^;]*)(;)/g,v=/(<link[^>]*)(rel=['|"]?stylesheet['|"]?[^>]*>)/g,r={mb:function(a,b){a.href&&a.setAttribute("href",r.fa(a.getAttribute("href"),b));a.src&&a.setAttribute("src",r.fa(a.getAttribute("src"),b));if("style"===a.localName){var c=
x=/Trident/.test(navigator.userAgent)||/Edge\/\d./i.test(navigator.userAgent);m.prototype.c=function(a){var b=this;a=a.querySelectorAll("link[rel=import]");k(a,function(a){return b.h(a)})};m.prototype.h=function(a){var b=this,c=a.href;if(void 0!==this.a[c]){var d=this.a[c];d&&d.__loaded&&(a.import=d,this.g(a))}else this.b++,this.a[c]="pending",y.load(c,function(a,d){a=b.s(a,d||c);b.a[c]=a;b.b--;b.c(a);b.i()},function(){b.a[c]=null;b.b--;b.i()})};m.prototype.s=function(a,b){if(!a)return document.createDocumentFragment(); r.Ma(a.textContent,b,t);a.textContent=r.Ma(c,b,u)}},Ma:function(a,b,c){return a.replace(c,function(a,c,d,e){a=d.replace(/["']/g,"");b&&(a=r.fa(a,b));return c+"'"+a+"'"+e})},fa:function(a,b){if(void 0===r.na){r.na=!1;try{var c=new URL("b","http://a");c.pathname="c%20d";r.na="http://a/c%20d"===c.href}catch(sf){}}if(r.na)return(new URL(a,b)).href;c=r.Za;c||(c=document.implementation.createHTMLDocument("temp"),r.Za=c,c.ya=c.createElement("base"),c.head.appendChild(c.ya),c.xa=c.createElement("a"));c.ya.href=
x&&(a=a.replace(w,function(a,b,c){return-1===a.indexOf("type=")?b+" type=import-disable "+c:a}));var c=document.createElement("template");c.innerHTML=a;if(c.content)a=c.content;else for(a=document.createDocumentFragment();c.firstChild;)a.appendChild(c.firstChild);if(c=a.querySelector("base"))b=q.ta(c.getAttribute("href"),b),c.removeAttribute("href");c=a.querySelectorAll('link[rel=import], link[rel=stylesheet][href][type=import-disable],\n style:not([type]), link[rel=stylesheet][href]:not([type]),\n script:not([type]), script[type="application/javascript"],\n script[type="text/javascript"]'); b;c.xa.href=a;return c.xa.href||a}},w={async:!0,load:function(a,b,c){if(a)if(a.match(/^data:/)){a=a.split(",");var d=a[1];d=-1<a[0].indexOf(";base64")?atob(d):decodeURIComponent(d);b(d)}else{var e=new XMLHttpRequest;e.open("GET",a,w.async);e.onload=function(){var a=e.responseURL||e.getResponseHeader("Location");a&&0===a.indexOf("/")&&(a=(location.origin||location.protocol+"//"+location.host)+a);var d=e.response||e.responseText;304===e.status||0===e.status||200<=e.status&&300>e.status?b(d,a):c(d)};
var d=0;k(c,function(a){g(a);q.nb(a,b);a.setAttribute("import-dependency","");"script"===a.localName&&!a.src&&a.textContent&&(a.setAttribute("src","data:text/javascript;charset=utf-8,"+encodeURIComponent(a.textContent+("\n//# sourceURL="+b+(d?"-"+d:"")+".js\n"))),a.textContent="",d++)});return a};m.prototype.i=function(){var a=this;if(!this.b){this.f.disconnect();this.flatten(document);var b=!1,c=!1,d=function(){c&&b&&(a.c(document),a.b||(a.f.observe(document.head,{childList:!0,subtree:!0}),a.j()))}; e.send()}else c("error: href must be specified")}},z=/Trident/.test(navigator.userAgent)||/Edge\/\d./i.test(navigator.userAgent);m.prototype.c=function(a){var b=this;a=a.querySelectorAll("link[rel=import]");l(a,function(a){return b.h(a)})};m.prototype.h=function(a){var b=this,c=a.href;if(void 0!==this.a[c]){var d=this.a[c];d&&d.__loaded&&(a.import=d,this.g(a))}else this.b++,this.a[c]="pending",w.load(c,function(a,d){a=b.s(a,d||c);b.a[c]=a;b.b--;b.c(a);b.i()},function(){b.a[c]=null;b.b--;b.i()})};
this.v(function(){c=!0;d()});this.u(function(){b=!0;d()})}};m.prototype.flatten=function(a){var b=this;a=a.querySelectorAll("link[rel=import]");k(a,function(a){var c=b.a[a.href];(a.import=c)&&c.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&(b.a[a.href]=a,a.readyState="loading",a.import=a,b.flatten(c),a.appendChild(c))})};m.prototype.u=function(a){function b(e){if(e<d){var f=c[e],h=document.createElement("script");f.removeAttribute("import-dependency");k(f.attributes,function(a){return h.setAttribute(a.name, m.prototype.s=function(a,b){if(!a)return document.createDocumentFragment();z&&(a=a.replace(v,function(a,b,c){return-1===a.indexOf("type=")?b+" type=import-disable "+c:a}));var c=document.createElement("template");c.innerHTML=a;if(c.content)a=c.content;else for(a=document.createDocumentFragment();c.firstChild;)a.appendChild(c.firstChild);if(c=a.querySelector("base"))b=r.fa(c.getAttribute("href"),b),c.removeAttribute("href");c=a.querySelectorAll('link[rel=import], link[rel=stylesheet][href][type=import-disable],\n style:not([type]), link[rel=stylesheet][href]:not([type]),\n script:not([type]), script[type="application/javascript"],\n script[type="text/javascript"]');
a.value)});n=h;f.parentNode.replaceChild(h,f);g(h,function(){n=null;b(e+1)})}else a()}var c=document.querySelectorAll("script[import-dependency]"),d=c.length;b(0)};m.prototype.v=function(a){var b=document.querySelectorAll("style[import-dependency],\n link[rel=stylesheet][import-dependency]"),d=b.length;if(d){var e=x&&!!document.querySelector("link[rel=stylesheet][href][type=import-disable]");k(b,function(b){g(b,function(){b.removeAttribute("import-dependency");0===--d&&a()});if(e&&b.parentNode!== var d=0;l(c,function(a){h(a);r.mb(a,b);a.setAttribute("import-dependency","");"script"===a.localName&&!a.src&&a.textContent&&(a.setAttribute("src","data:text/javascript;charset=utf-8,"+encodeURIComponent(a.textContent+("\n//# sourceURL="+b+(d?"-"+d:"")+".js\n"))),a.textContent="",d++)});return a};m.prototype.i=function(){var a=this;if(!this.b){this.f.disconnect();this.flatten(document);var b=!1,c=!1,d=function(){c&&b&&(a.c(document),a.b||(a.f.observe(document.head,{childList:!0,subtree:!0}),a.j()))};
document.head){var f=document.createElement(b.localName);f.__appliedElement=b;f.setAttribute("type","import-placeholder");b.parentNode.insertBefore(f,b.nextSibling);for(f=c(b);f&&c(f);)f=c(f);f.parentNode!==document.head&&(f=null);document.head.insertBefore(b,f);b.removeAttribute("type")}})}else a()};m.prototype.j=function(){var a=this,b=document.querySelectorAll("link[rel=import]");k(b,function(b){return a.g(b)},!0)};m.prototype.g=function(a){a.__loaded||(a.__loaded=!0,a.import&&(a.import.readyState= this.v(function(){c=!0;d()});this.u(function(){b=!0;d()})}};m.prototype.flatten=function(a){var b=this;a=a.querySelectorAll("link[rel=import]");l(a,function(a){var c=b.a[a.href];(a.import=c)&&c.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&(b.a[a.href]=a,a.readyState="loading",a.import=a,b.flatten(c),a.appendChild(c))})};m.prototype.u=function(a){function b(e){if(e<d){var f=c[e],g=document.createElement("script");f.removeAttribute("import-dependency");l(f.attributes,function(a){return g.setAttribute(a.name,
"complete"),a.dispatchEvent(b(a.import?"load":"error",{bubbles:!1,cancelable:!1,detail:void 0})))};m.prototype.l=function(a){var b=this;k(a,function(a){return k(a.addedNodes,function(a){a&&a.nodeType===Node.ELEMENT_NODE&&(h(a)?b.h(a):b.c(a))})})};if(l){var v=document.querySelectorAll("link[rel=import]");k(v,function(a){a.import&&"loading"===a.import.readyState||(a.__loaded=!0)});v=function(a){a=a.target;h(a)&&(a.__loaded=!0)};document.addEventListener("load",v,!0);document.addEventListener("error", a.value)});p=g;f.parentNode.replaceChild(g,f);h(g,function(){p=null;b(e+1)})}else a()}var c=document.querySelectorAll("script[import-dependency]"),d=c.length;b(0)};m.prototype.v=function(a){var b=document.querySelectorAll("style[import-dependency],\n link[rel=stylesheet][import-dependency]"),d=b.length;if(d){var e=z&&!!document.querySelector("link[rel=stylesheet][href][type=import-disable]");l(b,function(b){h(b,function(){b.removeAttribute("import-dependency");0===--d&&a()});if(e&&b.parentNode!==
v,!0)}else{var u=Object.getOwnPropertyDescriptor(Node.prototype,"baseURI");Object.defineProperty((!u||u.configurable?Node:Element).prototype,"baseURI",{get:function(){var a=h(this)?this:c(this);return a?a.href:u&&u.get?u.get.call(this):(document.querySelector("base")||window.location).href},configurable:!0,enumerable:!0});e(function(){return new m})}f(function(){return document.dispatchEvent(b("HTMLImportsLoaded",{cancelable:!0,bubbles:!0,detail:void 0}))});a.useNative=l;a.whenReady=f;a.importForElement= document.head){var f=document.createElement(b.localName);f.__appliedElement=b;f.setAttribute("type","import-placeholder");b.parentNode.insertBefore(f,b.nextSibling);for(f=c(b);f&&c(f);)f=c(f);f.parentNode!==document.head&&(f=null);document.head.insertBefore(b,f);b.removeAttribute("type")}})}else a()};m.prototype.j=function(){var a=this,b=document.querySelectorAll("link[rel=import]");l(b,function(b){return a.g(b)},!0)};m.prototype.g=function(a){a.__loaded||(a.__loaded=!0,a.import&&(a.import.readyState=
c})(window.HTMLImports=window.HTMLImports||{});window.WebComponents=window.WebComponents||{flags:{}};var md=document.querySelector('script[src*="webcomponents-lite.js"]'),Ge=/wc-(.+)/,E={};if(!E.noOpts){location.search.slice(1).split("&").forEach(function(a){a=a.split("=");var b;a[0]&&(b=a[0].match(Ge))&&(E[b[1]]=a[1]||!0)});if(md)for(var nd=0,Ba;Ba=md.attributes[nd];nd++)"src"!==Ba.name&&(E[Ba.name]=Ba.value||!0);if(E.log&&E.log.split){var He=E.log.split(",");E.log={};He.forEach(function(a){E.log[a]= "complete"),a.dispatchEvent(b(a.import?"load":"error",{bubbles:!1,cancelable:!1,detail:void 0})))};m.prototype.l=function(a){var b=this;l(a,function(a){return l(a.addedNodes,function(a){a&&a.nodeType===Node.ELEMENT_NODE&&(g(a)?b.h(a):b.c(a))})})};if(n){var x=document.querySelectorAll("link[rel=import]");l(x,function(a){a.import&&"loading"===a.import.readyState||(a.__loaded=!0)});x=function(a){a=a.target;g(a)&&(a.__loaded=!0)};document.addEventListener("load",x,!0);document.addEventListener("error",
!0})}else E.log={}}window.WebComponents.flags=E;var od=E.shadydom;od&&(window.ShadyDOM=window.ShadyDOM||{},window.ShadyDOM.force=od);var pd=E.register||E.ce;pd&&window.customElements&&(window.customElements.forcePolyfill=pd);var D=window.ShadyDOM||{};D.ob=!(!Element.prototype.attachShadow||!Node.prototype.getRootNode);var yb=Object.getOwnPropertyDescriptor(Node.prototype,"firstChild");D.W=!!(yb&&yb.configurable&&yb.get);D.Ha=D.force||!D.ob;var da=Element.prototype,qd=da.matches||da.matchesSelector|| x,!0)}else{var y=Object.getOwnPropertyDescriptor(Node.prototype,"baseURI");Object.defineProperty((!y||y.configurable?Node:Element).prototype,"baseURI",{get:function(){var a=g(this)?this:c(this);return a?a.href:y&&y.get?y.get.call(this):(document.querySelector("base")||window.location).href},configurable:!0,enumerable:!0});e(function(){return new m})}f(function(){return document.dispatchEvent(b("HTMLImportsLoaded",{cancelable:!0,bubbles:!0,detail:void 0}))});a.useNative=n;a.whenReady=f;a.importForElement=
da.mozMatchesSelector||da.msMatchesSelector||da.oMatchesSelector||da.webkitMatchesSelector,eb=document.createTextNode(""),kc=0,db=[];(new MutationObserver(function(){for(;db.length;)try{db.shift()()}catch(a){throw eb.textContent=kc++,a;}})).observe(eb,{characterData:!0});var ja=[],fb;va.list=ja;ta.prototype.vb=function(){var a=this;this.a||(this.a=!0,jc(function(){a.b()}))};ta.prototype.b=function(){if(this.a){this.a=!1;var a=this.takeRecords();a.length&&this.ba.forEach(function(b){b(a)})}};ta.prototype.takeRecords= c})(window.HTMLImports=window.HTMLImports||{});window.WebComponents=window.WebComponents||{flags:{}};var sd=document.querySelector('script[src*="webcomponents-lite.js"]'),Re=/wc-(.+)/,H={};if(!H.noOpts){location.search.slice(1).split("&").forEach(function(a){a=a.split("=");var b;a[0]&&(b=a[0].match(Re))&&(H[b[1]]=a[1]||!0)});if(sd)for(var td=0,Fa;Fa=sd.attributes[td];td++)"src"!==Fa.name&&(H[Fa.name]=Fa.value||!0);if(H.log&&H.log.split){var Se=H.log.split(",");H.log={};Se.forEach(function(a){H.log[a]=
function(){if(this.addedNodes.length||this.removedNodes.length){var a=[{addedNodes:this.addedNodes,removedNodes:this.removedNodes}];this.addedNodes=[];this.removedNodes=[];return a}return[]};var Bc=Element.prototype.appendChild,mb=Element.prototype.insertBefore,ka=Element.prototype.removeChild,Jc=Element.prototype.setAttribute,rd=Element.prototype.removeAttribute,zb=Element.prototype.cloneNode,nb=Document.prototype.importNode,Rc=Element.prototype.addEventListener,Uc=Element.prototype.removeEventListener, !0})}else H.log={}}window.WebComponents.flags=H;var ud=H.shadydom;ud&&(window.ShadyDOM=window.ShadyDOM||{},window.ShadyDOM.force=ud);var vd=H.register||H.ce;vd&&window.customElements&&(window.customElements.forcePolyfill=vd);var G=window.ShadyDOM||{};G.nb=!(!Element.prototype.attachShadow||!Node.prototype.getRootNode);var Eb=Object.getOwnPropertyDescriptor(Node.prototype,"firstChild");G.X=!!(Eb&&Eb.configurable&&Eb.get);G.Ja=G.force||!G.nb;var ha=Element.prototype,wd=ha.matches||ha.matchesSelector||
Qc=Window.prototype.addEventListener,Tc=Window.prototype.removeEventListener,Ab=Element.prototype.dispatchEvent,Ie=Object.freeze({appendChild:Bc,insertBefore:mb,removeChild:ka,setAttribute:Jc,removeAttribute:rd,cloneNode:zb,importNode:nb,addEventListener:Rc,removeEventListener:Uc,Jb:Qc,Kb:Tc,dispatchEvent:Ab,querySelector:Element.prototype.querySelector,querySelectorAll:Element.prototype.querySelectorAll}),me=/[&\u00A0"]/g,pe=/[&\u00A0<>]/g,ne=nc("area base br col command embed hr img input keygen link meta param source track wbr".split(" ")), ha.mozMatchesSelector||ha.msMatchesSelector||ha.oMatchesSelector||ha.webkitMatchesSelector,kb=document.createTextNode(""),pc=0,jb=[];(new MutationObserver(function(){for(;jb.length;)try{jb.shift()()}catch(a){throw kb.textContent=pc++,a;}})).observe(kb,{characterData:!0});var Te=!!document.contains,na=[],lb;za.list=na;xa.prototype.ub=function(){var a=this;this.a||(this.a=!0,oc(function(){a.b()}))};xa.prototype.b=function(){if(this.a){this.a=!1;var a=this.takeRecords();a.length&&this.ca.forEach(function(b){b(a)})}};
oe=nc("style script xmp iframe noembed noframes plaintext noscript".split(" ")),B=document.createTreeWalker(document,NodeFilter.SHOW_ALL,null,!1),C=document.createTreeWalker(document,NodeFilter.SHOW_ELEMENT,null,!1),Je=Object.freeze({parentNode:aa,firstChild:Sa,lastChild:Ta,previousSibling:oc,nextSibling:pc,childNodes:U,parentElement:qc,firstElementChild:rc,lastElementChild:sc,previousElementSibling:tc,nextElementSibling:uc,children:vc,innerHTML:wc,textContent:xc}),Bb=Object.getOwnPropertyDescriptor(Element.prototype, xa.prototype.takeRecords=function(){if(this.addedNodes.length||this.removedNodes.length){var a=[{addedNodes:this.addedNodes,removedNodes:this.removedNodes}];this.addedNodes=[];this.removedNodes=[];return a}return[]};var Hc=Element.prototype.appendChild,sb=Element.prototype.insertBefore,oa=Element.prototype.removeChild,Pc=Element.prototype.setAttribute,xd=Element.prototype.removeAttribute,Fb=Element.prototype.cloneNode,tb=Document.prototype.importNode,Xc=Element.prototype.addEventListener,$c=Element.prototype.removeEventListener,
"innerHTML")||Object.getOwnPropertyDescriptor(HTMLElement.prototype,"innerHTML"),Ca=document.implementation.createHTMLDocument("inert").createElement("div"),Cb=Object.getOwnPropertyDescriptor(Document.prototype,"activeElement"),yc={parentElement:{get:function(){var a=this.__shady&&this.__shady.parentNode;a&&a.nodeType!==Node.ELEMENT_NODE&&(a=null);return void 0!==a?a:qc(this)},configurable:!0},parentNode:{get:function(){var a=this.__shady&&this.__shady.parentNode;return void 0!==a?a:aa(this)},configurable:!0}, Wc=Window.prototype.addEventListener,Zc=Window.prototype.removeEventListener,Gb=Element.prototype.dispatchEvent,pa=Node.prototype.contains||HTMLElement.prototype.contains,Ue=Object.freeze({appendChild:Hc,insertBefore:sb,removeChild:oa,setAttribute:Pc,removeAttribute:xd,cloneNode:Fb,importNode:tb,addEventListener:Xc,removeEventListener:$c,Kb:Wc,Lb:Zc,dispatchEvent:Gb,querySelector:Element.prototype.querySelector,querySelectorAll:Element.prototype.querySelectorAll,contains:pa}),xe=/[&\u00A0"]/g,Ae=
nextSibling:{get:function(){var a=this.__shady&&this.__shady.nextSibling;return void 0!==a?a:pc(this)},configurable:!0},previousSibling:{get:function(){var a=this.__shady&&this.__shady.previousSibling;return void 0!==a?a:oc(this)},configurable:!0},className:{get:function(){return this.getAttribute("class")||""},set:function(a){this.setAttribute("class",a)},configurable:!0},nextElementSibling:{get:function(){if(this.__shady&&void 0!==this.__shady.nextSibling){for(var a=this.nextSibling;a&&a.nodeType!== /[&\u00A0<>]/g,ye=tc("area base br col command embed hr img input keygen link meta param source track wbr".split(" ")),ze=tc("style script xmp iframe noembed noframes plaintext noscript".split(" ")),E=document.createTreeWalker(document,NodeFilter.SHOW_ALL,null,!1),F=document.createTreeWalker(document,NodeFilter.SHOW_ELEMENT,null,!1),Ve=Object.freeze({parentNode:da,firstChild:Ya,lastChild:Za,previousSibling:uc,nextSibling:vc,childNodes:X,parentElement:wc,firstElementChild:xc,lastElementChild:yc,previousElementSibling:zc,
Node.ELEMENT_NODE;)a=a.nextSibling;return a}return uc(this)},configurable:!0},previousElementSibling:{get:function(){if(this.__shady&&void 0!==this.__shady.previousSibling){for(var a=this.previousSibling;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.previousSibling;return a}return tc(this)},configurable:!0}},hb={childNodes:{get:function(){if(Z(this)){if(!this.__shady.childNodes){this.__shady.childNodes=[];for(var a=this.firstChild;a;a=a.nextSibling)this.__shady.childNodes.push(a)}var b=this.__shady.childNodes}else b= nextElementSibling:Ac,children:Bc,innerHTML:Cc,textContent:Dc}),Hb=Object.getOwnPropertyDescriptor(Element.prototype,"innerHTML")||Object.getOwnPropertyDescriptor(HTMLElement.prototype,"innerHTML"),Ga=document.implementation.createHTMLDocument("inert").createElement("div"),Ib=Object.getOwnPropertyDescriptor(Document.prototype,"activeElement"),Ec={parentElement:{get:function(){var a=this.__shady&&this.__shady.parentNode;a&&a.nodeType!==Node.ELEMENT_NODE&&(a=null);return void 0!==a?a:wc(this)},configurable:!0},
U(this);b.item=function(a){return b[a]};return b},configurable:!0},childElementCount:{get:function(){return this.children.length},configurable:!0},firstChild:{get:function(){var a=this.__shady&&this.__shady.firstChild;return void 0!==a?a:Sa(this)},configurable:!0},lastChild:{get:function(){var a=this.__shady&&this.__shady.lastChild;return void 0!==a?a:Ta(this)},configurable:!0},textContent:{get:function(){if(Z(this)){for(var a=[],b=0,c=this.childNodes,d;d=c[b];b++)d.nodeType!==Node.COMMENT_NODE&& parentNode:{get:function(){var a=this.__shady&&this.__shady.parentNode;return void 0!==a?a:da(this)},configurable:!0},nextSibling:{get:function(){var a=this.__shady&&this.__shady.nextSibling;return void 0!==a?a:vc(this)},configurable:!0},previousSibling:{get:function(){var a=this.__shady&&this.__shady.previousSibling;return void 0!==a?a:uc(this)},configurable:!0},className:{get:function(){return this.getAttribute("class")||""},set:function(a){this.setAttribute("class",a)},configurable:!0},nextElementSibling:{get:function(){if(this.__shady&&
a.push(d.textContent);return a.join("")}return xc(this)},set:function(a){switch(this.nodeType){case Node.ELEMENT_NODE:case Node.DOCUMENT_FRAGMENT_NODE:for(;this.firstChild;)this.removeChild(this.firstChild);(0<a.length||this.nodeType===Node.ELEMENT_NODE)&&this.appendChild(document.createTextNode(a));break;default:this.nodeValue=a}},configurable:!0},firstElementChild:{get:function(){if(this.__shady&&void 0!==this.__shady.firstChild){for(var a=this.firstChild;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.nextSibling; void 0!==this.__shady.nextSibling){for(var a=this.nextSibling;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.nextSibling;return a}return Ac(this)},configurable:!0},previousElementSibling:{get:function(){if(this.__shady&&void 0!==this.__shady.previousSibling){for(var a=this.previousSibling;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.previousSibling;return a}return zc(this)},configurable:!0}},nb={childNodes:{get:function(){if(ca(this)){if(!this.__shady.childNodes){this.__shady.childNodes=[];for(var a=this.firstChild;a;a=
return a}return rc(this)},configurable:!0},lastElementChild:{get:function(){if(this.__shady&&void 0!==this.__shady.lastChild){for(var a=this.lastChild;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.previousSibling;return a}return sc(this)},configurable:!0},children:{get:function(){var a;Z(this)?a=Array.prototype.filter.call(this.childNodes,function(a){return a.nodeType===Node.ELEMENT_NODE}):a=vc(this);a.item=function(b){return a[b]};return a},configurable:!0},innerHTML:{get:function(){var a="template"=== a.nextSibling)this.__shady.childNodes.push(a)}var b=this.__shady.childNodes}else b=X(this);b.item=function(a){return b[a]};return b},configurable:!0},childElementCount:{get:function(){return this.children.length},configurable:!0},firstChild:{get:function(){var a=this.__shady&&this.__shady.firstChild;return void 0!==a?a:Ya(this)},configurable:!0},lastChild:{get:function(){var a=this.__shady&&this.__shady.lastChild;return void 0!==a?a:Za(this)},configurable:!0},textContent:{get:function(){if(ca(this)){for(var a=
this.localName?this.content:this;return Z(this)?gb(a):wc(a)},set:function(a){for(var b="template"===this.localName?this.content:this;b.firstChild;)b.removeChild(b.firstChild);for(Bb&&Bb.set?Bb.set.call(Ca,a):Ca.innerHTML=a;Ca.firstChild;)b.appendChild(Ca.firstChild)},configurable:!0}},sd={shadowRoot:{get:function(){return this.__shady&&this.__shady.tb||null},configurable:!0}},ib={activeElement:{get:function(){var a=Cb&&Cb.get?Cb.get.call(document):D.W?void 0:document.activeElement;if(a&&a.nodeType){var b= [],b=0,c=this.childNodes,d;d=c[b];b++)d.nodeType!==Node.COMMENT_NODE&&a.push(d.textContent);return a.join("")}return Dc(this)},set:function(a){switch(this.nodeType){case Node.ELEMENT_NODE:case Node.DOCUMENT_FRAGMENT_NODE:for(;this.firstChild;)this.removeChild(this.firstChild);(0<a.length||this.nodeType===Node.ELEMENT_NODE)&&this.appendChild(document.createTextNode(a));break;default:this.nodeValue=a}},configurable:!0},firstElementChild:{get:function(){if(this.__shady&&void 0!==this.__shady.firstChild){for(var a=
!!I(this);if(this===document||b&&this.host!==a&&this.host.contains(a)){for(b=ia(a);b&&b!==this;)a=b.host,b=ia(a);a=this===document?b?null:a:b===this?a:null}else a=null}else a=null;return a},set:function(){},configurable:!0}},ac=D.W?function(){}:function(a){a.__shady&&a.__shady.Xa||(a.__shady=a.__shady||{},a.__shady.Xa=!0,M(a,yc,!0))},$b=D.W?function(){}:function(a){a.__shady&&a.__shady.Va||(a.__shady=a.__shady||{},a.__shady.Va=!0,M(a,hb,!0),M(a,sd,!0))},xa=null,la="__eventWrappers"+Date.now(),Ke= this.firstChild;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.nextSibling;return a}return xc(this)},configurable:!0},lastElementChild:{get:function(){if(this.__shady&&void 0!==this.__shady.lastChild){for(var a=this.lastChild;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.previousSibling;return a}return yc(this)},configurable:!0},children:{get:function(){var a;ca(this)?a=Array.prototype.filter.call(this.childNodes,function(a){return a.nodeType===Node.ELEMENT_NODE}):a=Bc(this);a.item=function(b){return a[b]};return a},
{blur:!0,focus:!0,focusin:!0,focusout:!0,click:!0,dblclick:!0,mousedown:!0,mouseenter:!0,mouseleave:!0,mousemove:!0,mouseout:!0,mouseover:!0,mouseup:!0,wheel:!0,beforeinput:!0,input:!0,keydown:!0,keyup:!0,compositionstart:!0,compositionupdate:!0,compositionend:!0,touchstart:!0,touchend:!0,touchmove:!0,touchcancel:!0,pointerover:!0,pointerenter:!0,pointerdown:!0,pointermove:!0,pointerup:!0,pointercancel:!0,pointerout:!0,pointerleave:!0,gotpointercapture:!0,lostpointercapture:!0,dragstart:!0,drag:!0, configurable:!0},innerHTML:{get:function(){var a="template"===this.localName?this.content:this;return ca(this)?mb(a):Cc(a)},set:function(a){for(var b="template"===this.localName?this.content:this;b.firstChild;)b.removeChild(b.firstChild);for(Hb&&Hb.set?Hb.set.call(Ga,a):Ga.innerHTML=a;Ga.firstChild;)b.appendChild(Ga.firstChild)},configurable:!0}},yd={shadowRoot:{get:function(){return this.__shady&&this.__shady.sb||null},configurable:!0}},ob={activeElement:{get:function(){var a=Ib&&Ib.get?Ib.get.call(document):
dragenter:!0,dragleave:!0,dragover:!0,drop:!0,dragend:!0,DOMActivate:!0,DOMFocusIn:!0,DOMFocusOut:!0,keypress:!0},Vc={get composed(){!1!==this.isTrusted&&void 0===this.ia&&(this.ia=Ke[this.type]);return this.ia||!1},composedPath:function(){this.xa||(this.xa=ob(this.__target,this.composed));return this.xa},get target(){return Lc(this.currentTarget,this.composedPath())},get relatedTarget(){if(!this.ya)return null;this.za||(this.za=ob(this.ya,!0));return Lc(this.currentTarget,this.za)},stopPropagation:function(){Event.prototype.stopPropagation.call(this); G.X?void 0:document.activeElement;if(a&&a.nodeType){var b=!!J(this);if(this===document||b&&this.host!==a&&pa.call(this.host,a)){for(b=ma(a);b&&b!==this;)a=b.host,b=ma(a);a=this===document?b?null:a:b===this?a:null}else a=null}else a=null;return a},set:function(){},configurable:!0}},fc=G.X?function(){}:function(a){a.__shady&&a.__shady.Xa||(a.__shady=a.__shady||{},a.__shady.Xa=!0,O(a,Ec,!0))},ec=G.X?function(){}:function(a){a.__shady&&a.__shady.Va||(a.__shady=a.__shady||{},a.__shady.Va=!0,O(a,nb,!0),
this.ja=!0},stopImmediatePropagation:function(){Event.prototype.stopImmediatePropagation.call(this);this.ja=this.Ua=!0}},qb={focus:!0,blur:!0},Le=pb(window.Event),Me=pb(window.CustomEvent),Ne=pb(window.MouseEvent),Zb={};l.prototype=Object.create(DocumentFragment.prototype);l.prototype.D=function(a,b){this.Wa="ShadyRoot";sa(a);sa(this);this.host=a;this.L=b&&b.mode;a.__shady=a.__shady||{};a.__shady.root=this;a.__shady.tb="closed"!==this.L?this:null;this.T=!1;this.b=[];this.a=null;b=U(a);for(var c=0, O(a,yd,!0))},Ba=null,qa="__eventWrappers"+Date.now(),We={blur:!0,focus:!0,focusin:!0,focusout:!0,click:!0,dblclick:!0,mousedown:!0,mouseenter:!0,mouseleave:!0,mousemove:!0,mouseout:!0,mouseover:!0,mouseup:!0,wheel:!0,beforeinput:!0,input:!0,keydown:!0,keyup:!0,compositionstart:!0,compositionupdate:!0,compositionend:!0,touchstart:!0,touchend:!0,touchmove:!0,touchcancel:!0,pointerover:!0,pointerenter:!0,pointerdown:!0,pointermove:!0,pointerup:!0,pointercancel:!0,pointerout:!0,pointerleave:!0,gotpointercapture:!0,
d=b.length;c<d;c++)ka.call(a,b[c])};l.prototype.M=function(){var a=this;this.T||(this.T=!0,lc(function(){return a.Da()}))};l.prototype.C=function(){for(var a=this,b=this;b;)b.T&&(a=b),b=b.hb();return a};l.prototype.hb=function(){var a=this.host.getRootNode();if(I(a))for(var b=this.host.childNodes,c=0,d;c<b.length;c++)if(d=b[c],this.h(d))return a};l.prototype.Da=function(){this.T&&this.C()._renderRoot()};l.prototype._renderRoot=function(){this.T=!1;this.v();this.s()};l.prototype.v=function(){for(var a= lostpointercapture:!0,dragstart:!0,drag:!0,dragenter:!0,dragleave:!0,dragover:!0,drop:!0,dragend:!0,DOMActivate:!0,DOMFocusIn:!0,DOMFocusOut:!0,keypress:!0},ad={get composed(){!1!==this.isTrusted&&void 0===this.ka&&(this.ka=We[this.type]);return this.ka||!1},composedPath:function(){this.za||(this.za=ub(this.__target,this.composed));return this.za},get target(){return Rc(this.currentTarget,this.composedPath())},get relatedTarget(){if(!this.Aa)return null;this.Ba||(this.Ba=ub(this.Aa,!0));return Rc(this.currentTarget,
0,b;a<this.b.length;a++)b=this.b[a],this.l(b);for(b=this.host.firstChild;b;b=b.nextSibling)this.f(b);for(a=0;a<this.b.length;a++){b=this.b[a];if(!b.__shady.assignedNodes.length)for(var c=b.firstChild;c;c=c.nextSibling)this.f(c,b);c=b.parentNode;(c=c.__shady&&c.__shady.root)&&c.Aa()&&c._renderRoot();this.c(b.__shady.V,b.__shady.assignedNodes);if(c=b.__shady.Ca){for(var d=0;d<c.length;d++)c[d].__shady.ma=null;b.__shady.Ca=null;c.length>b.__shady.assignedNodes.length&&(b.__shady.pa=!0)}b.__shady.pa&& this.Ba)},stopPropagation:function(){Event.prototype.stopPropagation.call(this);this.la=!0},stopImmediatePropagation:function(){Event.prototype.stopImmediatePropagation.call(this);this.la=this.Ua=!0}},wb={focus:!0,blur:!0},Xe=vb(window.Event),Ye=vb(window.CustomEvent),Ze=vb(window.MouseEvent),dc={};l.prototype=Object.create(DocumentFragment.prototype);l.prototype.H=function(a,b){this.Wa="ShadyRoot";wa(a);wa(this);this.host=a;this.J=b&&b.mode;a.__shady=a.__shady||{};a.__shady.root=this;a.__shady.sb=
(b.__shady.pa=!1,this.g(b))}};l.prototype.f=function(a,b){a.__shady=a.__shady||{};var c=a.__shady.ma;a.__shady.ma=null;b||(b=(b=this.a[a.slot||"__catchall"])&&b[0]);b?(b.__shady.assignedNodes.push(a),a.__shady.assignedSlot=b):a.__shady.assignedSlot=void 0;c!==a.__shady.assignedSlot&&a.__shady.assignedSlot&&(a.__shady.assignedSlot.__shady.pa=!0)};l.prototype.l=function(a){var b=a.__shady.assignedNodes;a.__shady.assignedNodes=[];a.__shady.V=[];if(a.__shady.Ca=b)for(var c=0;c<b.length;c++){var d=b[c]; "closed"!==this.J?this:null;this.U=!1;this.b=[];this.a={};this.c=[];b=X(a);for(var c=0,d=b.length;c<d;c++)oa.call(a,b[c])};l.prototype.O=function(){var a=this;this.U||(this.U=!0,rc(function(){return a.Fa()}))};l.prototype.N=function(){for(var a=this,b=this;b;)b.U&&(a=b),b=b.hb();return a};l.prototype.hb=function(){var a=this.host.getRootNode();if(J(a))for(var b=this.host.childNodes,c=0,d;c<b.length;c++)if(d=b[c],this.j(d))return a};l.prototype.Fa=function(){this.U&&this.N()._renderRoot()};l.prototype._renderRoot=
d.__shady.ma=d.__shady.assignedSlot;d.__shady.assignedSlot===a&&(d.__shady.assignedSlot=null)}};l.prototype.c=function(a,b){for(var c=0,d;c<b.length&&(d=b[c]);c++)"slot"==d.localName?this.c(a,d.__shady.assignedNodes):a.push(b[c])};l.prototype.g=function(a){Ab.call(a,new Event("slotchange"));a.__shady.assignedSlot&&this.g(a.__shady.assignedSlot)};l.prototype.s=function(){for(var a=this.b,b=[],c=0;c<a.length;c++){var d=a[c].parentNode;d.__shady&&d.__shady.root||!(0>b.indexOf(d))||b.push(d)}for(a=0;a< function(){this.U=!1;this.D();this.v()};l.prototype.D=function(){this.f();for(var a=0,b;a<this.b.length;a++)b=this.b[a],this.u(b);for(b=this.host.firstChild;b;b=b.nextSibling)this.h(b);for(a=0;a<this.b.length;a++){b=this.b[a];if(!b.__shady.assignedNodes.length)for(var c=b.firstChild;c;c=c.nextSibling)this.h(c,b);c=b.parentNode;(c=c.__shady&&c.__shady.root)&&c.Ca()&&c._renderRoot();this.g(b.__shady.W,b.__shady.assignedNodes);if(c=b.__shady.Ea){for(var d=0;d<c.length;d++)c[d].__shady.oa=null;b.__shady.Ea=
b.length;a++)c=b[a],this.I(c===this?this.host:c,this.u(c))};l.prototype.u=function(a){var b=[];a=a.childNodes;for(var c=0;c<a.length;c++){var d=a[c];if(this.h(d)){d=d.__shady.V;for(var e=0;e<d.length;e++)b.push(d[e])}else b.push(d)}return b};l.prototype.h=function(a){return"slot"==a.localName};l.prototype.I=function(a,b){for(var c=U(a),d=se(b,b.length,c,c.length),e=0,f=0,g;e<d.length&&(g=d[e]);e++){for(var h=0,k;h<g.Y.length&&(k=g.Y[h]);h++)aa(k)===a&&ka.call(a,k),c.splice(g.index+f,1);f-=g.aa}for(e= null;c.length>b.__shady.assignedNodes.length&&(b.__shady.sa=!0)}b.__shady.sa&&(b.__shady.sa=!1,this.i(b))}};l.prototype.h=function(a,b){a.__shady=a.__shady||{};var c=a.__shady.oa;a.__shady.oa=null;b||(b=(b=this.a[a.slot||"__catchall"])&&b[0]);b?(b.__shady.assignedNodes.push(a),a.__shady.assignedSlot=b):a.__shady.assignedSlot=void 0;c!==a.__shady.assignedSlot&&a.__shady.assignedSlot&&(a.__shady.assignedSlot.__shady.sa=!0)};l.prototype.u=function(a){var b=a.__shady.assignedNodes;a.__shady.assignedNodes=
0;e<d.length&&(g=d[e]);e++)for(f=c[g.index],h=g.index;h<g.index+g.aa;h++)k=b[h],mb.call(a,k,f),c.splice(h,0,k)};l.prototype.$a=function(a){this.a=this.a||{};this.b=this.b||[];for(var b=0;b<a.length;b++){var c=a[b];c.__shady=c.__shady||{};sa(c);sa(c.parentNode);var d=this.i(c);if(this.a[d]){var e=e||{};e[d]=!0;this.a[d].push(c)}else this.a[d]=[c];this.b.push(c)}if(e)for(var f in e)this.a[f]=this.j(this.a[f])};l.prototype.i=function(a){var b=a.name||a.getAttribute("name")||"__catchall";return a.Ya= [];a.__shady.W=[];if(a.__shady.Ea=b)for(var c=0;c<b.length;c++){var d=b[c];d.__shady.oa=d.__shady.assignedSlot;d.__shady.assignedSlot===a&&(d.__shady.assignedSlot=null)}};l.prototype.g=function(a,b){for(var c=0,d;c<b.length&&(d=b[c]);c++)"slot"==d.localName?this.g(a,d.__shady.assignedNodes):a.push(b[c])};l.prototype.i=function(a){Gb.call(a,new Event("slotchange"));a.__shady.assignedSlot&&this.i(a.__shady.assignedSlot)};l.prototype.v=function(){for(var a=this.b,b=[],c=0;c<a.length;c++){var d=a[c].parentNode;
b};l.prototype.j=function(a){return a.sort(function(a,c){a=Wc(a);for(var b=Wc(c),e=0;e<a.length;e++){c=a[e];var f=b[e];if(c!==f)return a=Array.from(c.parentNode.childNodes),a.indexOf(c)-a.indexOf(f)}})};l.prototype.gb=function(a){this.a=this.a||{};this.b=this.b||[];var b=this.a,c;for(c in b)for(var d=b[c],e=0;e<d.length;e++){var f=d[e],g;a:{for(g=f;g;){if(g==a){g=!0;break a}g=g.parentNode}g=void 0}if(g){d.splice(e,1);var h=this.b.indexOf(f);0<=h&&this.b.splice(h,1);e--;this.H(f);h=!0}}return h};l.prototype.ib= d.__shady&&d.__shady.root||!(0>b.indexOf(d))||b.push(d)}for(a=0;a<b.length;a++)c=b[a],this.T(c===this?this.host:c,this.B(c))};l.prototype.B=function(a){var b=[];a=a.childNodes;for(var c=0;c<a.length;c++){var d=a[c];if(this.j(d)){d=d.__shady.W;for(var e=0;e<d.length;e++)b.push(d[e])}else b.push(d)}return b};l.prototype.j=function(a){return"slot"==a.localName};l.prototype.T=function(a,b){for(var c=X(a),d=De(b,b.length,c,c.length),e=0,f=0,h;e<d.length&&(h=d[e]);e++){for(var g=0,m;g<h.Z.length&&(m=h.Z[g]);g++)da(m)===
function(a){var b=a.Ya,c=this.i(a);if(c!==b){b=this.a[b];var d=b.indexOf(a);0<=d&&b.splice(d,1);b=this.a[c]||(this.a[c]=[]);b.push(a);1<b.length&&(this.a[c]=this.j(b))}};l.prototype.H=function(a){if(a=a.__shady.V)for(var b=0;b<a.length;b++){var c=a[b],d=aa(c);d&&ka.call(d,c)}};l.prototype.Aa=function(){return!!this.b.length};l.prototype.addEventListener=function(a,b,c){"object"!==typeof c&&(c={capture:!!c});c.ka=this;this.host.addEventListener(a,b,c)};l.prototype.removeEventListener=function(a,b, a&&oa.call(a,m),c.splice(h.index+f,1);f-=h.ba}for(e=0;e<d.length&&(h=d[e]);e++)for(f=c[h.index],g=h.index;g<h.index+h.ba;g++)m=b[g],sb.call(a,m,f),c.splice(g,0,m)};l.prototype.$a=function(a){this.c.push.apply(this.c,[].concat(a instanceof Array?a:Sd(Rd(a))))};l.prototype.f=function(){this.c.length&&(this.I(this.c),this.c=[])};l.prototype.I=function(a){for(var b,c=0;c<a.length;c++){var d=a[c];d.__shady=d.__shady||{};wa(d);wa(d.parentNode);var e=this.l(d);this.a[e]?(b=b||{},b[e]=!0,this.a[e].push(d)):
c){"object"!==typeof c&&(c={capture:!!c});c.ka=this;this.host.removeEventListener(a,b,c)};l.prototype.getElementById=function(a){return wa(this,function(b){return b.id==a},function(a){return!!a})[0]||null};(function(a){M(a,hb,!0);M(a,ib,!0)})(l.prototype);var we={addEventListener:Oc.bind(window),removeEventListener:Sc.bind(window)},ve={addEventListener:Oc,removeEventListener:Sc,appendChild:function(a){return jb(this,a)},insertBefore:function(a,b){return jb(this,a,b)},removeChild:function(a){return kb(this, this.a[e]=[d];this.b.push(d)}if(b)for(var f in b)this.a[f]=this.s(this.a[f])};l.prototype.l=function(a){var b=a.name||a.getAttribute("name")||"__catchall";return a.Ya=b};l.prototype.s=function(a){return a.sort(function(a,c){a=bd(a);for(var b=bd(c),e=0;e<a.length;e++){c=a[e];var f=b[e];if(c!==f)return a=Array.from(c.parentNode.childNodes),a.indexOf(c)-a.indexOf(f)}})};l.prototype.gb=function(a){this.f();var b=this.a,c;for(c in b)for(var d=b[c],e=0;e<d.length;e++){var f=d[e];if(qc(a,f)){d.splice(e,
a)},replaceChild:function(a,b){jb(this,a,b);kb(this,b);return a},cloneNode:function(a){if("template"==this.localName)var b=zb.call(this,a);else if(b=zb.call(this,!1),a){a=this.childNodes;for(var c=0,d;c<a.length;c++)d=a[c].cloneNode(!0),b.appendChild(d)}return b},getRootNode:function(){return Fc(this)},get isConnected(){var a=this.ownerDocument;if(a&&a.contains&&a.contains(this)||(a=a.documentElement)&&a.contains&&a.contains(this))return!0;for(a=this;a&&!(a instanceof Document);)a=a.parentNode||(a instanceof 1);var h=this.b.indexOf(f);0<=h&&this.b.splice(h,1);e--;this.K(f);h=!0}}return h};l.prototype.ib=function(a){var b=a.Ya,c=this.l(a);if(c!==b){b=this.a[b];var d=b.indexOf(a);0<=d&&b.splice(d,1);b=this.a[c]||(this.a[c]=[]);b.push(a);1<b.length&&(this.a[c]=this.s(b))}};l.prototype.K=function(a){if(a=a.__shady.W)for(var b=0;b<a.length;b++){var c=a[b],d=da(c);d&&oa.call(d,c)}};l.prototype.Ca=function(){this.f();return!!this.b.length};l.prototype.addEventListener=function(a,b,c){"object"!==typeof c&&(c=
l?a.host:void 0);return!!(a&&a instanceof Document)},dispatchEvent:function(a){va();return Ab.call(this,a)}},xe={get assignedSlot(){return Xc(this)}},rb={querySelector:function(a){return wa(this,function(b){return qd.call(b,a)},function(a){return!!a})[0]||null},querySelectorAll:function(a){return wa(this,function(b){return qd.call(b,a)})}},$c={assignedNodes:function(a){if("slot"===this.localName)return Hc(this),this.__shady?(a&&a.flatten?this.__shady.V:this.__shady.assignedNodes)||[]:[]}},Yc=cb({setAttribute:function(a, {capture:!!c});c.ma=this;this.host.addEventListener(a,b,c)};l.prototype.removeEventListener=function(a,b,c){"object"!==typeof c&&(c={capture:!!c});c.ma=this;this.host.removeEventListener(a,b,c)};l.prototype.getElementById=function(a){return Aa(this,function(b){return b.id==a},function(a){return!!a})[0]||null};(function(a){O(a,nb,!0);O(a,ob,!0)})(l.prototype);var He={addEventListener:Uc.bind(window),removeEventListener:Yc.bind(window)},Ge={addEventListener:Uc,removeEventListener:Yc,appendChild:function(a){return pb(this,
b){Ic(this,a,b)},removeAttribute:function(a){rd.call(this,a);Ec(this,a)},attachShadow:function(a){if(!this)throw"Must provide a host.";if(!a)throw"Not enough arguments.";return new l(Zb,this,a)},get slot(){return this.getAttribute("slot")},set slot(a){Ic(this,"slot",a)},get assignedSlot(){return Xc(this)}},rb,$c);Object.defineProperties(Yc,sd);var Zc=cb({importNode:function(a,b){return Kc(a,b)},getElementById:function(a){return wa(this,function(b){return b.id==a},function(a){return!!a})[0]||null}}, a)},insertBefore:function(a,b){return pb(this,a,b)},removeChild:function(a){return qb(this,a)},replaceChild:function(a,b){pb(this,a,b);qb(this,b);return a},cloneNode:function(a){if("template"==this.localName)var b=Fb.call(this,a);else if(b=Fb.call(this,!1),a){a=this.childNodes;for(var c=0,d;c<a.length;c++)d=a[c].cloneNode(!0),b.appendChild(d)}return b},getRootNode:function(){return Lc(this)},contains:function(a){return qc(this,a)},get isConnected(){var a=this.ownerDocument;if(Te&&pa.call(a,this)||
rb);Object.defineProperties(Zc,{_activeElement:ib.activeElement});var Oe=HTMLElement.prototype.blur,ye=cb({blur:function(){var a=this.__shady&&this.__shady.root;(a=a&&a.activeElement)?a.blur():Oe.call(this)}});D.Ha&&(window.ShadyDOM={inUse:D.Ha,patch:function(a){return a},isShadyRoot:I,enqueue:lc,flush:va,settings:D,filterMutations:le,observeChildren:Zd,unobserveChildren:Yd,nativeMethods:Ie,nativeTree:Je},window.Event=Le,window.CustomEvent=Me,window.MouseEvent=Ne,re(),ue(),window.ShadowRoot=l);var ze= a.documentElement&&pa.call(a.documentElement,this))return!0;for(a=this;a&&!(a instanceof Document);)a=a.parentNode||(a instanceof l?a.host:void 0);return!!(a&&a instanceof Document)},dispatchEvent:function(a){za();return Gb.call(this,a)}},Ie={get assignedSlot(){return cd(this)}},xb={querySelector:function(a){return Aa(this,function(b){return wd.call(b,a)},function(a){return!!a})[0]||null},querySelectorAll:function(a){return Aa(this,function(b){return wd.call(b,a)})}},fd={assignedNodes:function(a){if("slot"===
new Set("annotation-xml color-profile font-face font-face-src font-face-uri font-face-format font-face-name missing-glyph".split(" "));z.prototype.D=function(a,b){this.u.set(a,b);this.s.set(b.constructor,b)};z.prototype.c=function(a){return this.u.get(a)};z.prototype.C=function(a){return this.s.get(a)};z.prototype.v=function(a){this.h=!0;this.j.push(a)};z.prototype.l=function(a){var b=this;this.h&&P(a,function(a){return b.g(a)})};z.prototype.g=function(a){if(this.h&&!a.__CE_patched){a.__CE_patched= this.localName)return Nc(this),this.__shady?(a&&a.flatten?this.__shady.W:this.__shady.assignedNodes)||[]:[]}},dd=ib({setAttribute:function(a,b){Oc(this,a,b)},removeAttribute:function(a){xd.call(this,a);Kc(this,a)},attachShadow:function(a){if(!this)throw"Must provide a host.";if(!a)throw"Not enough arguments.";return new l(dc,this,a)},get slot(){return this.getAttribute("slot")},set slot(a){Oc(this,"slot",a)},get assignedSlot(){return cd(this)}},xb,fd);Object.defineProperties(dd,yd);var ed=ib({importNode:function(a,
!0;for(var b=0;b<this.j.length;b++)this.j[b](a)}};z.prototype.b=function(a){var b=[];P(a,function(a){return b.push(a)});for(a=0;a<b.length;a++){var c=b[a];1===c.__CE_state?this.connectedCallback(c):this.i(c)}};z.prototype.a=function(a){var b=[];P(a,function(a){return b.push(a)});for(a=0;a<b.length;a++){var c=b[a];1===c.__CE_state&&this.disconnectedCallback(c)}};z.prototype.f=function(a,b){var c=this;b=b?b:{};var d=b.yb||new Set,e=b.Na||function(a){return c.i(a)},f=[];P(a,function(a){if("link"===a.localName&& b){return Qc(a,b)},getElementById:function(a){return Aa(this,function(b){return b.id==a},function(a){return!!a})[0]||null}},xb);Object.defineProperties(ed,{_activeElement:ob.activeElement});var $e=HTMLElement.prototype.blur,Je=ib({blur:function(){var a=this.__shady&&this.__shady.root;(a=a&&a.activeElement)?a.blur():$e.call(this)}});G.Ja&&(window.ShadyDOM={inUse:G.Ja,patch:function(a){return a},isShadyRoot:J,enqueue:rc,flush:za,settings:G,filterMutations:we,observeChildren:je,unobserveChildren:ie,
"import"===a.getAttribute("rel")){var b=a.import;b instanceof Node&&"complete"===b.readyState?(b.__CE_isImportDocument=!0,b.__CE_hasRegistry=!0):a.addEventListener("load",function(){var b=a.import;b.__CE_documentLoadHandled||(b.__CE_documentLoadHandled=!0,b.__CE_isImportDocument=!0,b.__CE_hasRegistry=!0,d.delete(b),c.f(b,{yb:d,Na:e}))})}else f.push(a)},d);if(this.h)for(a=0;a<f.length;a++)this.g(f[a]);for(a=0;a<f.length;a++)e(f[a])};z.prototype.i=function(a){if(void 0===a.__CE_state){var b=this.c(a.localName); nativeMethods:Ue,nativeTree:Ve},window.Event=Xe,window.CustomEvent=Ye,window.MouseEvent=Ze,Ce(),Fe(),window.ShadowRoot=l);var Ke=new Set("annotation-xml color-profile font-face font-face-src font-face-uri font-face-format font-face-name missing-glyph".split(" "));B.prototype.D=function(a,b){this.u.set(a,b);this.s.set(b.constructor,b)};B.prototype.c=function(a){return this.u.get(a)};B.prototype.B=function(a){return this.s.get(a)};B.prototype.v=function(a){this.h=!0;this.j.push(a)};B.prototype.l=function(a){var b=
if(b){b.constructionStack.push(a);var c=b.constructor;try{try{if(new c!==a)throw Error("The custom element constructor did not produce the element being upgraded.");}finally{b.constructionStack.pop()}}catch(f){throw a.__CE_state=2,f;}a.__CE_state=1;a.__CE_definition=b;if(b.attributeChangedCallback)for(b=b.observedAttributes,c=0;c<b.length;c++){var d=b[c],e=a.getAttribute(d);null!==e&&this.attributeChangedCallback(a,d,null,e,null)}n(a)&&this.connectedCallback(a)}}};z.prototype.connectedCallback=function(a){var b= this;this.h&&S(a,function(a){return b.g(a)})};B.prototype.g=function(a){if(this.h&&!a.__CE_patched){a.__CE_patched=!0;for(var b=0;b<this.j.length;b++)this.j[b](a)}};B.prototype.b=function(a){var b=[];S(a,function(a){return b.push(a)});for(a=0;a<b.length;a++){var c=b[a];1===c.__CE_state?this.connectedCallback(c):this.i(c)}};B.prototype.a=function(a){var b=[];S(a,function(a){return b.push(a)});for(a=0;a<b.length;a++){var c=b[a];1===c.__CE_state&&this.disconnectedCallback(c)}};B.prototype.f=function(a,
a.__CE_definition;b.connectedCallback&&b.connectedCallback.call(a)};z.prototype.disconnectedCallback=function(a){var b=a.__CE_definition;b.disconnectedCallback&&b.disconnectedCallback.call(a)};z.prototype.attributeChangedCallback=function(a,b,c,d,e){var f=a.__CE_definition;f.attributeChangedCallback&&-1<f.observedAttributes.indexOf(b)&&f.attributeChangedCallback.call(a,b,c,d,e)};Ra.prototype.c=function(){this.N&&this.N.disconnect()};Ra.prototype.f=function(a){var b=this.a.readyState;"interactive"!== b){var c=this;b=b?b:{};var d=b.xb||new Set,e=b.Na||function(a){return c.i(a)},f=[];S(a,function(a){if("link"===a.localName&&"import"===a.getAttribute("rel")){var b=a.import;b instanceof Node&&"complete"===b.readyState?(b.__CE_isImportDocument=!0,b.__CE_hasRegistry=!0):a.addEventListener("load",function(){var b=a.import;if(!b.__CE_documentLoadHandled){b.__CE_documentLoadHandled=!0;b.__CE_isImportDocument=!0;b.__CE_hasRegistry=!0;var f=new Set(d);f.delete(b);c.f(b,{xb:f,Na:e})}})}else f.push(a)},d);
b&&"complete"!==b||this.c();for(b=0;b<a.length;b++)for(var c=a[b].addedNodes,d=0;d<c.length;d++)this.b.f(c[d])};Yb.prototype.resolve=function(a){if(this.a)throw Error("Already resolved.");this.a=a;this.b&&this.b(a)};x.prototype.define=function(a,b){var c=this;if(!(b instanceof Function))throw new TypeError("Custom element constructors must be functions.");if(!ad(a))throw new SyntaxError("The element name '"+a+"' is not valid.");if(this.a.c(a))throw Error("A custom element with name '"+a+"' has already been defined."); if(this.h)for(a=0;a<f.length;a++)this.g(f[a]);for(a=0;a<f.length;a++)e(f[a])};B.prototype.i=function(a){if(void 0===a.__CE_state){var b=this.c(a.localName);if(b){b.constructionStack.push(a);var c=b.constructor;try{try{if(new c!==a)throw Error("The custom element constructor did not produce the element being upgraded.");}finally{b.constructionStack.pop()}}catch(f){throw a.__CE_state=2,f;}a.__CE_state=1;a.__CE_definition=b;if(b.attributeChangedCallback)for(b=b.observedAttributes,c=0;c<b.length;c++){var d=
if(this.c)throw Error("A custom element is already being defined.");this.c=!0;try{var d=function(a){var b=e[a];if(void 0!==b&&!(b instanceof Function))throw Error("The '"+a+"' callback must be a function.");return b},e=b.prototype;if(!(e instanceof Object))throw new TypeError("The custom element constructor's prototype is not an object.");var f=d("connectedCallback");var g=d("disconnectedCallback");var h=d("adoptedCallback");var k=d("attributeChangedCallback");var l=b.observedAttributes||[]}catch(cf){return}finally{this.c= b[c],e=a.getAttribute(d);null!==e&&this.attributeChangedCallback(a,d,null,e,null)}u(a)&&this.connectedCallback(a)}}};B.prototype.connectedCallback=function(a){var b=a.__CE_definition;b.connectedCallback&&b.connectedCallback.call(a)};B.prototype.disconnectedCallback=function(a){var b=a.__CE_definition;b.disconnectedCallback&&b.disconnectedCallback.call(a)};B.prototype.attributeChangedCallback=function(a,b,c,d,e){var f=a.__CE_definition;f.attributeChangedCallback&&-1<f.observedAttributes.indexOf(b)&&
!1}b={localName:a,constructor:b,connectedCallback:f,disconnectedCallback:g,adoptedCallback:h,attributeChangedCallback:k,observedAttributes:l,constructionStack:[]};this.a.D(a,b);this.g.push(b);this.b||(this.b=!0,this.f(function(){return c.j()}))};x.prototype.j=function(){var a=this;if(!1!==this.b){this.b=!1;for(var b=this.g,c=[],d=new Map,e=0;e<b.length;e++)d.set(b[e].localName,[]);this.a.f(document,{Na:function(b){if(void 0===b.__CE_state){var e=b.localName,f=d.get(e);f?f.push(b):a.a.c(e)&&c.push(b)}}}); f.attributeChangedCallback.call(a,b,c,d,e)};Xa.prototype.c=function(){this.P&&this.P.disconnect()};Xa.prototype.f=function(a){var b=this.a.readyState;"interactive"!==b&&"complete"!==b||this.c();for(b=0;b<a.length;b++)for(var c=a[b].addedNodes,d=0;d<c.length;d++)this.b.f(c[d])};cc.prototype.resolve=function(a){if(this.a)throw Error("Already resolved.");this.a=a;this.b&&this.b(a)};y.prototype.define=function(a,b){var c=this;if(!(b instanceof Function))throw new TypeError("Custom element constructors must be functions.");
for(e=0;e<c.length;e++)this.a.i(c[e]);for(;0<b.length;){var f=b.shift();e=f.localName;f=d.get(f.localName);for(var g=0;g<f.length;g++)this.a.i(f[g]);(e=this.h.get(e))&&e.resolve(void 0)}}};x.prototype.get=function(a){if(a=this.a.c(a))return a.constructor};x.prototype.whenDefined=function(a){if(!ad(a))return Promise.reject(new SyntaxError("'"+a+"' is not a valid custom element name."));var b=this.h.get(a);if(b)return b.c;b=new Yb;this.h.set(a,b);this.a.c(a)&&!this.g.some(function(b){return b.localName=== if(!gd(a))throw new SyntaxError("The element name '"+a+"' is not valid.");if(this.a.c(a))throw Error("A custom element with name '"+a+"' has already been defined.");if(this.c)throw Error("A custom element is already being defined.");this.c=!0;try{var d=function(a){var b=e[a];if(void 0!==b&&!(b instanceof Function))throw Error("The '"+a+"' callback must be a function.");return b},e=b.prototype;if(!(e instanceof Object))throw new TypeError("The custom element constructor's prototype is not an object.");
a})&&b.resolve(void 0);return b.c};x.prototype.l=function(a){this.i.c();var b=this.f;this.f=function(c){return a(function(){return b(c)})}};window.CustomElementRegistry=x;x.prototype.define=x.prototype.define;x.prototype.get=x.prototype.get;x.prototype.whenDefined=x.prototype.whenDefined;x.prototype.polyfillWrapFlushCallback=x.prototype.l;var Na=window.Document.prototype.createElement,Td=window.Document.prototype.createElementNS,Sd=window.Document.prototype.importNode,Ud=window.Document.prototype.prepend, var f=d("connectedCallback");var g=d("disconnectedCallback");var k=d("adoptedCallback");var l=d("attributeChangedCallback");var q=b.observedAttributes||[]}catch(n){return}finally{this.c=!1}b={localName:a,constructor:b,connectedCallback:f,disconnectedCallback:g,adoptedCallback:k,attributeChangedCallback:l,observedAttributes:q,constructionStack:[]};this.a.D(a,b);this.g.push(b);this.b||(this.b=!0,this.f(function(){return c.j()}))};y.prototype.j=function(){var a=this;if(!1!==this.b){this.b=!1;for(var b=
Vd=window.Document.prototype.append,Nb=window.Node.prototype.cloneNode,qa=window.Node.prototype.appendChild,Vb=window.Node.prototype.insertBefore,Oa=window.Node.prototype.removeChild,Wb=window.Node.prototype.replaceChild,Qa=Object.getOwnPropertyDescriptor(window.Node.prototype,"textContent"),Mb=window.Element.prototype.attachShadow,La=Object.getOwnPropertyDescriptor(window.Element.prototype,"innerHTML"),Pa=window.Element.prototype.getAttribute,Ob=window.Element.prototype.setAttribute,Qb=window.Element.prototype.removeAttribute, this.g,c=[],d=new Map,e=0;e<b.length;e++)d.set(b[e].localName,[]);this.a.f(document,{Na:function(b){if(void 0===b.__CE_state){var e=b.localName,f=d.get(e);f?f.push(b):a.a.c(e)&&c.push(b)}}});for(e=0;e<c.length;e++)this.a.i(c[e]);for(;0<b.length;){var f=b.shift();e=f.localName;f=d.get(f.localName);for(var g=0;g<f.length;g++)this.a.i(f[g]);(e=this.h.get(e))&&e.resolve(void 0)}}};y.prototype.get=function(a){if(a=this.a.c(a))return a.constructor};y.prototype.whenDefined=function(a){if(!gd(a))return Promise.reject(new SyntaxError("'"+
ra=window.Element.prototype.getAttributeNS,Pb=window.Element.prototype.setAttributeNS,Rb=window.Element.prototype.removeAttributeNS,Tb=window.Element.prototype.insertAdjacentElement,Jd=window.Element.prototype.prepend,Kd=window.Element.prototype.append,Md=window.Element.prototype.before,Nd=window.Element.prototype.after,Od=window.Element.prototype.replaceWith,Pd=window.Element.prototype.remove,Xd=window.HTMLElement,Ma=Object.getOwnPropertyDescriptor(window.HTMLElement.prototype,"innerHTML"),Sb=window.HTMLElement.prototype.insertAdjacentElement, a+"' is not a valid custom element name."));var b=this.h.get(a);if(b)return b.c;b=new cc;this.h.set(a,b);this.a.c(a)&&!this.g.some(function(b){return b.localName===a})&&b.resolve(void 0);return b.c};y.prototype.l=function(a){this.i.c();var b=this.f;this.f=function(c){return a(function(){return b(c)})}};window.CustomElementRegistry=y;y.prototype.define=y.prototype.define;y.prototype.get=y.prototype.get;y.prototype.whenDefined=y.prototype.whenDefined;y.prototype.polyfillWrapFlushCallback=y.prototype.l;
Xb=new function(){},Da=window.customElements;if(!Da||Da.forcePolyfill||"function"!=typeof Da.define||"function"!=typeof Da.get){var na=new z;Wd(na);Rd(na);Qd(na);Id(na);document.__CE_hasRegistry=!0;var Pe=new x(na);Object.defineProperty(window,"customElements",{configurable:!0,enumerable:!0,value:Pe})}var L={STYLE_RULE:1,ha:7,MEDIA_RULE:4,ua:1E3},G={mb:/\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,port:/@import[^;]*;/gim,Ea:/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim,Ia:/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim, var Sa=window.Document.prototype.createElement,de=window.Document.prototype.createElementNS,ce=window.Document.prototype.importNode,ee=window.Document.prototype.prepend,fe=window.Document.prototype.append,af=window.DocumentFragment.prototype.prepend,bf=window.DocumentFragment.prototype.append,Tb=window.Node.prototype.cloneNode,ua=window.Node.prototype.appendChild,$b=window.Node.prototype.insertBefore,Ta=window.Node.prototype.removeChild,ac=window.Node.prototype.replaceChild,Wa=Object.getOwnPropertyDescriptor(window.Node.prototype,
sb:/@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim,xb:/[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,rb:/^@[^\s]*keyframes/,Ja:/\s+/g},q=!(window.ShadyDOM&&window.ShadyDOM.inUse);if(window.ShadyCSS&&void 0!==window.ShadyCSS.nativeCss)var v=window.ShadyCSS.nativeCss;else window.ShadyCSS?(dd(window.ShadyCSS),window.ShadyCSS=void 0):dd(window.WebComponents&&window.WebComponents.flags);var Ea=/(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};{])+)|\{([^}]*)\}(?:(?=[;\s}])|$))/gi, "textContent"),Sb=window.Element.prototype.attachShadow,Qa=Object.getOwnPropertyDescriptor(window.Element.prototype,"innerHTML"),Ua=window.Element.prototype.getAttribute,Ub=window.Element.prototype.setAttribute,Wb=window.Element.prototype.removeAttribute,va=window.Element.prototype.getAttributeNS,Vb=window.Element.prototype.setAttributeNS,Xb=window.Element.prototype.removeAttributeNS,Zb=window.Element.prototype.insertAdjacentElement,Ud=window.Element.prototype.prepend,Vd=window.Element.prototype.append,
Fa=/(?:^|\W+)@apply\s*\(?([^);\n]*)\)?/gi,Qe=/(--[\w-]+)\s*([:,;)]|$)/gi,Re=/(animation\s*:)|(animation-name\s*:)/,Be=/@media\s(.*)/,Se=/\{[^}]*\}/g,S=null;r.prototype.a=function(a,b,c){a.__styleScoped?a.__styleScoped=null:this.i(a,b||"",c)};r.prototype.i=function(a,b,c){a.nodeType===Node.ELEMENT_NODE&&this.C(a,b,c);if(a="template"===a.localName?(a.content||a.Cb).childNodes:a.children||a.childNodes)for(var d=0;d<a.length;d++)this.i(a[d],b,c)};r.prototype.C=function(a,b,c){if(b)if(a.classList)c?(a.classList.remove("style-scope"), Xd=window.Element.prototype.before,Yd=window.Element.prototype.after,Zd=window.Element.prototype.replaceWith,$d=window.Element.prototype.remove,he=window.HTMLElement,Ra=Object.getOwnPropertyDescriptor(window.HTMLElement.prototype,"innerHTML"),Yb=window.HTMLElement.prototype.insertAdjacentElement,bc=new function(){},Ha=window.customElements;if(!Ha||Ha.forcePolyfill||"function"!=typeof Ha.define||"function"!=typeof Ha.get){var ia=new B;ge(ia);be(ia);Va(ia,DocumentFragment.prototype,{ea:af,append:bf});
a.classList.remove(b)):(a.classList.add("style-scope"),a.classList.add(b));else if(a.getAttribute){var d=a.getAttribute(Te);c?d&&(b=d.replace("style-scope","").replace(b,""),za(a,b)):za(a,(d?d+" ":"")+"style-scope "+b)}};r.prototype.b=function(a,b,c){var d=a.__cssBuild;q||"shady"===d?b=ba(b,c):(a=T(a),b=this.I(b,a.is,a.Z,c)+"\n\n");return b.trim()};r.prototype.I=function(a,b,c,d){var e=this.f(b,c);b=this.h(b);var f=this;return ba(a,function(a){a.c||(f.S(a,b,e),a.c=!0);d&&d(a,b,e)})};r.prototype.h= ae(ia);Td(ia);document.__CE_hasRegistry=!0;var cf=new y(ia);Object.defineProperty(window,"customElements",{configurable:!0,enumerable:!0,value:cf})}var M={STYLE_RULE:1,ja:7,MEDIA_RULE:4,wa:1E3},I={lb:/\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,port:/@import[^;]*;/gim,Ga:/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim,Ka:/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim,rb:/@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim,wb:/[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,qb:/^@[^\s]*keyframes/,La:/\s+/g},
function(a){return a?Ue+a:""};r.prototype.f=function(a,b){return b?"[is="+a+"]":a};r.prototype.S=function(a,b,c){this.j(a,this.g,b,c)};r.prototype.j=function(a,b,c,d){a.selector=a.A=this.l(a,b,c,d)};r.prototype.l=function(a,b,c,d){var e=a.selector.split(td);if(!ed(a)){a=0;for(var f=e.length,g;a<f&&(g=e[a]);a++)e[a]=b.call(this,g,c,d)}return e.join(td)};r.prototype.u=function(a){return a.replace(Db,function(a,c,d){-1<d.indexOf("+")?d=d.replace(/\+/g,"___"):-1<d.indexOf("___")&&(d=d.replace(/___/g, z=!(window.ShadyDOM&&window.ShadyDOM.inUse);if(window.ShadyCSS&&void 0!==window.ShadyCSS.nativeCss)var A=window.ShadyCSS.nativeCss;else window.ShadyCSS?(jd(window.ShadyCSS),window.ShadyCSS=void 0):jd(window.WebComponents&&window.WebComponents.flags);var Ia=/(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};{])+)|\{([^}]*)\}(?:(?=[;\s}])|$))/gi,Ja=/(?:^|\W+)@apply\s*\(?([^);\n]*)\)?/gi,df=/(--[\w-]+)\s*([:,;)]|$)/gi,ef=/(animation\s*:)|(animation-name\s*:)/,Me=/@media\s(.*)/,
"+"));return":"+c+"("+d+")"})};r.prototype.g=function(a,b,c){var d=this,e=!1;a=a.trim();var f=Db.test(a);f&&(a=a.replace(Db,function(a,b,c){return":"+b+"("+c.replace(/\s/g,"")+")"}),a=this.u(a));a=a.replace(Ve,Eb+" $1");a=a.replace(We,function(a,f,k){e||(a=d.D(k,f,b,c),e=e||a.stop,f=a.lb,k=a.value);return f+k});f&&(a=this.u(a));return a};r.prototype.D=function(a,b,c,d){var e=a.indexOf(Fb);0<=a.indexOf(Eb)?a=this.H(a,d):0!==e&&(a=c?this.s(a,c):a);c=!1;0<=e&&(b="",c=!0);if(c){var f=!0;c&&(a=a.replace(Xe, ff=/\{[^}]*\}/g,V=null;v.prototype.b=function(a,b,c){a.__styleScoped?a.__styleScoped=null:this.j(a,b||"",c)};v.prototype.j=function(a,b,c){a.nodeType===Node.ELEMENT_NODE&&this.h(a,b,c);if(a="template"===a.localName?(a.content||a.Cb).childNodes:a.children||a.childNodes)for(var d=0;d<a.length;d++)this.j(a[d],b,c)};v.prototype.h=function(a,b,c){if(b)if(a.classList)c?(a.classList.remove("style-scope"),a.classList.remove(b)):(a.classList.add("style-scope"),a.classList.add(b));else if(a.getAttribute){var d=
function(a,b){return" > "+b}))}a=a.replace(Ye,function(a,b,c){return'[dir="'+c+'"] '+b+", "+b+'[dir="'+c+'"]'});return{value:a,lb:b,stop:f}};r.prototype.s=function(a,b){a=a.split(ud);a[0]+=b;return a.join(ud)};r.prototype.H=function(a,b){var c=a.match(vd);return(c=c&&c[2].trim()||"")?c[0].match(wd)?a.replace(vd,function(a,c,f){return b+f}):c.split(wd)[0]===b?c:Ze:a.replace(Eb,b)};r.prototype.R=function(a){a.selector=a.parsedSelector;this.v(a);this.j(a,this.L)};r.prototype.v=function(a){a.selector=== a.getAttribute(gf);c?d&&(b=d.replace("style-scope","").replace(b,""),Da(a,b)):Da(a,(d?d+" ":"")+"style-scope "+b)}};v.prototype.c=function(a,b,c){var d=a.__cssBuild;z||"shady"===d?b=ea(b,c):(a=W(a),b=this.I(b,a.is,a.$,c)+"\n\n");return b.trim()};v.prototype.I=function(a,b,c,d){var e=this.f(b,c);b=this.i(b);var f=this;return ea(a,function(a){a.c||(f.K(a,b,e),a.c=!0);d&&d(a,b,e)})};v.prototype.i=function(a){return a?hf+a:""};v.prototype.f=function(a,b){return b?"[is="+a+"]":a};v.prototype.K=function(a,
$e&&(a.selector="html")};r.prototype.L=function(a){return a.match(Fb)?this.g(a,xd):this.s(a.trim(),xd)};Jb.Object.defineProperties(r.prototype,{c:{configurable:!0,enumerable:!0,get:function(){return"style-scope"}}});var Db=/:(nth[-\w]+)\(([^)]+)\)/,xd=":not(.style-scope)",td=",",We=/(^|[\s>+~]+)((?:\[.+?\]|[^\s>+~=[])+)/g,wd=/[[.:#*]/,Eb=":host",$e=":root",Fb="::slotted",Ve=new RegExp("^("+Fb+")"),vd=/(:host)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/,Xe=/(?:::slotted)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/,Ye= b,c){this.l(a,this.g,b,c)};v.prototype.l=function(a,b,c,d){a.selector=a.A=this.s(a,b,c,d)};v.prototype.s=function(a,b,c,d){var e=a.selector.split(zd);if(!kd(a)){a=0;for(var f=e.length,g;a<f&&(g=e[a]);a++)e[a]=b.call(this,g,c,d)}return e.join(zd)};v.prototype.v=function(a){return a.replace(Jb,function(a,c,d){-1<d.indexOf("+")?d=d.replace(/\+/g,"___"):-1<d.indexOf("___")&&(d=d.replace(/___/g,"+"));return":"+c+"("+d+")"})};v.prototype.g=function(a,b,c){var d=this,e=!1;a=a.trim();var f=Jb.test(a);f&&
/(.*):dir\((?:(ltr|rtl))\)/,Ue=".",ud=":",Te="class",Ze="should_not_match",w=new r;t.get=function(a){return a?a.__styleInfo:null};t.set=function(a,b){return a.__styleInfo=b};t.prototype.c=function(){return this.G};t.prototype._getStyleRules=t.prototype.c;var yd=function(a){return a.matches||a.matchesSelector||a.mozMatchesSelector||a.msMatchesSelector||a.oMatchesSelector||a.webkitMatchesSelector}(window.Element.prototype),af=navigator.userAgent.match("Trident");p.prototype.R=function(a){var b=this, (a=a.replace(Jb,function(a,b,c){return":"+b+"("+c.replace(/\s/g,"")+")"}),a=this.v(a));a=a.replace(jf,Kb+" $1");a=a.replace(kf,function(a,f,g){e||(a=d.D(g,f,b,c),e=e||a.stop,f=a.kb,g=a.value);return f+g});f&&(a=this.v(a));return a};v.prototype.D=function(a,b,c,d){var e=a.indexOf(Lb);0<=a.indexOf(Kb)?a=this.H(a,d):0!==e&&(a=c?this.u(a,c):a);c=!1;0<=e&&(b="",c=!0);if(c){var f=!0;c&&(a=a.replace(lf,function(a,b){return" > "+b}))}a=a.replace(mf,function(a,b,c){return'[dir="'+c+'"] '+b+", "+b+'[dir="'+
c={},d=[],e=0;ca(a,function(a){b.c(a);a.index=e++;b.I(a.w.cssText,c)},function(a){d.push(a)});a.b=d;a=[];for(var f in c)a.push(f);return a};p.prototype.c=function(a){if(!a.w){var b={},c={};this.b(a,c)&&(b.F=c,a.rules=null);b.cssText=this.H(a);a.w=b}};p.prototype.b=function(a,b){var c=a.w;if(c){if(c.F)return Object.assign(b,c.F),!0}else{c=a.parsedCssText;for(var d;a=Ea.exec(c);){d=(a[2]||a[3]).trim();if("inherit"!==d||"unset"!==d)b[a[1].trim()]=d;d=!0}return d}};p.prototype.H=function(a){return this.L(a.parsedCssText)}; c+'"]'});return{value:a,kb:b,stop:f}};v.prototype.u=function(a,b){a=a.split(Ad);a[0]+=b;return a.join(Ad)};v.prototype.H=function(a,b){var c=a.match(Bd);return(c=c&&c[2].trim()||"")?c[0].match(Cd)?a.replace(Bd,function(a,c,f){return b+f}):c.split(Cd)[0]===b?c:nf:a.replace(Kb,b)};v.prototype.J=function(a){a.selector=a.parsedSelector;this.B(a);this.l(a,this.N)};v.prototype.B=function(a){a.selector===of&&(a.selector="html")};v.prototype.N=function(a){return a.match(Lb)?this.g(a,Dd):this.u(a.trim(),Dd)};
p.prototype.L=function(a){return a.replace(Se,"").replace(Ea,"")};p.prototype.I=function(a,b){for(var c;c=Qe.exec(a);){var d=c[1];":"!==c[2]&&(b[d]=!0)}};p.prototype.ea=function(a){for(var b=Object.getOwnPropertyNames(a),c=0,d;c<b.length;c++)d=b[c],a[d]=this.a(a[d],a)};p.prototype.a=function(a,b){if(a)if(0<=a.indexOf(";"))a=this.f(a,b);else{var c=this;a=gd(a,function(a,e,f,g){if(!e)return a+g;(e=c.a(b[e],b))&&"initial"!==e?"apply-shim-inherit"===e&&(e="inherit"):e=c.a(b[f]||f,b)||f;return a+(e||"")+ N.Object.defineProperties(v.prototype,{a:{configurable:!0,enumerable:!0,get:function(){return"style-scope"}}});var Jb=/:(nth[-\w]+)\(([^)]+)\)/,Dd=":not(.style-scope)",zd=",",kf=/(^|[\s>+~]+)((?:\[.+?\]|[^\s>+~=[])+)/g,Cd=/[[.:#*]/,Kb=":host",of=":root",Lb="::slotted",jf=new RegExp("^("+Lb+")"),Bd=/(:host)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/,lf=/(?:::slotted)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/,mf=/(.*):dir\((?:(ltr|rtl))\)/,hf=".",Ad=":",gf="class",nf="should_not_match",r=new v;w.get=function(a){return a?
g})}return a&&a.trim()||""};p.prototype.f=function(a,b){a=a.split(";");for(var c=0,d,e;c<a.length;c++)if(d=a[c]){Fa.lastIndex=0;if(e=Fa.exec(d))d=this.a(b[e[1]],b);else if(e=d.indexOf(":"),-1!==e){var f=d.substring(e);f=f.trim();f=this.a(f,b)||f;d=d.substring(0,e)+f}a[c]=d&&d.lastIndexOf(";")===d.length-1?d.slice(0,-1):d||""}return a.join(";")};p.prototype.D=function(a,b){var c="";a.w||this.c(a);a.w.cssText&&(c=this.f(a.w.cssText,b));a.cssText=c};p.prototype.C=function(a,b){var c=a.cssText,d=a.cssText; a.__styleInfo:null};w.set=function(a,b){return a.__styleInfo=b};w.prototype.c=function(){return this.G};w.prototype._getStyleRules=w.prototype.c;var Ed=function(a){return a.matches||a.matchesSelector||a.mozMatchesSelector||a.msMatchesSelector||a.oMatchesSelector||a.webkitMatchesSelector}(window.Element.prototype),pf=navigator.userAgent.match("Trident");p.prototype.J=function(a){var b=this,c={},d=[],e=0;fa(a,function(a){b.c(a);a.index=e++;b.I(a.w.cssText,c)},function(a){d.push(a)});a.b=d;a=[];for(var f in c)a.push(f);
null==a.Ga&&(a.Ga=Re.test(c));if(a.Ga)if(null==a.ca){a.ca=[];for(var e in b)d=b[e],d=d(c),c!==d&&(c=d,a.ca.push(e))}else{for(e=0;e<a.ca.length;++e)d=b[a.ca[e]],c=d(c);d=c}a.cssText=d};p.prototype.da=function(a,b){var c={},d=this,e=[];ca(a,function(a){a.w||d.c(a);var f=a.A||a.parsedSelector;b&&a.w.F&&f&&yd.call(b,f)&&(d.b(a,c),a=a.index,f=parseInt(a/32,10),e[f]=(e[f]||0)|1<<a%32)},null,!0);return{F:c,key:e}};p.prototype.ga=function(a,b,c,d){b.w||this.c(b);if(b.w.F){var e=T(a);a=e.is;e=e.Z;e=a?w.f(a, return a};p.prototype.c=function(a){if(!a.w){var b={},c={};this.b(a,c)&&(b.F=c,a.rules=null);b.cssText=this.H(a);a.w=b}};p.prototype.b=function(a,b){var c=a.w;if(c){if(c.F)return Object.assign(b,c.F),!0}else{c=a.parsedCssText;for(var d;a=Ia.exec(c);){d=(a[2]||a[3]).trim();if("inherit"!==d||"unset"!==d)b[a[1].trim()]=d;d=!0}return d}};p.prototype.H=function(a){return this.N(a.parsedCssText)};p.prototype.N=function(a){return a.replace(ff,"").replace(Ia,"")};p.prototype.I=function(a,b){for(var c;c=df.exec(a);){var d=
e):"html";var f=b.parsedSelector,g=":host > *"===f||"html"===f,h=0===f.indexOf(":host")&&!g;"shady"===c&&(g=f===e+" > *."+e||-1!==f.indexOf("html"),h=!g&&0===f.indexOf(e));"shadow"===c&&(g=":host > *"===f||"html"===f,h=h&&!g);if(g||h)c=e,h&&(q&&!b.A&&(b.A=w.l(b,w.g,w.h(a),e)),c=b.A||e),d({wb:c,qb:h,Fb:g})}};p.prototype.S=function(a,b){var c={},d={},e=this,f=b&&b.__cssBuild;ca(b,function(b){e.ga(a,b,f,function(f){yd.call(a.Db||a,f.wb)&&(f.qb?e.b(b,c):e.b(b,d))})},null,!0);return{ub:d,pb:c}};p.prototype.fa= c[1];":"!==c[2]&&(b[d]=!0)}};p.prototype.ga=function(a){for(var b=Object.getOwnPropertyNames(a),c=0,d;c<b.length;c++)d=b[c],a[d]=this.a(a[d],a)};p.prototype.a=function(a,b){if(a)if(0<=a.indexOf(";"))a=this.f(a,b);else{var c=this;a=md(a,function(a,e,f,g){if(!e)return a+g;(e=c.a(b[e],b))&&"initial"!==e?"apply-shim-inherit"===e&&(e="inherit"):e=c.a(b[f]||f,b)||f;return a+(e||"")+g})}return a&&a.trim()||""};p.prototype.f=function(a,b){a=a.split(";");for(var c=0,d,e;c<a.length;c++)if(d=a[c]){Ja.lastIndex=
function(a,b,c){var d=this,e=T(a),f=w.f(e.is,e.Z),g=new RegExp("(?:^|[^.#[:])"+(a.extends?"\\"+f.slice(0,-1)+"\\]":f)+"($|[.:[\\s>+~])");e=t.get(a).G;var h=this.h(e,c);return w.b(a,e,function(a){d.D(a,b);q||ed(a)||!a.cssText||(d.C(a,h),d.l(a,g,f,c))})};p.prototype.h=function(a,b){a=a.b;var c={};if(!q&&a)for(var d=0,e=a[d];d<a.length;e=a[++d])this.j(e,b),c[e.keyframesName]=this.i(e);return c};p.prototype.i=function(a){return function(b){return b.replace(a.f,a.a)}};p.prototype.j=function(a,b){a.f=new RegExp(a.keyframesName, 0;if(e=Ja.exec(d))d=this.a(b[e[1]],b);else if(e=d.indexOf(":"),-1!==e){var f=d.substring(e);f=f.trim();f=this.a(f,b)||f;d=d.substring(0,e)+f}a[c]=d&&d.lastIndexOf(";")===d.length-1?d.slice(0,-1):d||""}return a.join(";")};p.prototype.D=function(a,b){var c="";a.w||this.c(a);a.w.cssText&&(c=this.f(a.w.cssText,b));a.cssText=c};p.prototype.B=function(a,b){var c=a.cssText,d=a.cssText;null==a.Ia&&(a.Ia=ef.test(c));if(a.Ia)if(null==a.da){a.da=[];for(var e in b)d=b[e],d=d(c),c!==d&&(c=d,a.da.push(e))}else{for(e=
"g");a.a=a.keyframesName+"-"+b;a.A=a.A||a.selector;a.selector=a.A.replace(a.keyframesName,a.a)};p.prototype.l=function(a,b,c,d){a.A=a.A||a.selector;d="."+d;for(var e=a.A.split(","),f=0,g=e.length,h;f<g&&(h=e[f]);f++)e[f]=h.match(b)?h.replace(c,d):d+" "+h;a.selector=e.join(",")};p.prototype.u=function(a,b,c){var d=a.getAttribute("class")||"",e=d;c&&(e=d.replace(new RegExp("\\s*x-scope\\s*"+c+"\\s*","g")," "));e+=(e?" ":"")+"x-scope "+b;d!==e&&za(a,e)};p.prototype.v=function(a,b,c,d){b=d?d.textContent|| 0;e<a.da.length;++e)d=b[a.da[e]],c=d(c);d=c}a.cssText=d};p.prototype.T=function(a,b){var c={},d=this,e=[];fa(a,function(a){a.w||d.c(a);var f=a.A||a.parsedSelector;b&&a.w.F&&f&&Ed.call(b,f)&&(d.b(a,c),a=a.index,f=parseInt(a/32,10),e[f]=(e[f]||0)|1<<a%32)},null,!0);return{F:c,key:e}};p.prototype.ia=function(a,b,c,d){b.w||this.c(b);if(b.w.F){var e=W(a);a=e.is;e=e.$;e=a?r.f(a,e):"html";var f=b.parsedSelector,g=":host > *"===f||"html"===f,k=0===f.indexOf(":host")&&!g;"shady"===c&&(g=f===e+" > *."+e||-1!==
"":this.fa(a,b,c);var e=t.get(a),f=e.a;f&&!q&&f!==d&&(f._useCount--,0>=f._useCount&&f.parentNode&&f.parentNode.removeChild(f));q?e.a?(e.a.textContent=b,d=e.a):b&&(d=ub(b,c,a.shadowRoot,e.b)):d?d.parentNode||(af&&-1<b.indexOf("@media")&&(d.textContent=b),fd(d,null,e.b)):b&&(d=ub(b,c,null,e.b));d&&(d._useCount=d._useCount||0,e.a!=d&&d._useCount++,e.a=d);return d};p.prototype.s=function(a,b){var c=ya(a),d=this;a.textContent=ba(c,function(a){var c=a.cssText=a.parsedCssText;a.w&&a.w.cssText&&(c=c.replace(G.Ea, f.indexOf("html"),k=!g&&0===f.indexOf(e));"shadow"===c&&(g=":host > *"===f||"html"===f,k=k&&!g);if(g||k)c=e,k&&(z&&!b.A&&(b.A=r.s(b,r.g,r.i(a),e)),c=b.A||e),d({vb:c,pb:k,Gb:g})}};p.prototype.K=function(a,b){var c={},d={},e=this,f=b&&b.__cssBuild;fa(b,function(b){e.ia(a,b,f,function(f){Ed.call(a.Db||a,f.vb)&&(f.pb?e.b(b,c):e.b(b,d))})},null,!0);return{tb:d,ob:c}};p.prototype.ha=function(a,b,c){var d=this,e=W(a),f=r.f(e.is,e.$),g=new RegExp("(?:^|[^.#[:])"+(a.extends?"\\"+f.slice(0,-1)+"\\]":f)+"($|[.:[\\s>+~])");
"").replace(G.Ia,""),a.cssText=d.f(c,b))})};Jb.Object.defineProperties(p.prototype,{g:{configurable:!0,enumerable:!0,get:function(){return"x-scope"}}});var O=new p,Gb={},Ga=window.customElements;if(Ga&&!q){var bf=Ga.define;Ga.define=function(a,b,c){var d=document.createComment(" Shady DOM styles for "+a+" "),e=document.head;e.insertBefore(d,(S?S.nextSibling:null)||e.firstChild);S=d;Gb[a]=d;return bf.call(Ga,a,b,c)}}pa.prototype.a=function(a,b,c){for(var d=0;d<c.length;d++){var e=c[d];if(a.F[e]!== e=w.get(a).G;var k=this.h(e,c);return r.c(a,e,function(a){d.D(a,b);z||kd(a)||!a.cssText||(d.B(a,k),d.l(a,g,f,c))})};p.prototype.h=function(a,b){a=a.b;var c={};if(!z&&a)for(var d=0,e=a[d];d<a.length;e=a[++d])this.j(e,b),c[e.keyframesName]=this.i(e);return c};p.prototype.i=function(a){return function(b){return b.replace(a.f,a.a)}};p.prototype.j=function(a,b){a.f=new RegExp(a.keyframesName,"g");a.a=a.keyframesName+"-"+b;a.A=a.A||a.selector;a.selector=a.A.replace(a.keyframesName,a.a)};p.prototype.l=function(a,
b[e])return!1}return!0};pa.prototype.b=function(a,b,c,d){var e=this.cache[a]||[];e.push({F:b,styleElement:c,B:d});e.length>this.c&&e.shift();this.cache[a]=e};pa.prototype.fetch=function(a,b,c){if(a=this.cache[a])for(var d=a.length-1;0<=d;d--){var e=a[d];if(this.a(e,b,c))return e}};if(!q){var zd=new MutationObserver(hd),Ad=function(a){zd.observe(a,{childList:!0,subtree:!0})};if(window.customElements&&!window.customElements.polyfillWrapFlushCallback)Ad(document);else{var Hb=function(){Ad(document.body)}; b,c,d){a.A=a.A||a.selector;d="."+d;for(var e=a.A.split(","),f=0,g=e.length,k;f<g&&(k=e[f]);f++)e[f]=k.match(b)?k.replace(c,d):d+" "+k;a.selector=e.join(",")};p.prototype.u=function(a,b,c){var d=a.getAttribute("class")||"",e=d;c&&(e=d.replace(new RegExp("\\s*x-scope\\s*"+c+"\\s*","g")," "));e+=(e?" ":"")+"x-scope "+b;d!==e&&Da(a,e)};p.prototype.v=function(a,b,c,d){b=d?d.textContent||"":this.ha(a,b,c);var e=w.get(a),f=e.a;f&&!z&&f!==d&&(f._useCount--,0>=f._useCount&&f.parentNode&&f.parentNode.removeChild(f));
window.HTMLImports?window.HTMLImports.whenReady(Hb):requestAnimationFrame(function(){if("loading"===document.readyState){var a=function(){Hb();document.removeEventListener("readystatechange",a)};document.addEventListener("readystatechange",a)}else Hb()})}Lb=function(){hd(zd.takeRecords())}}var Aa={},Ee=Promise.resolve(),vb=null,jd=window.HTMLImports&&window.HTMLImports.whenReady||null,wb,Ha=null,oa=null;F.prototype.Fa=function(){!this.enqueued&&oa&&(this.enqueued=!0,Kb(oa))};F.prototype.b=function(a){a.__seenByShadyCSS|| z?e.a?(e.a.textContent=b,d=e.a):b&&(d=Ab(b,c,a.shadowRoot,e.b)):d?d.parentNode||(pf&&-1<b.indexOf("@media")&&(d.textContent=b),ld(d,null,e.b)):b&&(d=Ab(b,c,null,e.b));d&&(d._useCount=d._useCount||0,e.a!=d&&d._useCount++,e.a=d);return d};p.prototype.s=function(a,b){var c=Ca(a),d=this;a.textContent=ea(c,function(a){var c=a.cssText=a.parsedCssText;a.w&&a.w.cssText&&(c=c.replace(I.Ga,"").replace(I.Ka,""),a.cssText=d.f(c,b))})};N.Object.defineProperties(p.prototype,{g:{configurable:!0,enumerable:!0,get:function(){return"x-scope"}}});
(a.__seenByShadyCSS=!0,this.customStyles.push(a),this.Fa())};F.prototype.a=function(a){return a.__shadyCSSCachedStyle?a.__shadyCSSCachedStyle:a.getStyle?a.getStyle():a};F.prototype.c=function(){for(var a=this.customStyles,b=0;b<a.length;b++){var c=a[b];if(!c.__shadyCSSCachedStyle){var d=this.a(c);d&&(d=d.__appliedElement||d,Ha&&Ha(d),c.__shadyCSSCachedStyle=d)}}return a};F.prototype.addCustomStyle=F.prototype.b;F.prototype.getStyleForCustomStyle=F.prototype.a;F.prototype.processStyles=F.prototype.c; var Q=new p,Mb={},Ka=window.customElements;if(Ka&&!z){var qf=Ka.define;Ka.define=function(a,b,c){var d=document.createComment(" Shady DOM styles for "+a+" "),e=document.head;e.insertBefore(d,(V?V.nextSibling:null)||e.firstChild);V=d;Mb[a]=d;return qf.call(Ka,a,b,c)}}ta.prototype.a=function(a,b,c){for(var d=0;d<c.length;d++){var e=c[d];if(a.F[e]!==b[e])return!1}return!0};ta.prototype.b=function(a,b,c,d){var e=this.cache[a]||[];e.push({F:b,styleElement:c,C:d});e.length>this.c&&e.shift();this.cache[a]=
Object.defineProperties(F.prototype,{transformCallback:{get:function(){return Ha},set:function(a){Ha=a}},validateCallback:{get:function(){return oa},set:function(a){var b=!1;oa||(b=!0);oa=a;b&&this.Fa()}}});var Bd=new pa;k.prototype.C=function(){Lb()};k.prototype.S=function(a){var b=this.s[a]=(this.s[a]||0)+1;return a+"-"+b};k.prototype.Ra=function(a){return ya(a)};k.prototype.Ta=function(a){return ba(a)};k.prototype.R=function(a){a=a.content.querySelectorAll("style");for(var b=[],c=0;c<a.length;c++){var d= e};ta.prototype.fetch=function(a,b,c){if(a=this.cache[a])for(var d=a.length-1;0<=d;d--){var e=a[d];if(this.a(e,b,c))return e}};if(!z){var Fd=new MutationObserver(nd),Gd=function(a){Fd.observe(a,{childList:!0,subtree:!0})};if(window.customElements&&!window.customElements.polyfillWrapFlushCallback)Gd(document);else{var Nb=function(){Gd(document.body)};window.HTMLImports?window.HTMLImports.whenReady(Nb):requestAnimationFrame(function(){if("loading"===document.readyState){var a=function(){Nb();document.removeEventListener("readystatechange",
a[c];b.push(d.textContent);d.parentNode.removeChild(d)}return b.join("").trim()};k.prototype.ea=function(a){return(a=a.content.querySelector("style"))?a.getAttribute("css-build")||"":""};k.prototype.prepareTemplate=function(a,b,c){if(!a.f){a.f=!0;a.name=b;a.extends=c;Aa[b]=a;var d=this.ea(a),e=this.R(a);c={is:b,extends:c,Ab:d};q||w.a(a.content,b);this.c();var f=Fa.test(e)||Ea.test(e);Fa.lastIndex=0;Ea.lastIndex=0;e=tb(e);f&&v&&this.a&&this.a.transformRules(e,b);a._styleAst=e;a.g=d;d=[];v||(d=O.R(a._styleAst)); a)};document.addEventListener("readystatechange",a)}else Nb()})}R=function(){nd(Fd.takeRecords())}}var Ea={},Pe=Promise.resolve(),Bb=null,pd=window.HTMLImports&&window.HTMLImports.whenReady||null,Cb,La=null,sa=null;t.prototype.Ha=function(){!this.enqueued&&sa&&(this.enqueued=!0,Rb(sa))};t.prototype.b=function(a){a.__seenByShadyCSS||(a.__seenByShadyCSS=!0,this.customStyles.push(a),this.Ha())};t.prototype.a=function(a){return a.__shadyCSSCachedStyle?a.__shadyCSSCachedStyle:a.getStyle?a.getStyle():a};
if(!d.length||v)b=this.da(c,a._styleAst,q?a.content:null,Gb[b]),a.a=b;a.c=d}};k.prototype.da=function(a,b,c,d){b=w.b(a,b);if(b.length)return ub(b,a.is,c,d)};k.prototype.ga=function(a){var b=T(a),c=b.is;b=b.Z;var d=Gb[c];c=Aa[c];if(c){var e=c._styleAst;var f=c.c}return t.set(a,new t(e,d,f,0,b))};k.prototype.H=function(){!this.a&&window.ShadyCSS&&window.ShadyCSS.ApplyShim&&(this.a=window.ShadyCSS.ApplyShim,this.a.invalidCallback=Ce)};k.prototype.I=function(){var a=this;!this.b&&window.ShadyCSS&&window.ShadyCSS.CustomStyleInterface&& t.prototype.c=function(){for(var a=this.customStyles,b=0;b<a.length;b++){var c=a[b];if(!c.__shadyCSSCachedStyle){var d=this.a(c);d&&(d=d.__appliedElement||d,La&&La(d),c.__shadyCSSCachedStyle=d)}}return a};t.prototype.addCustomStyle=t.prototype.b;t.prototype.getStyleForCustomStyle=t.prototype.a;t.prototype.processStyles=t.prototype.c;Object.defineProperties(t.prototype,{transformCallback:{get:function(){return La},set:function(a){La=a}},validateCallback:{get:function(){return sa},set:function(a){var b=
(this.b=window.ShadyCSS.CustomStyleInterface,this.b.transformCallback=function(b){a.v(b)},this.b.validateCallback=function(){requestAnimationFrame(function(){(a.b.enqueued||a.i)&&a.f()})})};k.prototype.c=function(){this.H();this.I()};k.prototype.f=function(){this.c();if(this.b){var a=this.b.processStyles();this.b.enqueued&&(v?this.Pa(a):(this.u(this.g,this.h),this.D(a)),this.b.enqueued=!1,this.i&&!v&&this.styleDocument())}};k.prototype.styleElement=function(a,b){var c=T(a).is,d=t.get(a);d||(d=this.ga(a)); !1;sa||(b=!0);sa=a;b&&this.Ha()}}});var Hd=new ta;g.prototype.B=function(){R()};g.prototype.K=function(a){var b=this.s[a]=(this.s[a]||0)+1;return a+"-"+b};g.prototype.Ra=function(a){return Ca(a)};g.prototype.Ta=function(a){return ea(a)};g.prototype.J=function(a){a=a.content.querySelectorAll("style");for(var b=[],c=0;c<a.length;c++){var d=a[c];b.push(d.textContent);d.parentNode.removeChild(d)}return b.join("").trim()};g.prototype.ga=function(a){return(a=a.content.querySelector("style"))?a.getAttribute("css-build")||
this.j(a)||(this.i=!0);b&&(d.P=d.P||{},Object.assign(d.P,b));if(v){if(d.P){b=d.P;for(var e in b)null===e?a.style.removeProperty(e):a.style.setProperty(e,b[e])}if(((e=Aa[c])||this.j(a))&&e&&e.a&&!id(e)){if(id(e)||e._applyShimValidatingVersion!==e._applyShimNextVersion)this.c(),this.a&&this.a.transformRules(e._styleAst,c),e.a.textContent=w.b(a,d.G),De(e);q&&(c=a.shadowRoot)&&(c.querySelector("style").textContent=w.b(a,d.G));d.G=e._styleAst}}else this.u(a,d),d.ra&&d.ra.length&&this.L(a,d)};k.prototype.l= "":""};g.prototype.prepareTemplate=function(a,b,c){if(!a.f){a.f=!0;a.name=b;a.extends=c;Ea[b]=a;var d=this.ga(a),e=this.J(a);c={is:b,extends:c,zb:d};z||r.b(a.content,b);this.c();var f=Ja.test(e)||Ia.test(e);Ja.lastIndex=0;Ia.lastIndex=0;e=zb(e);f&&A&&this.a&&this.a.transformRules(e,b);a._styleAst=e;a.g=d;d=[];A||(d=Q.J(a._styleAst));if(!d.length||A)b=this.T(c,a._styleAst,z?a.content:null,Mb[b]),a.a=b;a.c=d}};g.prototype.T=function(a,b,c,d){b=r.c(a,b);if(b.length)return Ab(b,a.is,c,d)};g.prototype.ia=
function(a){return(a=a.getRootNode().host)?t.get(a)?a:this.l(a):this.g};k.prototype.j=function(a){return a===this.g};k.prototype.L=function(a,b){var c=T(a).is,d=Bd.fetch(c,b.K,b.ra),e=d?d.styleElement:null,f=b.B;b.B=d&&d.B||this.S(c);e=O.v(a,b.K,b.B,e);q||O.u(a,b.B,f);d||Bd.b(c,b.K,e,b.B)};k.prototype.u=function(a,b){var c=this.l(a),d=t.get(c);c=Object.create(d.K||null);var e=O.S(a,b.G);a=O.da(d.G,a).F;Object.assign(c,e.pb,a,e.ub);this.fa(c,b.P);O.ea(c);b.K=c};k.prototype.fa=function(a,b){for(var c in b){var d= function(a){var b=W(a),c=b.is;b=b.$;var d=Mb[c];c=Ea[c];if(c){var e=c._styleAst;var f=c.c}return w.set(a,new w(e,d,f,0,b))};g.prototype.H=function(){!this.a&&window.ShadyCSS&&window.ShadyCSS.ApplyShim&&(this.a=window.ShadyCSS.ApplyShim,this.a.invalidCallback=Ne)};g.prototype.I=function(){var a=this;!this.b&&window.ShadyCSS&&window.ShadyCSS.CustomStyleInterface&&(this.b=window.ShadyCSS.CustomStyleInterface,this.b.transformCallback=function(b){a.v(b)},this.b.validateCallback=function(){requestAnimationFrame(function(){(a.b.enqueued||
b[c];if(d||0===d)a[c]=d}};k.prototype.styleDocument=function(a){this.styleSubtree(this.g,a)};k.prototype.styleSubtree=function(a,b){var c=a.shadowRoot;(c||this.j(a))&&this.styleElement(a,b);if(b=c&&(c.children||c.childNodes))for(a=0;a<b.length;a++)this.styleSubtree(b[a]);else if(a=a.children||a.childNodes)for(b=0;b<a.length;b++)this.styleSubtree(a[b])};k.prototype.Pa=function(a){for(var b=0;b<a.length;b++){var c=this.b.getStyleForCustomStyle(a[b]);c&&this.Oa(c)}};k.prototype.D=function(a){for(var b= a.i)&&a.f()})})};g.prototype.c=function(){this.H();this.I()};g.prototype.f=function(){this.c();if(this.b){var a=this.b.processStyles();this.b.enqueued&&(A?this.Pa(a):(this.u(this.g,this.h),this.D(a)),this.b.enqueued=!1,this.i&&!A&&this.styleDocument())}};g.prototype.styleElement=function(a,b){var c=W(a).is,d=w.get(a);d||(d=this.ia(a));this.j(a)||(this.i=!0);b&&(d.S=d.S||{},Object.assign(d.S,b));if(A){if(d.S){b=d.S;for(var e in b)null===e?a.style.removeProperty(e):a.style.setProperty(e,b[e])}if(((e=
0;b<a.length;b++){var c=this.b.getStyleForCustomStyle(a[b]);c&&O.s(c,this.h.K)}};k.prototype.v=function(a){var b=this,c=ya(a);ca(c,function(a){q?w.v(a):w.R(a);v&&(b.c(),b.a&&b.a.transformRule(a))});v?a.textContent=ba(c):this.h.G.rules.push(c)};k.prototype.Oa=function(a){if(v&&this.a){var b=ya(a);this.c();this.a.transformRules(b);a.textContent=ba(b)}};k.prototype.getComputedStyleValue=function(a,b){var c;v||(c=(t.get(a)||t.get(this.l(a))).K[b]);return(c=c||window.getComputedStyle(a).getPropertyValue(b))? Ea[c])||this.j(a))&&e&&e.a&&!od(e)){if(od(e)||e._applyShimValidatingVersion!==e._applyShimNextVersion)this.c(),this.a&&this.a.transformRules(e._styleAst,c),e.a.textContent=r.c(a,d.G),Oe(e);z&&(c=a.shadowRoot)&&(c.querySelector("style").textContent=r.c(a,d.G));d.G=e._styleAst}}else this.u(a,d),d.ua&&d.ua.length&&this.N(a,d)};g.prototype.l=function(a){return(a=a.getRootNode().host)?w.get(a)?a:this.l(a):this.g};g.prototype.j=function(a){return a===this.g};g.prototype.N=function(a,b){var c=W(a).is,d=
c.trim():""};k.prototype.Sa=function(a,b){var c=a.getRootNode();b=b?b.split(/\s/):[];c=c.host&&c.host.localName;if(!c){var d=a.getAttribute("class");if(d){d=d.split(/\s/);for(var e=0;e<d.length;e++)if(d[e]===w.c){c=d[e+1];break}}}c&&b.push(w.c,c);v||(c=t.get(a))&&c.B&&b.push(O.g,c.B);za(a,b.join(" "))};k.prototype.Qa=function(a){return t.get(a)};k.prototype.flush=k.prototype.C;k.prototype.prepareTemplate=k.prototype.prepareTemplate;k.prototype.styleElement=k.prototype.styleElement;k.prototype.styleDocument= Hd.fetch(c,b.M,b.ua),e=d?d.styleElement:null,f=b.C;b.C=d&&d.C||this.K(c);e=Q.v(a,b.M,b.C,e);z||Q.u(a,b.C,f);d||Hd.b(c,b.M,e,b.C)};g.prototype.u=function(a,b){var c=this.l(a),d=w.get(c);c=Object.create(d.M||null);var e=Q.K(a,b.G);a=Q.T(d.G,a).F;Object.assign(c,e.ob,a,e.tb);this.ha(c,b.S);Q.ga(c);b.M=c};g.prototype.ha=function(a,b){for(var c in b){var d=b[c];if(d||0===d)a[c]=d}};g.prototype.styleDocument=function(a){this.styleSubtree(this.g,a)};g.prototype.styleSubtree=function(a,b){var c=a.shadowRoot;
k.prototype.styleDocument;k.prototype.styleSubtree=k.prototype.styleSubtree;k.prototype.getComputedStyleValue=k.prototype.getComputedStyleValue;k.prototype.setElementClass=k.prototype.Sa;k.prototype._styleInfoForNode=k.prototype.Qa;k.prototype.transformCustomStyleForDocument=k.prototype.v;k.prototype.getStyleAst=k.prototype.Ra;k.prototype.styleAstToString=k.prototype.Ta;k.prototype.flushCustomStyles=k.prototype.f;Object.defineProperties(k.prototype,{nativeShadow:{get:function(){return q}},nativeCss:{get:function(){return v}}}); (c||this.j(a))&&this.styleElement(a,b);if(b=c&&(c.children||c.childNodes))for(a=0;a<b.length;a++)this.styleSubtree(b[a]);else if(a=a.children||a.childNodes)for(b=0;b<a.length;b++)this.styleSubtree(a[b])};g.prototype.Pa=function(a){for(var b=0;b<a.length;b++){var c=this.b.getStyleForCustomStyle(a[b]);c&&this.Oa(c)}};g.prototype.D=function(a){for(var b=0;b<a.length;b++){var c=this.b.getStyleForCustomStyle(a[b]);c&&Q.s(c,this.h.M)}};g.prototype.v=function(a){var b=this,c=Ca(a);fa(c,function(a){z?r.B(a):
var J=new k;if(window.ShadyCSS){var Cd=window.ShadyCSS.ApplyShim;var Dd=window.ShadyCSS.CustomStyleInterface}window.ShadyCSS={ScopingShim:J,prepareTemplate:function(a,b,c){J.f();J.prepareTemplate(a,b,c)},styleSubtree:function(a,b){J.f();J.styleSubtree(a,b)},styleElement:function(a){J.f();J.styleElement(a)},styleDocument:function(a){J.f();J.styleDocument(a)},getComputedStyleValue:function(a,b){return J.getComputedStyleValue(a,b)},nativeCss:v,nativeShadow:q};Cd&&(window.ShadyCSS.ApplyShim=Cd);Dd&&(window.ShadyCSS.CustomStyleInterface= r.J(a);A&&(b.c(),b.a&&b.a.transformRule(a))});A?a.textContent=ea(c):this.h.G.rules.push(c)};g.prototype.Oa=function(a){if(A&&this.a){var b=Ca(a);this.c();this.a.transformRules(b);a.textContent=ea(b)}};g.prototype.getComputedStyleValue=function(a,b){var c;A||(c=(w.get(a)||w.get(this.l(a))).M[b]);return(c=c||window.getComputedStyle(a).getPropertyValue(b))?c.trim():""};g.prototype.Sa=function(a,b){var c=a.getRootNode();b=b?b.split(/\s/):[];c=c.host&&c.host.localName;if(!c){var d=a.getAttribute("class");
Dd);var Ib=window.customElements,Ia=window.HTMLImports;window.WebComponents=window.WebComponents||{};if(Ib&&Ib.polyfillWrapFlushCallback){var Ja,Ed=function(){if(Ja){var a=Ja;Ja=null;a();return!0}},Fd=Ia.whenReady;Ib.polyfillWrapFlushCallback(function(a){Ja=a;Fd(Ed)});Ia.whenReady=function(a){Fd(function(){Ed()?Ia.whenReady(a):a()})}}Ia.whenReady(function(){requestAnimationFrame(function(){window.WebComponents.ready=!0;document.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))})}); if(d){d=d.split(/\s/);for(var e=0;e<d.length;e++)if(d[e]===r.a){c=d[e+1];break}}}c&&b.push(r.a,c);A||(c=w.get(a))&&c.C&&b.push(Q.g,c.C);Da(a,b.join(" "))};g.prototype.Qa=function(a){return w.get(a)};g.prototype.flush=g.prototype.B;g.prototype.prepareTemplate=g.prototype.prepareTemplate;g.prototype.styleElement=g.prototype.styleElement;g.prototype.styleDocument=g.prototype.styleDocument;g.prototype.styleSubtree=g.prototype.styleSubtree;g.prototype.getComputedStyleValue=g.prototype.getComputedStyleValue;
var Gd=document.createElement("style");Gd.textContent="body {transition: opacity ease-in 0.2s; } \nbody[unresolved] {opacity: 0; display: block; overflow: hidden; position: relative; } \n";var Hd=document.querySelector("head");Hd.insertBefore(Gd,Hd.firstChild)})();}).call(this); g.prototype.setElementClass=g.prototype.Sa;g.prototype._styleInfoForNode=g.prototype.Qa;g.prototype.transformCustomStyleForDocument=g.prototype.v;g.prototype.getStyleAst=g.prototype.Ra;g.prototype.styleAstToString=g.prototype.Ta;g.prototype.flushCustomStyles=g.prototype.f;Object.defineProperties(g.prototype,{nativeShadow:{get:function(){return z}},nativeCss:{get:function(){return A}}});var K=new g;if(window.ShadyCSS){var Id=window.ShadyCSS.ApplyShim;var Jd=window.ShadyCSS.CustomStyleInterface}window.ShadyCSS=
{ScopingShim:K,prepareTemplate:function(a,b,c){K.f();K.prepareTemplate(a,b,c)},styleSubtree:function(a,b){K.f();K.styleSubtree(a,b)},styleElement:function(a){K.f();K.styleElement(a)},styleDocument:function(a){K.f();K.styleDocument(a)},getComputedStyleValue:function(a,b){return K.getComputedStyleValue(a,b)},nativeCss:A,nativeShadow:z};Id&&(window.ShadyCSS.ApplyShim=Id);Jd&&(window.ShadyCSS.CustomStyleInterface=Jd);var Ob=window.customElements,Ma=window.HTMLImports;window.WebComponents=window.WebComponents||
{};if(Ob&&Ob.polyfillWrapFlushCallback){var Na,Kd=function(){if(Na){var a=Na;Na=null;a();return!0}},Ld=Ma.whenReady;Ob.polyfillWrapFlushCallback(function(a){Na=a;Ld(Kd)});Ma.whenReady=function(a){Ld(function(){Kd()?Ma.whenReady(a):a()})}}Ma.whenReady(function(){requestAnimationFrame(function(){window.WebComponents.ready=!0;document.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))})});var Md=document.createElement("style");Md.textContent="body {transition: opacity ease-in 0.2s; } \nbody[unresolved] {opacity: 0; display: block; overflow: hidden; position: relative; } \n";
var Nd=document.querySelector("head");Nd.insertBefore(Md,Nd.firstChild)})();}).call(this);
//# sourceMappingURL=webcomponents-lite.js.map //# sourceMappingURL=webcomponents-lite.js.map

View File

@ -24,7 +24,7 @@ from homeassistant.helpers.event import track_time_change
from homeassistant.util import convert, dt from homeassistant.util import convert, dt
REQUIREMENTS = [ REQUIREMENTS = [
'google-api-python-client==1.6.2', 'google-api-python-client==1.6.4',
'oauth2client==4.0.0', 'oauth2client==4.0.0',
] ]
@ -99,10 +99,10 @@ def do_authentication(hass, config):
from oauth2client.file import Storage from oauth2client.file import Storage
oauth = OAuth2WebServerFlow( oauth = OAuth2WebServerFlow(
config[CONF_CLIENT_ID], client_id=config[CONF_CLIENT_ID],
config[CONF_CLIENT_SECRET], client_secret=config[CONF_CLIENT_SECRET],
'https://www.googleapis.com/auth/calendar.readonly', scope='https://www.googleapis.com/auth/calendar.readonly',
'Home-Assistant.io', redirect_uri='Home-Assistant.io',
) )
try: try:

View File

@ -269,7 +269,7 @@ def async_setup(hass, config):
hass.services.async_register( hass.services.async_register(
DOMAIN, SERVICE_RELOAD, reload_service_handler, DOMAIN, SERVICE_RELOAD, reload_service_handler,
descriptions[DOMAIN][SERVICE_RELOAD], schema=RELOAD_SERVICE_SCHEMA) descriptions[SERVICE_RELOAD], schema=RELOAD_SERVICE_SCHEMA)
@asyncio.coroutine @asyncio.coroutine
def groups_service_handler(service): def groups_service_handler(service):
@ -346,11 +346,11 @@ def async_setup(hass, config):
hass.services.async_register( hass.services.async_register(
DOMAIN, SERVICE_SET, groups_service_handler, DOMAIN, SERVICE_SET, groups_service_handler,
descriptions[DOMAIN][SERVICE_SET], schema=SET_SERVICE_SCHEMA) descriptions[SERVICE_SET], schema=SET_SERVICE_SCHEMA)
hass.services.async_register( hass.services.async_register(
DOMAIN, SERVICE_REMOVE, groups_service_handler, DOMAIN, SERVICE_REMOVE, groups_service_handler,
descriptions[DOMAIN][SERVICE_REMOVE], schema=REMOVE_SERVICE_SCHEMA) descriptions[SERVICE_REMOVE], schema=REMOVE_SERVICE_SCHEMA)
@asyncio.coroutine @asyncio.coroutine
def visibility_service_handler(service): def visibility_service_handler(service):
@ -368,7 +368,7 @@ def async_setup(hass, config):
hass.services.async_register( hass.services.async_register(
DOMAIN, SERVICE_SET_VISIBILITY, visibility_service_handler, DOMAIN, SERVICE_SET_VISIBILITY, visibility_service_handler,
descriptions[DOMAIN][SERVICE_SET_VISIBILITY], descriptions[SERVICE_SET_VISIBILITY],
schema=SET_VISIBILITY_SERVICE_SCHEMA) schema=SET_VISIBILITY_SERVICE_SCHEMA)
return True return True

View File

@ -0,0 +1,59 @@
reload:
description: "Reload group configuration."
set_visibility:
description: Hide or show a group
fields:
entity_id:
description: Name(s) of entities to set value
example: 'group.travel'
visible:
description: True if group should be shown or False if it should be hidden.
example: True
set:
description: Create/Update a user group
fields:
object_id:
description: Group id and part of entity id
example: 'test_group'
name:
description: Name of group
example: 'My test group'
view:
description: Boolean for if the group is a view
example: True
icon:
description: Name of icon for the group
example: 'mdi:camera'
control:
description: Value for control the group control
example: 'hidden'
visible:
description: If the group is visible on UI
example: True
entities:
description: List of all members in the group. Not compatible with 'delta'
example: domain.entity_id1, domain.entity_id2
add_entities:
description: List of members they will change on group listening.
example: domain.entity_id1, domain.entity_id2
remove:
description: Remove a user group
fields:
object_id:
description: Group id and part of entity id
example: 'test_group'

View File

@ -14,9 +14,13 @@ from aiohttp import web
from aiohttp.web_exceptions import HTTPBadGateway from aiohttp.web_exceptions import HTTPBadGateway
from aiohttp.hdrs import CONTENT_TYPE from aiohttp.hdrs import CONTENT_TYPE
import async_timeout import async_timeout
import voluptuous as vol
from homeassistant.const import CONTENT_TYPE_TEXT_PLAIN import homeassistant.helpers.config_validation as cv
from homeassistant.components.http import HomeAssistantView, KEY_AUTHENTICATED from homeassistant.const import CONTENT_TYPE_TEXT_PLAIN, SERVER_PORT
from homeassistant.components.http import (
HomeAssistantView, KEY_AUTHENTICATED, CONF_API_PASSWORD, CONF_SERVER_PORT,
CONF_SSL_CERTIFICATE)
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.components.frontend import register_built_in_panel from homeassistant.components.frontend import register_built_in_panel
@ -25,16 +29,42 @@ _LOGGER = logging.getLogger(__name__)
DOMAIN = 'hassio' DOMAIN = 'hassio'
DEPENDENCIES = ['http'] DEPENDENCIES = ['http']
SERVICE_ADDON_START = 'addon_start'
SERVICE_ADDON_STOP = 'addon_stop'
SERVICE_ADDON_RESTART = 'addon_restart'
SERVICE_ADDON_STDIN = 'addon_stdin'
ATTR_ADDON = 'addon'
ATTR_INPUT = 'input'
NO_TIMEOUT = { NO_TIMEOUT = {
re.compile(r'^homeassistant/update$'), re.compile(r'^host/update$'), re.compile(r'^homeassistant/update$'),
re.compile(r'^supervisor/update$'), re.compile(r'^addons/[^/]*/update$'), re.compile(r'^host/update$'),
re.compile(r'^addons/[^/]*/install$') re.compile(r'^supervisor/update$'),
re.compile(r'^addons/[^/]*/update$'),
re.compile(r'^addons/[^/]*/install$'),
re.compile(r'^addons/[^/]*/rebuild$')
} }
NO_AUTH = { NO_AUTH = {
re.compile(r'^panel$'), re.compile(r'^addons/[^/]*/logo$') re.compile(r'^panel$'), re.compile(r'^addons/[^/]*/logo$')
} }
SCHEMA_ADDON = vol.Schema({
vol.Required(ATTR_ADDON): cv.slug,
})
SCHEMA_ADDON_STDIN = SCHEMA_ADDON.extend({
vol.Required(ATTR_INPUT): vol.Any(dict, cv.string)
})
MAP_SERVICE_API = {
SERVICE_ADDON_START: ('/addons/{addon}/start', SCHEMA_ADDON),
SERVICE_ADDON_STOP: ('/addons/{addon}/stop', SCHEMA_ADDON),
SERVICE_ADDON_RESTART: ('/addons/{addon}/restart', SCHEMA_ADDON),
SERVICE_ADDON_STDIN: ('/addons/{addon}/stdin', SCHEMA_ADDON_STDIN),
}
@asyncio.coroutine @asyncio.coroutine
def async_setup(hass, config): def async_setup(hass, config):
@ -48,8 +78,7 @@ def async_setup(hass, config):
websession = async_get_clientsession(hass) websession = async_get_clientsession(hass)
hassio = HassIO(hass.loop, websession, host) hassio = HassIO(hass.loop, websession, host)
api_ok = yield from hassio.is_connected() if not (yield from hassio.is_connected()):
if not api_ok:
_LOGGER.error("Not connected with HassIO!") _LOGGER.error("Not connected with HassIO!")
return False return False
@ -59,6 +88,23 @@ def async_setup(hass, config):
register_built_in_panel(hass, 'hassio', 'Hass.io', register_built_in_panel(hass, 'hassio', 'Hass.io',
'mdi:access-point-network') 'mdi:access-point-network')
if 'http' in config:
yield from hassio.update_hass_api(config.get('http'))
@asyncio.coroutine
def async_service_handler(service):
"""Handle service calls for HassIO."""
api_command = MAP_SERVICE_API[service.service][0]
addon = service.data[ATTR_ADDON]
data = service.data[ATTR_INPUT] if ATTR_INPUT in service.data else None
yield from hassio.send_command(
api_command.format(addon=addon), payload=data, timeout=60)
for service, settings in MAP_SERVICE_API.items():
hass.services.async_register(
DOMAIN, service, async_service_handler, schema=settings[1])
return True return True
@ -71,30 +117,52 @@ class HassIO(object):
self.websession = websession self.websession = websession
self._ip = ip self._ip = ip
@asyncio.coroutine
def is_connected(self): def is_connected(self):
"""Return True if it connected to HassIO supervisor. """Return True if it connected to HassIO supervisor.
This method return a coroutine.
"""
return self.send_command("/supervisor/ping", method="get")
def update_hass_api(self, http_config):
"""Update Home-Assistant API data on HassIO.
This method return a coroutine.
"""
port = http_config.get(CONF_SERVER_PORT) or SERVER_PORT
options = {
'ssl': CONF_SSL_CERTIFICATE in http_config,
'port': port,
'password': http_config.get(CONF_API_PASSWORD),
}
return self.send_command("/homeassistant/options", payload=options)
@asyncio.coroutine
def send_command(self, command, method="post", payload=None, timeout=10):
"""Send API command to HassIO.
This method is a coroutine. This method is a coroutine.
""" """
try: try:
with async_timeout.timeout(10, loop=self.loop): with async_timeout.timeout(timeout, loop=self.loop):
request = yield from self.websession.get( request = yield from self.websession.request(
"http://{}{}".format(self._ip, "/supervisor/ping") method, "http://{}{}".format(self._ip, command),
) json=payload)
if request.status != 200: if request.status != 200:
_LOGGER.error("Ping return code %d.", request.status) _LOGGER.error(
"%s return code %d.", command, request.status)
return False return False
answer = yield from request.json() answer = yield from request.json()
return answer and answer['result'] == 'ok' return answer and answer['result'] == 'ok'
except asyncio.TimeoutError: except asyncio.TimeoutError:
_LOGGER.error("Timeout on ping request") _LOGGER.error("Timeout on %s request", command)
except aiohttp.ClientError as err: except aiohttp.ClientError as err:
_LOGGER.error("Client error on ping request %s", err) _LOGGER.error("Client error on %s request %s", command, err)
return False return False

View File

@ -41,6 +41,7 @@ def last_recorder_run(hass):
with session_scope(hass=hass) as session: with session_scope(hass=hass) as session:
res = (session.query(RecorderRuns) res = (session.query(RecorderRuns)
.filter(RecorderRuns.end.isnot(None))
.order_by(RecorderRuns.end.desc()).first()) .order_by(RecorderRuns.end.desc()).first())
if res is None: if res is None:
return None return None
@ -48,8 +49,8 @@ def last_recorder_run(hass):
return res return res
def get_significant_states(hass, start_time, end_time=None, entity_id=None, def get_significant_states(hass, start_time, end_time=None, entity_ids=None,
filters=None): filters=None, include_start_time_state=True):
""" """
Return states changes during UTC period start_time - end_time. Return states changes during UTC period start_time - end_time.
@ -60,8 +61,6 @@ def get_significant_states(hass, start_time, end_time=None, entity_id=None,
timer_start = time.perf_counter() timer_start = time.perf_counter()
from homeassistant.components.recorder.models import States from homeassistant.components.recorder.models import States
entity_ids = (entity_id.lower(), ) if entity_id is not None else None
with session_scope(hass=hass) as session: with session_scope(hass=hass) as session:
query = session.query(States).filter( query = session.query(States).filter(
(States.domain.in_(SIGNIFICANT_DOMAINS) | (States.domain.in_(SIGNIFICANT_DOMAINS) |
@ -86,7 +85,9 @@ def get_significant_states(hass, start_time, end_time=None, entity_id=None,
_LOGGER.debug( _LOGGER.debug(
'get_significant_states took %fs', elapsed) 'get_significant_states took %fs', elapsed)
return states_to_json(hass, states, start_time, entity_id, filters) return states_to_json(
hass, states, start_time, entity_ids, filters,
include_start_time_state)
def state_changes_during_period(hass, start_time, end_time=None, def state_changes_during_period(hass, start_time, end_time=None,
@ -105,10 +106,12 @@ def state_changes_during_period(hass, start_time, end_time=None,
if entity_id is not None: if entity_id is not None:
query = query.filter_by(entity_id=entity_id.lower()) query = query.filter_by(entity_id=entity_id.lower())
entity_ids = [entity_id] if entity_id is not None else None
states = execute( states = execute(
query.order_by(States.last_updated)) query.order_by(States.last_updated))
return states_to_json(hass, states, start_time, entity_id) return states_to_json(hass, states, start_time, entity_ids)
def get_states(hass, utc_point_in_time, entity_ids=None, run=None, def get_states(hass, utc_point_in_time, entity_ids=None, run=None,
@ -185,7 +188,13 @@ def get_states(hass, utc_point_in_time, entity_ids=None, run=None,
if not state.attributes.get(ATTR_HIDDEN, False)] if not state.attributes.get(ATTR_HIDDEN, False)]
def states_to_json(hass, states, start_time, entity_id, filters=None): def states_to_json(
hass,
states,
start_time,
entity_ids,
filters=None,
include_start_time_state=True):
"""Convert SQL results into JSON friendly data structure. """Convert SQL results into JSON friendly data structure.
This takes our state list and turns it into a JSON friendly data This takes our state list and turns it into a JSON friendly data
@ -197,10 +206,9 @@ def states_to_json(hass, states, start_time, entity_id, filters=None):
""" """
result = defaultdict(list) result = defaultdict(list)
entity_ids = [entity_id] if entity_id is not None else None
# Get the states at the start time # Get the states at the start time
timer_start = time.perf_counter() timer_start = time.perf_counter()
if include_start_time_state:
for state in get_states(hass, start_time, entity_ids, filters=filters): for state in get_states(hass, start_time, entity_ids, filters=filters):
state.last_changed = start_time state.last_changed = start_time
state.last_updated = start_time state.last_updated = start_time
@ -250,7 +258,7 @@ class HistoryPeriodView(HomeAssistantView):
extra_urls = ['/api/history/period/{datetime}'] extra_urls = ['/api/history/period/{datetime}']
def __init__(self, filters): def __init__(self, filters):
"""Initilalize the history period view.""" """Initialize the history period view."""
self.filters = filters self.filters = filters
@asyncio.coroutine @asyncio.coroutine
@ -276,17 +284,21 @@ class HistoryPeriodView(HomeAssistantView):
end_time = request.query.get('end_time') end_time = request.query.get('end_time')
if end_time: if end_time:
end_time = dt_util.as_utc( end_time = dt_util.parse_datetime(end_time)
dt_util.parse_datetime(end_time)) if end_time:
if end_time is None: end_time = dt_util.as_utc(end_time)
else:
return self.json_message('Invalid end_time', HTTP_BAD_REQUEST) return self.json_message('Invalid end_time', HTTP_BAD_REQUEST)
else: else:
end_time = start_time + one_day end_time = start_time + one_day
entity_id = request.query.get('filter_entity_id') entity_ids = request.query.get('filter_entity_id')
if entity_ids:
entity_ids = entity_ids.lower().split(',')
include_start_time_state = 'skip_initial_state' not in request.query
result = yield from request.app['hass'].async_add_job( result = yield from request.app['hass'].async_add_job(
get_significant_states, request.app['hass'], start_time, end_time, get_significant_states, request.app['hass'], start_time, end_time,
entity_id, self.filters) entity_ids, self.filters, include_start_time_state)
result = result.values() result = result.values()
if _LOGGER.isEnabledFor(logging.DEBUG): if _LOGGER.isEnabledFor(logging.DEBUG):
elapsed = time.perf_counter() - timer_start elapsed = time.perf_counter() - timer_start

View File

@ -0,0 +1,87 @@
"""
Support to graphs card in the UI.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/history_graph/
"""
import asyncio
import logging
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.const import CONF_ENTITIES, CONF_NAME, ATTR_ENTITY_ID
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
DEPENDENCIES = ['history']
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'history_graph'
CONF_HOURS_TO_SHOW = 'hours_to_show'
CONF_REFRESH = 'refresh'
ATTR_HOURS_TO_SHOW = CONF_HOURS_TO_SHOW
ATTR_REFRESH = CONF_REFRESH
GRAPH_SCHEMA = vol.Schema({
vol.Required(CONF_ENTITIES): cv.entity_ids,
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_HOURS_TO_SHOW, default=24): vol.Range(min=1),
vol.Optional(CONF_REFRESH, default=0): vol.Range(min=0),
})
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({cv.slug: GRAPH_SCHEMA})
}, extra=vol.ALLOW_EXTRA)
@asyncio.coroutine
def async_setup(hass, config):
"""Load graph configurations."""
component = EntityComponent(
_LOGGER, DOMAIN, hass)
graphs = []
for object_id, cfg in config[DOMAIN].items():
name = cfg.get(CONF_NAME, object_id)
graph = HistoryGraphEntity(name, cfg)
graphs.append(graph)
yield from component.async_add_entities(graphs)
return True
class HistoryGraphEntity(Entity):
"""Representation of a graph entity."""
def __init__(self, name, cfg):
"""Initialize the graph."""
self._name = name
self._hours = cfg[CONF_HOURS_TO_SHOW]
self._refresh = cfg[CONF_REFRESH]
self._entities = cfg[CONF_ENTITIES]
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def name(self):
"""Return the name of the entity."""
return self._name
@property
def state_attributes(self):
"""Return the state attributes."""
attrs = {
ATTR_HOURS_TO_SHOW: self._hours,
ATTR_REFRESH: self._refresh,
ATTR_ENTITY_ID: self._entities,
}
return attrs

View File

@ -18,7 +18,7 @@ from homeassistant.const import (
CONF_PLATFORM, CONF_HOSTS, CONF_NAME, ATTR_ENTITY_ID) CONF_PLATFORM, CONF_HOSTS, CONF_NAME, ATTR_ENTITY_ID)
from homeassistant.helpers import discovery from homeassistant.helpers import discovery
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.event import track_time_interval
from homeassistant.config import load_yaml_config_file from homeassistant.config import load_yaml_config_file
REQUIREMENTS = ['pyhomematic==0.1.32'] REQUIREMENTS = ['pyhomematic==0.1.32']
@ -292,7 +292,7 @@ def setup(hass, config):
entity_hubs = [] entity_hubs = []
for _, hub_data in hosts.items(): for _, hub_data in hosts.items():
entity_hubs.append(HMHub( entity_hubs.append(HMHub(
homematic, hub_data[CONF_NAME], hub_data[CONF_VARIABLES])) hass, homematic, hub_data[CONF_NAME], hub_data[CONF_VARIABLES]))
# Register HomeMatic services # Register HomeMatic services
descriptions = load_yaml_config_file( descriptions = load_yaml_config_file(
@ -571,8 +571,9 @@ def _device_from_servicecall(hass, service):
class HMHub(Entity): class HMHub(Entity):
"""The HomeMatic hub. (CCU2/HomeGear).""" """The HomeMatic hub. (CCU2/HomeGear)."""
def __init__(self, homematic, name, use_variables): def __init__(self, hass, homematic, name, use_variables):
"""Initialize HomeMatic hub.""" """Initialize HomeMatic hub."""
self.hass = hass
self.entity_id = "{}.{}".format(DOMAIN, name.lower()) self.entity_id = "{}.{}".format(DOMAIN, name.lower())
self._homematic = homematic self._homematic = homematic
self._variables = {} self._variables = {}
@ -580,18 +581,15 @@ class HMHub(Entity):
self._state = STATE_UNKNOWN self._state = STATE_UNKNOWN
self._use_variables = use_variables self._use_variables = use_variables
@asyncio.coroutine
def async_added_to_hass(self):
"""Load data init callbacks."""
# Load data # Load data
async_track_time_interval( track_time_interval(
self.hass, self._update_hub, SCAN_INTERVAL_HUB) self.hass, self._update_hub, SCAN_INTERVAL_HUB)
yield from self.hass.async_add_job(self._update_hub, None) self.hass.add_job(self._update_hub, None)
if self._use_variables: if self._use_variables:
async_track_time_interval( track_time_interval(
self.hass, self._update_variables, SCAN_INTERVAL_VARIABLES) self.hass, self._update_variables, SCAN_INTERVAL_VARIABLES)
yield from self.hass.async_add_job(self._update_variables, None) self.hass.add_job(self._update_variables, None)
@property @property
def name(self): def name(self):
@ -621,10 +619,12 @@ class HMHub(Entity):
def _update_hub(self, now): def _update_hub(self, now):
"""Retrieve latest state.""" """Retrieve latest state."""
state = self._homematic.getServiceMessages(self._name) service_message = self._homematic.getServiceMessages(self._name)
self._state = STATE_UNKNOWN if state is None else len(state) state = None if service_message is None else len(service_message)
if now: # state have change?
if self._state != state:
self._state = state
self.schedule_update_ha_state() self.schedule_update_ha_state()
def _update_variables(self, now): def _update_variables(self, now):
@ -641,7 +641,7 @@ class HMHub(Entity):
state_change = True state_change = True
self._variables.update({key: value}) self._variables.update({key: value})
if state_change and now: if state_change:
self.schedule_update_ha_state() self.schedule_update_ha_state()
def hm_set_variable(self, name, value): def hm_set_variable(self, name, value):

View File

@ -1,8 +1,11 @@
"""Authentication for HTTP component.""" """Authentication for HTTP component."""
import asyncio import asyncio
import base64
import hmac import hmac
import logging import logging
from aiohttp import hdrs
from homeassistant.const import HTTP_HEADER_HA_AUTH from homeassistant.const import HTTP_HEADER_HA_AUTH
from .util import get_real_ip from .util import get_real_ip
from .const import KEY_TRUSTED_NETWORKS, KEY_AUTHENTICATED from .const import KEY_TRUSTED_NETWORKS, KEY_AUTHENTICATED
@ -41,6 +44,10 @@ def auth_middleware(app, handler):
validate_password(request, request.query[DATA_API_PASSWORD])): validate_password(request, request.query[DATA_API_PASSWORD])):
authenticated = True authenticated = True
elif (hdrs.AUTHORIZATION in request.headers and
validate_authorization_header(request)):
authenticated = True
elif is_trusted_ip(request): elif is_trusted_ip(request):
authenticated = True authenticated = True
@ -64,3 +71,22 @@ def validate_password(request, api_password):
"""Test if password is valid.""" """Test if password is valid."""
return hmac.compare_digest( return hmac.compare_digest(
api_password, request.app['hass'].http.api_password) api_password, request.app['hass'].http.api_password)
def validate_authorization_header(request):
"""Test an authorization header if valid password."""
if hdrs.AUTHORIZATION not in request.headers:
return False
auth_type, auth = request.headers.get(hdrs.AUTHORIZATION).split(' ', 1)
if auth_type != 'Basic':
return False
decoded = base64.b64decode(auth).decode('utf-8')
username, password = decoded.split(':', 1)
if username != 'homeassistant':
return False
return validate_password(request, password)

View File

@ -17,7 +17,7 @@ from homeassistant.components.image_processing import (
ImageProcessingEntity) ImageProcessingEntity)
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['numpy==1.13.1'] REQUIREMENTS = ['numpy==1.13.3']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -28,7 +28,7 @@ CASCADE_URL = \
'https://raw.githubusercontent.com/opencv/opencv/master/data/' + \ 'https://raw.githubusercontent.com/opencv/opencv/master/data/' + \
'lbpcascades/lbpcascade_frontalface.xml' 'lbpcascades/lbpcascade_frontalface.xml'
CONF_CLASSIFIER = 'classifer' CONF_CLASSIFIER = 'classifier'
CONF_FILE = 'file' CONF_FILE = 'file'
CONF_MIN_SIZE = 'min_size' CONF_MIN_SIZE = 'min_size'
CONF_NEIGHBORS = 'neighbors' CONF_NEIGHBORS = 'neighbors'

View File

@ -1,5 +1,5 @@
""" """
Local optical character recognition processing of seven segements displays. Local optical character recognition processing of seven segments displays.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/image_processing.seven_segments/ https://home-assistant.io/components/image_processing.seven_segments/

View File

@ -18,7 +18,7 @@ from homeassistant.helpers import state as state_helper
from homeassistant.helpers.entity_values import EntityValues from homeassistant.helpers.entity_values import EntityValues
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['influxdb==3.0.0'] REQUIREMENTS = ['influxdb==4.1.1']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@ -0,0 +1,227 @@
"""
Component to offer a way to select a date and / or a time.
For more details about this component, please refer to the documentation
at https://home-assistant.io/components/input_datetime/
"""
import asyncio
import logging
import datetime
import voluptuous as vol
from homeassistant.const import (
ATTR_ENTITY_ID, CONF_ICON, CONF_NAME, STATE_UNKNOWN)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.restore_state import async_get_last_state
from homeassistant.util import dt as dt_util
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'input_datetime'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
CONF_HAS_DATE = 'has_date'
CONF_HAS_TIME = 'has_time'
CONF_INITIAL = 'initial'
ATTR_DATE = 'date'
ATTR_TIME = 'time'
SERVICE_SET_DATETIME = 'set_datetime'
SERVICE_SET_DATETIME_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_DATE): cv.date,
vol.Optional(ATTR_TIME): cv.time,
})
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
cv.slug: vol.All({
vol.Optional(CONF_NAME): cv.string,
vol.Required(CONF_HAS_DATE): cv.boolean,
vol.Required(CONF_HAS_TIME): cv.boolean,
vol.Optional(CONF_ICON): cv.icon,
vol.Optional(CONF_INITIAL): cv.datetime,
}, cv.has_at_least_one_key_value((CONF_HAS_DATE, True),
(CONF_HAS_TIME, True)))})
}, extra=vol.ALLOW_EXTRA)
@asyncio.coroutine
def async_set_datetime(hass, entity_id, dt_value):
"""Set date and / or time of input_datetime."""
yield from hass.services.async_call(DOMAIN, SERVICE_SET_DATETIME, {
ATTR_ENTITY_ID: entity_id,
ATTR_DATE: dt_value.date(),
ATTR_TIME: dt_value.time()
})
@asyncio.coroutine
def async_setup(hass, config):
"""Set up an input datetime."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
entities = []
for object_id, cfg in config[DOMAIN].items():
name = cfg.get(CONF_NAME)
has_time = cfg.get(CONF_HAS_TIME)
has_date = cfg.get(CONF_HAS_DATE)
icon = cfg.get(CONF_ICON)
initial = cfg.get(CONF_INITIAL)
entities.append(InputDatetime(object_id, name,
has_date, has_time, icon, initial))
if not entities:
return False
@asyncio.coroutine
def async_set_datetime_service(call):
"""Handle a call to the input datetime 'set datetime' service."""
target_inputs = component.async_extract_from_service(call)
tasks = []
for input_datetime in target_inputs:
time = call.data.get(ATTR_TIME)
date = call.data.get(ATTR_DATE)
if (input_datetime.has_date() and not date) or \
(input_datetime.has_time() and not time):
_LOGGER.error("Invalid service data for "
"input_datetime.set_datetime: %s",
str(call.data))
continue
tasks.append(input_datetime.async_set_datetime(date, time))
if tasks:
yield from asyncio.wait(tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_SET_DATETIME, async_set_datetime_service,
schema=SERVICE_SET_DATETIME_SCHEMA)
yield from component.async_add_entities(entities)
return True
class InputDatetime(Entity):
"""Representation of a datetime input."""
def __init__(self, object_id, name, has_date, has_time, icon, initial):
"""Initialize a select input."""
self.entity_id = ENTITY_ID_FORMAT.format(object_id)
self._name = name
self._has_date = has_date
self._has_time = has_time
self._icon = icon
self._initial = initial
self._current_datetime = None
@asyncio.coroutine
def async_added_to_hass(self):
"""Run when entity about to be added."""
restore_val = None
# Priority 1: Initial State
if self._initial is not None:
restore_val = self._initial
# Priority 2: Old state
if restore_val is None:
old_state = yield from async_get_last_state(self.hass,
self.entity_id)
if old_state is not None:
restore_val = dt_util.parse_datetime(old_state.state)
if restore_val is not None:
if not self._has_date:
self._current_datetime = restore_val.time()
elif not self._has_time:
self._current_datetime = restore_val.date()
else:
self._current_datetime = restore_val
def has_date(self):
"""Return whether the input datetime carries a date."""
return self._has_date
def has_time(self):
"""Return whether the input datetime carries a time."""
return self._has_time
@property
def should_poll(self):
"""If entity should be polled."""
return False
@property
def name(self):
"""Return the name of the select input."""
return self._name
@property
def icon(self):
"""Return the icon to be used for this entity."""
return self._icon
@property
def state(self):
"""Return the state of the component."""
if self._current_datetime is None:
return STATE_UNKNOWN
return self._current_datetime
@property
def state_attributes(self):
"""Return the state attributes."""
attrs = {
'has_date': self._has_date,
'has_time': self._has_time,
}
if self._current_datetime is None:
return attrs
if self._has_date and self._current_datetime is not None:
attrs['year'] = self._current_datetime.year
attrs['month'] = self._current_datetime.month
attrs['day'] = self._current_datetime.day
if self._has_time and self._current_datetime is not None:
attrs['hour'] = self._current_datetime.hour
attrs['minute'] = self._current_datetime.minute
attrs['second'] = self._current_datetime.second
if self._current_datetime is not None:
if not self._has_date:
attrs['timestamp'] = self._current_datetime.hour * 3600 + \
self._current_datetime.minute * 60 + \
self._current_datetime.second
elif not self._has_time:
extended = datetime.datetime.combine(self._current_datetime,
datetime.time(0, 0))
attrs['timestamp'] = extended.timestamp()
else:
attrs['timestamp'] = self._current_datetime.timestamp()
return attrs
@asyncio.coroutine
def async_set_datetime(self, date_val, time_val):
"""Set a new date / time."""
if self._has_date and self._has_time and date_val and time_val:
self._current_datetime = datetime.datetime.combine(date_val,
time_val)
elif self._has_date and not self._has_time and date_val:
self._current_datetime = date_val
if self._has_time and not self._has_date and time_val:
self._current_datetime = time_val
yield from self.async_update_ha_state()

View File

@ -1,8 +1,8 @@
""" """
Component to offer a way to select a value from a slider. Component to offer a way to set a numeric value from a slider or text box.
For more details about this component, please refer to the documentation For more details about this component, please refer to the documentation
at https://home-assistant.io/components/input_slider/ at https://home-assistant.io/components/input_number/
""" """
import asyncio import asyncio
import logging import logging
@ -19,29 +19,34 @@ from homeassistant.helpers.restore_state import async_get_last_state
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DOMAIN = 'input_slider' DOMAIN = 'input_number'
ENTITY_ID_FORMAT = DOMAIN + '.{}' ENTITY_ID_FORMAT = DOMAIN + '.{}'
CONF_INITIAL = 'initial' CONF_INITIAL = 'initial'
CONF_MIN = 'min' CONF_MIN = 'min'
CONF_MAX = 'max' CONF_MAX = 'max'
CONF_MODE = 'mode'
CONF_STEP = 'step' CONF_STEP = 'step'
MODE_SLIDER = 'slider'
MODE_BOX = 'box'
ATTR_VALUE = 'value' ATTR_VALUE = 'value'
ATTR_MIN = 'min' ATTR_MIN = 'min'
ATTR_MAX = 'max' ATTR_MAX = 'max'
ATTR_STEP = 'step' ATTR_STEP = 'step'
ATTR_MODE = 'mode'
SERVICE_SELECT_VALUE = 'select_value' SERVICE_SET_VALUE = 'set_value'
SERVICE_SELECT_VALUE_SCHEMA = vol.Schema({ SERVICE_SET_VALUE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_VALUE): vol.Coerce(float), vol.Required(ATTR_VALUE): vol.Coerce(float),
}) })
def _cv_input_slider(cfg): def _cv_input_number(cfg):
"""Configure validation helper for input slider (voluptuous).""" """Configure validation helper for input number (voluptuous)."""
minimum = cfg.get(CONF_MIN) minimum = cfg.get(CONF_MIN)
maximum = cfg.get(CONF_MAX) maximum = cfg.get(CONF_MAX)
if minimum >= maximum: if minimum >= maximum:
@ -64,16 +69,18 @@ CONFIG_SCHEMA = vol.Schema({
vol.Optional(CONF_STEP, default=1): vol.Optional(CONF_STEP, default=1):
vol.All(vol.Coerce(float), vol.Range(min=1e-3)), vol.All(vol.Coerce(float), vol.Range(min=1e-3)),
vol.Optional(CONF_ICON): cv.icon, vol.Optional(CONF_ICON): cv.icon,
vol.Optional(ATTR_UNIT_OF_MEASUREMENT): cv.string vol.Optional(ATTR_UNIT_OF_MEASUREMENT): cv.string,
}, _cv_input_slider) vol.Optional(CONF_MODE, default=MODE_SLIDER):
vol.In([MODE_BOX, MODE_SLIDER]),
}, _cv_input_number)
}) })
}, required=True, extra=vol.ALLOW_EXTRA) }, required=True, extra=vol.ALLOW_EXTRA)
@bind_hass @bind_hass
def select_value(hass, entity_id, value): def set_value(hass, entity_id, value):
"""Set input_slider to value.""" """Set input_number to value."""
hass.services.call(DOMAIN, SERVICE_SELECT_VALUE, { hass.services.call(DOMAIN, SERVICE_SET_VALUE, {
ATTR_ENTITY_ID: entity_id, ATTR_ENTITY_ID: entity_id,
ATTR_VALUE: value, ATTR_VALUE: value,
}) })
@ -94,37 +101,39 @@ def async_setup(hass, config):
step = cfg.get(CONF_STEP) step = cfg.get(CONF_STEP)
icon = cfg.get(CONF_ICON) icon = cfg.get(CONF_ICON)
unit = cfg.get(ATTR_UNIT_OF_MEASUREMENT) unit = cfg.get(ATTR_UNIT_OF_MEASUREMENT)
mode = cfg.get(CONF_MODE)
entities.append(InputSlider( entities.append(InputNumber(
object_id, name, initial, minimum, maximum, step, icon, unit)) object_id, name, initial, minimum, maximum, step, icon, unit,
mode))
if not entities: if not entities:
return False return False
@asyncio.coroutine @asyncio.coroutine
def async_select_value_service(call): def async_set_value_service(call):
"""Handle a calls to the input slider services.""" """Handle a calls to the input slider services."""
target_inputs = component.async_extract_from_service(call) target_inputs = component.async_extract_from_service(call)
tasks = [input_slider.async_select_value(call.data[ATTR_VALUE]) tasks = [input_number.async_set_value(call.data[ATTR_VALUE])
for input_slider in target_inputs] for input_number in target_inputs]
if tasks: if tasks:
yield from asyncio.wait(tasks, loop=hass.loop) yield from asyncio.wait(tasks, loop=hass.loop)
hass.services.async_register( hass.services.async_register(
DOMAIN, SERVICE_SELECT_VALUE, async_select_value_service, DOMAIN, SERVICE_SET_VALUE, async_set_value_service,
schema=SERVICE_SELECT_VALUE_SCHEMA) schema=SERVICE_SET_VALUE_SCHEMA)
yield from component.async_add_entities(entities) yield from component.async_add_entities(entities)
return True return True
class InputSlider(Entity): class InputNumber(Entity):
"""Represent an slider.""" """Represent an slider."""
def __init__(self, object_id, name, initial, minimum, maximum, step, icon, def __init__(self, object_id, name, initial, minimum, maximum, step, icon,
unit): unit, mode):
"""Initialize a select input.""" """Initialize an input number."""
self.entity_id = ENTITY_ID_FORMAT.format(object_id) self.entity_id = ENTITY_ID_FORMAT.format(object_id)
self._name = name self._name = name
self._current_value = initial self._current_value = initial
@ -133,6 +142,7 @@ class InputSlider(Entity):
self._step = step self._step = step
self._icon = icon self._icon = icon
self._unit = unit self._unit = unit
self._mode = mode
@property @property
def should_poll(self): def should_poll(self):
@ -141,7 +151,7 @@ class InputSlider(Entity):
@property @property
def name(self): def name(self):
"""Return the name of the select input slider.""" """Return the name of the input slider."""
return self._name return self._name
@property @property
@ -165,7 +175,8 @@ class InputSlider(Entity):
return { return {
ATTR_MIN: self._minimum, ATTR_MIN: self._minimum,
ATTR_MAX: self._maximum, ATTR_MAX: self._maximum,
ATTR_STEP: self._step ATTR_STEP: self._step,
ATTR_MODE: self._mode,
} }
@asyncio.coroutine @asyncio.coroutine
@ -184,8 +195,8 @@ class InputSlider(Entity):
self._current_value = self._minimum self._current_value = self._minimum
@asyncio.coroutine @asyncio.coroutine
def async_select_value(self, value): def async_set_value(self, value):
"""Select new value.""" """Set new value."""
num_value = float(value) num_value = float(value)
if num_value < self._minimum or num_value > self._maximum: if num_value < self._minimum or num_value > self._maximum:
_LOGGER.warning("Invalid value: %s (range %s - %s)", _LOGGER.warning("Invalid value: %s (range %s - %s)",

View File

@ -217,7 +217,7 @@ class KNXModule(object):
@asyncio.coroutine @asyncio.coroutine
def service_send_to_knx_bus(self, call): def service_send_to_knx_bus(self, call):
"""Service for sending an arbitray KNX message to the KNX bus.""" """Service for sending an arbitrary KNX message to the KNX bus."""
from xknx.knx import Telegram, Address, DPTBinary, DPTArray from xknx.knx import Telegram, Address, DPTBinary, DPTArray
attr_payload = call.data.get(SERVICE_KNX_ATTR_PAYLOAD) attr_payload = call.data.get(SERVICE_KNX_ATTR_PAYLOAD)
attr_address = call.data.get(SERVICE_KNX_ATTR_ADDRESS) attr_address = call.data.get(SERVICE_KNX_ATTR_ADDRESS)

View File

@ -17,7 +17,7 @@ from homeassistant.components.light import (
SUPPORT_RGB_COLOR, Light, PLATFORM_SCHEMA) SUPPORT_RGB_COLOR, Light, PLATFORM_SCHEMA)
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['flux_led==0.19'] REQUIREMENTS = ['flux_led==0.20']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@ -206,7 +206,7 @@ def setup_bridge(host, hass, add_devices, filename, allow_unreachable,
if not skip_groups: if not skip_groups:
# Group ID 0 is a special group in the hub for all lights, but it # Group ID 0 is a special group in the hub for all lights, but it
# is not returned by get_api() so explicity get it and include it. # is not returned by get_api() so explicitly get it and include it.
# See https://developers.meethue.com/documentation/ # See https://developers.meethue.com/documentation/
# groups-api#21_get_all_groups # groups-api#21_get_all_groups
_LOGGER.debug("Getting group 0 from bridge") _LOGGER.debug("Getting group 0 from bridge")

View File

@ -134,7 +134,7 @@ class InsteonLocalDimmerDevice(Light):
@property @property
def name(self): def name(self):
"""Return the the name of the node.""" """Return the name of the node."""
return self.node.deviceName return self.node.deviceName
@property @property

View File

@ -60,12 +60,12 @@ class InsteonPLMDimmerDevice(Light):
@property @property
def address(self): def address(self):
"""Return the the address of the node.""" """Return the address of the node."""
return self._address return self._address
@property @property
def name(self): def name(self):
"""Return the the name of the node.""" """Return the name of the node."""
return self._name return self._name
@property @property

View File

@ -48,7 +48,7 @@ PLATFORM_SCHEMA = vol.Schema({
vol.All(cv.ensure_list, [cv.string]), vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_NOGROUP_ALIASES, default=[]): vol.Optional(CONF_NOGROUP_ALIASES, default=[]):
vol.All(cv.ensure_list, [cv.string]), vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_FIRE_EVENT, default=False): cv.boolean, vol.Optional(CONF_FIRE_EVENT): cv.boolean,
vol.Optional(CONF_SIGNAL_REPETITIONS): vol.Coerce(int), vol.Optional(CONF_SIGNAL_REPETITIONS): vol.Coerce(int),
vol.Optional(CONF_GROUP, default=True): cv.boolean, vol.Optional(CONF_GROUP, default=True): cv.boolean,
# deprecated config options # deprecated config options
@ -123,7 +123,7 @@ def devices_from_config(domain_config, hass=None):
_LOGGER.warning( _LOGGER.warning(
"Hybrid type for %s not compatible with signal " "Hybrid type for %s not compatible with signal "
"repetitions. Please set 'dimmable' or 'switchable' " "repetitions. Please set 'dimmable' or 'switchable' "
"type explicity in configuration", device_id) "type explicitly in configuration", device_id)
device = entity_class(device_id, hass, **device_config) device = entity_class(device_id, hass, **device_config)
devices.append(device) devices.append(device)

View File

@ -56,7 +56,7 @@ class TellstickLight(TellstickDevice, Light):
return kwargs.get(ATTR_BRIGHTNESS) return kwargs.get(ATTR_BRIGHTNESS)
def _parse_tellcore_data(self, tellcore_data): def _parse_tellcore_data(self, tellcore_data):
"""Turn the value recieved from tellcore into something useful.""" """Turn the value received from tellcore into something useful."""
if tellcore_data is not None: if tellcore_data is not None:
brightness = int(tellcore_data) brightness = int(tellcore_data)
return brightness return brightness

View File

@ -4,15 +4,18 @@ Support for the IKEA Tradfri platform.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.tradfri/ https://home-assistant.io/components/light.tradfri/
""" """
import asyncio
import logging import logging
from homeassistant.core import callback
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_RGB_COLOR, SUPPORT_BRIGHTNESS, ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_RGB_COLOR, ATTR_TRANSITION,
SUPPORT_COLOR_TEMP, SUPPORT_RGB_COLOR, Light) SUPPORT_BRIGHTNESS, SUPPORT_TRANSITION, SUPPORT_COLOR_TEMP,
from homeassistant.components.light import ( SUPPORT_RGB_COLOR, Light)
PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA) from homeassistant.components.light import \
from homeassistant.components.tradfri import ( PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA
KEY_GATEWAY, KEY_TRADFRI_GROUPS, KEY_API) from homeassistant.components.tradfri import KEY_GATEWAY, KEY_TRADFRI_GROUPS, \
KEY_API
from homeassistant.util import color as color_util from homeassistant.util import color as color_util
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -20,10 +23,13 @@ _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['tradfri'] DEPENDENCIES = ['tradfri']
PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA
IKEA = 'IKEA of Sweden' IKEA = 'IKEA of Sweden'
TRADFRI_LIGHT_MANAGER = 'Tradfri Light Manager'
SUPPORTED_FEATURES = (SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION)
ALLOWED_TEMPERATURES = {IKEA} ALLOWED_TEMPERATURES = {IKEA}
def setup_platform(hass, config, add_devices, discovery_info=None): @asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the IKEA Tradfri Light platform.""" """Set up the IKEA Tradfri Light platform."""
if discovery_info is None: if discovery_info is None:
return return
@ -31,14 +37,21 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
gateway_id = discovery_info['gateway'] gateway_id = discovery_info['gateway']
api = hass.data[KEY_API][gateway_id] api = hass.data[KEY_API][gateway_id]
gateway = hass.data[KEY_GATEWAY][gateway_id] gateway = hass.data[KEY_GATEWAY][gateway_id]
devices = api(gateway.get_devices())
lights = [dev for dev in devices if api(dev).has_light_control] devices_command = gateway.get_devices()
add_devices(Tradfri(light, api) for light in lights) devices_commands = yield from api(devices_command)
devices = yield from api(*devices_commands)
lights = [dev for dev in devices if dev.has_light_control]
if lights:
async_add_devices(TradfriLight(light, api) for light in lights)
allow_tradfri_groups = hass.data[KEY_TRADFRI_GROUPS][gateway_id] allow_tradfri_groups = hass.data[KEY_TRADFRI_GROUPS][gateway_id]
if allow_tradfri_groups: if allow_tradfri_groups:
groups = api(gateway.get_groups()) groups_command = gateway.get_groups()
add_devices(TradfriGroup(group, api) for group in groups) groups_commands = yield from api(groups_command)
groups = yield from api(*groups_commands)
if groups:
async_add_devices(TradfriGroup(group, api) for group in groups)
class TradfriGroup(Light): class TradfriGroup(Light):
@ -46,14 +59,26 @@ class TradfriGroup(Light):
def __init__(self, light, api): def __init__(self, light, api):
"""Initialize a Group.""" """Initialize a Group."""
self._group = api(light)
self._api = api self._api = api
self._name = self._group.name self._group = light
self._name = light.name
self._refresh(light)
@asyncio.coroutine
def async_added_to_hass(self):
"""Start thread when added to hass."""
self._async_start_observe()
@property
def should_poll(self):
"""No polling needed for tradfri group."""
return False
@property @property
def supported_features(self): def supported_features(self):
"""Flag supported features.""" """Flag supported features."""
return SUPPORT_BRIGHTNESS return SUPPORTED_FEATURES
@property @property
def name(self): def name(self):
@ -70,49 +95,68 @@ class TradfriGroup(Light):
"""Return the brightness of the group lights.""" """Return the brightness of the group lights."""
return self._group.dimmer return self._group.dimmer
def turn_off(self, **kwargs): @asyncio.coroutine
def async_turn_off(self, **kwargs):
"""Instruct the group lights to turn off.""" """Instruct the group lights to turn off."""
self._api(self._group.set_state(0)) self.hass.async_add_job(self._api(self._group.set_state(0)))
def turn_on(self, **kwargs): @asyncio.coroutine
def async_turn_on(self, **kwargs):
"""Instruct the group lights to turn on, or dim.""" """Instruct the group lights to turn on, or dim."""
keys = {}
if ATTR_TRANSITION in kwargs:
keys['transition_time'] = int(kwargs[ATTR_TRANSITION])
if ATTR_BRIGHTNESS in kwargs: if ATTR_BRIGHTNESS in kwargs:
self._api(self._group.set_dimmer(kwargs[ATTR_BRIGHTNESS])) self.hass.async_add_job(self._api(
self._group.set_dimmer(kwargs[ATTR_BRIGHTNESS], **keys)))
else: else:
self._api(self._group.set_state(1)) self.hass.async_add_job(self._api(self._group.set_state(1)))
@callback
def _async_start_observe(self, exc=None):
"""Start observation of light."""
from pytradfri.error import PyTradFriError
if exc:
_LOGGER.warning("Observation failed for %s", self._name,
exc_info=exc)
def update(self):
"""Fetch new state data for this group."""
from pytradfri import RequestTimeout
try: try:
self._api(self._group.update()) cmd = self._group.observe(callback=self._observe_update,
except RequestTimeout: err_callback=self._async_start_observe,
_LOGGER.warning("Tradfri update request timed out") duration=0)
self.hass.async_add_job(self._api(cmd))
except PyTradFriError as err:
_LOGGER.warning("Observation failed, trying again", exc_info=err)
self._async_start_observe()
def _refresh(self, group):
"""Refresh the light data."""
self._group = group
self._name = group.name
def _observe_update(self, tradfri_device):
"""Receive new state data for this light."""
self._refresh(tradfri_device)
self.hass.async_add_job(self.async_update_ha_state())
class Tradfri(Light): class TradfriLight(Light):
"""The platform class required by Home Asisstant.""" """The platform class required by Home Assistant."""
def __init__(self, light, api): def __init__(self, light, api):
"""Initialize a Light.""" """Initialize a Light."""
self._light = api(light)
self._api = api self._api = api
self._light = None
# Caching of LightControl and light object self._light_control = None
self._light_control = self._light.light_control self._light_data = None
self._light_data = self._light_control.lights[0] self._name = None
self._name = self._light.name
self._rgb_color = None self._rgb_color = None
self._features = SUPPORT_BRIGHTNESS self._features = SUPPORTED_FEATURES
self._temp_supported = False
if self._light_data.hex_color is not None: self._refresh(light)
if self._light.device_info.manufacturer == IKEA:
self._features |= SUPPORT_COLOR_TEMP
else:
self._features |= SUPPORT_RGB_COLOR
self._ok_temps = \
self._light.device_info.manufacturer in ALLOWED_TEMPERATURES
@property @property
def min_mireds(self): def min_mireds(self):
@ -126,6 +170,30 @@ class Tradfri(Light):
from pytradfri.color import MIN_KELVIN_WS from pytradfri.color import MIN_KELVIN_WS
return color_util.color_temperature_kelvin_to_mired(MIN_KELVIN_WS) return color_util.color_temperature_kelvin_to_mired(MIN_KELVIN_WS)
@property
def device_state_attributes(self):
"""Return the devices' state attributes."""
info = self._light.device_info
attrs = {
'manufacturer': info.manufacturer,
'model_number': info.model_number,
'serial': info.serial,
'firmware_version': info.firmware_version,
'power_source': info.power_source_str,
'battery_level': info.battery_level
}
return attrs
@asyncio.coroutine
def async_added_to_hass(self):
"""Start thread when added to hass."""
self._async_start_observe()
@property
def should_poll(self):
"""No polling needed for tradfri light."""
return False
@property @property
def supported_features(self): def supported_features(self):
"""Flag supported features.""" """Flag supported features."""
@ -151,7 +219,7 @@ class Tradfri(Light):
"""Return the CT color value in mireds.""" """Return the CT color value in mireds."""
if (self._light_data.kelvin_color is None or if (self._light_data.kelvin_color is None or
self.supported_features & SUPPORT_COLOR_TEMP == 0 or self.supported_features & SUPPORT_COLOR_TEMP == 0 or
not self._ok_temps): not self._temp_supported):
return None return None
return color_util.color_temperature_kelvin_to_mired( return color_util.color_temperature_kelvin_to_mired(
self._light_data.kelvin_color self._light_data.kelvin_color
@ -162,42 +230,90 @@ class Tradfri(Light):
"""RGB color of the light.""" """RGB color of the light."""
return self._rgb_color return self._rgb_color
def turn_off(self, **kwargs): @asyncio.coroutine
def async_turn_off(self, **kwargs):
"""Instruct the light to turn off.""" """Instruct the light to turn off."""
self._api(self._light_control.set_state(False)) self.hass.async_add_job(self._api(
self._light_control.set_state(False)))
def turn_on(self, **kwargs): @asyncio.coroutine
def async_turn_on(self, **kwargs):
""" """
Instruct the light to turn on. Instruct the light to turn on.
After adding "self._light_data.hexcolor is not None" After adding "self._light_data.hexcolor is not None"
for ATTR_RGB_COLOR, this also supports Philips Hue bulbs. for ATTR_RGB_COLOR, this also supports Philips Hue bulbs.
""" """
if ATTR_BRIGHTNESS in kwargs:
self._api(self._light_control.set_dimmer(kwargs[ATTR_BRIGHTNESS]))
else:
self._api(self._light_control.set_state(True))
if ATTR_RGB_COLOR in kwargs and self._light_data.hex_color is not None: if ATTR_RGB_COLOR in kwargs and self._light_data.hex_color is not None:
self._api(self._light.light_control.set_rgb_color( self.hass.async_add_job(self._api(
*kwargs[ATTR_RGB_COLOR])) self._light.light_control.set_rgb_color(
*kwargs[ATTR_RGB_COLOR])))
elif ATTR_COLOR_TEMP in kwargs and \ elif ATTR_COLOR_TEMP in kwargs and \
self._light_data.hex_color is not None and self._ok_temps: self._light_data.hex_color is not None and \
self._temp_supported:
kelvin = color_util.color_temperature_mired_to_kelvin( kelvin = color_util.color_temperature_mired_to_kelvin(
kwargs[ATTR_COLOR_TEMP]) kwargs[ATTR_COLOR_TEMP])
self._api(self._light_control.set_kelvin_color(kelvin)) self.hass.async_add_job(self._api(
self._light_control.set_kelvin_color(kelvin)))
keys = {}
if ATTR_TRANSITION in kwargs:
keys['transition_time'] = int(kwargs[ATTR_TRANSITION])
if ATTR_BRIGHTNESS in kwargs:
self.hass.async_add_job(self._api(
self._light_control.set_dimmer(kwargs[ATTR_BRIGHTNESS],
**keys)))
else:
self.hass.async_add_job(self._api(
self._light_control.set_state(True)))
@callback
def _async_start_observe(self, exc=None):
"""Start observation of light."""
from pytradfri.error import PyTradFriError
if exc:
_LOGGER.warning("Observation failed for %s", self._name,
exc_info=exc)
def update(self):
"""Fetch new state data for this light."""
from pytradfri import RequestTimeout
try: try:
self._api(self._light.update()) cmd = self._light.observe(callback=self._observe_update,
except RequestTimeout as exception: err_callback=self._async_start_observe,
_LOGGER.warning("Tradfri update request timed out: %s", exception) duration=0)
self.hass.async_add_job(self._api(cmd))
except PyTradFriError as err:
_LOGGER.warning("Observation failed, trying again", exc_info=err)
self._async_start_observe()
def _refresh(self, light):
"""Refresh the light data."""
self._light = light
# Caching of LightControl and light object
self._light_control = light.light_control
self._light_data = light.light_control.lights[0]
self._name = light.name
self._rgb_color = None
self._features = SUPPORTED_FEATURES
if self._light_data.hex_color is not None:
if self._light.device_info.manufacturer == IKEA:
self._features |= SUPPORT_COLOR_TEMP
else:
self._features |= SUPPORT_RGB_COLOR
self._temp_supported = self._light.device_info.manufacturer \
in ALLOWED_TEMPERATURES
def _observe_update(self, tradfri_device):
"""Receive new state data for this light."""
self._refresh(tradfri_device)
# Handle Hue lights paired with the gateway # Handle Hue lights paired with the gateway
# hex_color is 0 when bulb is unreachable # hex_color is 0 when bulb is unreachable
if self._light_data.hex_color not in (None, '0'): if self._light_data.hex_color not in (None, '0'):
self._rgb_color = color_util.rgb_hex_to_rgb_list( self._rgb_color = color_util.rgb_hex_to_rgb_list(
self._light_data.hex_color) self._light_data.hex_color)
self.hass.async_add_job(self.async_update_ha_state())

View File

@ -23,7 +23,7 @@ from homeassistant.components.light import (
Light, PLATFORM_SCHEMA) Light, PLATFORM_SCHEMA)
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['yeelight==0.3.2'] REQUIREMENTS = ['yeelight==0.3.3']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -434,7 +434,10 @@ class YeelightLight(Light):
def turn_off(self, **kwargs) -> None: def turn_off(self, **kwargs) -> None:
"""Turn off.""" """Turn off."""
import yeelight import yeelight
duration = int(self.config[CONF_TRANSITION]) # in ms
if ATTR_TRANSITION in kwargs: # passed kwarg overrides config
duration = int(kwargs.get(ATTR_TRANSITION) * 1000) # kwarg in s
try: try:
self._bulb.turn_off() self._bulb.turn_off(duration=duration)
except yeelight.BulbException as ex: except yeelight.BulbException as ex:
_LOGGER.error("Unable to turn the bulb off: %s", ex) _LOGGER.error("Unable to turn the bulb off: %s", ex)

View File

@ -17,7 +17,7 @@ get_usercode:
description: Node id of the lock description: Node id of the lock
example: 18 example: 18
code_slot: code_slot:
description: Code slot to retrive a code from description: Code slot to retrieve a code from
example: 1 example: 1
nuki_lock_n_go: nuki_lock_n_go:
@ -130,3 +130,16 @@ wink_set_lock_beeper_state:
description: enable or disable. true or false. description: enable or disable. true or false.
example: true example: true
wink_add_new_lock_key_code:
description: Add a new user key code.
fields:
entity_id:
description: Name of lock to unlock
example: 'lock.front_door'
name:
description: name of the new key code.
example: Bob
code:
description: new key code, length must match length of other codes. Default length is 4.
example: 1234

View File

@ -13,7 +13,7 @@ import voluptuous as vol
from homeassistant.components.lock import LockDevice from homeassistant.components.lock import LockDevice
from homeassistant.components.wink import WinkDevice, DOMAIN from homeassistant.components.wink import WinkDevice, DOMAIN
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN, ATTR_CODE
from homeassistant.config import load_yaml_config_file from homeassistant.config import load_yaml_config_file
DEPENDENCIES = ['wink'] DEPENDENCIES = ['wink']
@ -25,10 +25,12 @@ SERVICE_SET_ALARM_MODE = 'wink_set_lock_alarm_mode'
SERVICE_SET_ALARM_SENSITIVITY = 'wink_set_lock_alarm_sensitivity' SERVICE_SET_ALARM_SENSITIVITY = 'wink_set_lock_alarm_sensitivity'
SERVICE_SET_ALARM_STATE = 'wink_set_lock_alarm_state' SERVICE_SET_ALARM_STATE = 'wink_set_lock_alarm_state'
SERVICE_SET_BEEPER_STATE = 'wink_set_lock_beeper_state' SERVICE_SET_BEEPER_STATE = 'wink_set_lock_beeper_state'
SERVICE_ADD_KEY = 'wink_add_new_lock_key_code'
ATTR_ENABLED = 'enabled' ATTR_ENABLED = 'enabled'
ATTR_SENSITIVITY = 'sensitivity' ATTR_SENSITIVITY = 'sensitivity'
ATTR_MODE = 'mode' ATTR_MODE = 'mode'
ATTR_NAME = 'name'
ALARM_SENSITIVITY_MAP = {"low": 0.2, "medium_low": 0.4, ALARM_SENSITIVITY_MAP = {"low": 0.2, "medium_low": 0.4,
"medium": 0.6, "medium_high": 0.8, "medium": 0.6, "medium_high": 0.8,
@ -53,6 +55,12 @@ SET_ALARM_MODES_SCHEMA = vol.Schema({
vol.Required(ATTR_MODE): vol.In(ALARM_MODES_MAP) vol.Required(ATTR_MODE): vol.In(ALARM_MODES_MAP)
}) })
ADD_KEY_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_NAME): cv.string,
vol.Required(ATTR_CODE): cv.positive_int,
})
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Wink platform.""" """Set up the Wink platform."""
@ -86,6 +94,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
lock.set_alarm_mode(service.data.get(ATTR_MODE)) lock.set_alarm_mode(service.data.get(ATTR_MODE))
elif service.service == SERVICE_SET_ALARM_SENSITIVITY: elif service.service == SERVICE_SET_ALARM_SENSITIVITY:
lock.set_alarm_sensitivity(service.data.get(ATTR_SENSITIVITY)) lock.set_alarm_sensitivity(service.data.get(ATTR_SENSITIVITY))
elif service.service == SERVICE_ADD_KEY:
name = service.data.get(ATTR_NAME)
code = service.data.get(ATTR_CODE)
lock.add_new_key(code, name)
descriptions = load_yaml_config_file( descriptions = load_yaml_config_file(
path.join(path.dirname(__file__), 'services.yaml')) path.join(path.dirname(__file__), 'services.yaml'))
@ -115,6 +127,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
descriptions.get(SERVICE_SET_ALARM_SENSITIVITY), descriptions.get(SERVICE_SET_ALARM_SENSITIVITY),
schema=SET_SENSITIVITY_SCHEMA) schema=SET_SENSITIVITY_SCHEMA)
hass.services.register(DOMAIN, SERVICE_ADD_KEY,
service_handle,
descriptions.get(SERVICE_ADD_KEY),
schema=ADD_KEY_SCHEMA)
class WinkLockDevice(WinkDevice, LockDevice): class WinkLockDevice(WinkDevice, LockDevice):
"""Representation of a Wink lock.""" """Representation of a Wink lock."""
@ -149,6 +166,10 @@ class WinkLockDevice(WinkDevice, LockDevice):
"""Set lock's beeper mode.""" """Set lock's beeper mode."""
self.wink.set_beeper_mode(enabled) self.wink.set_beeper_mode(enabled)
def add_new_key(self, code, name):
"""Add a new user key code."""
self.wink.add_new_key(code, name)
def set_alarm_sensitivity(self, sensitivity): def set_alarm_sensitivity(self, sensitivity):
""" """
Set lock's alarm sensitivity. Set lock's alarm sensitivity.
@ -176,14 +197,14 @@ class WinkLockDevice(WinkDevice, LockDevice):
super_attrs = super().device_state_attributes super_attrs = super().device_state_attributes
sensitivity = dict_value_to_key(ALARM_SENSITIVITY_MAP, sensitivity = dict_value_to_key(ALARM_SENSITIVITY_MAP,
self.wink.alarm_sensitivity()) self.wink.alarm_sensitivity())
super_attrs['alarm sensitivity'] = sensitivity super_attrs['alarm_sensitivity'] = sensitivity
super_attrs['vacation mode'] = self.wink.vacation_mode_enabled() super_attrs['vacation_mode'] = self.wink.vacation_mode_enabled()
super_attrs['beeper mode'] = self.wink.beeper_enabled() super_attrs['beeper_mode'] = self.wink.beeper_enabled()
super_attrs['auto lock'] = self.wink.auto_lock_enabled() super_attrs['auto_lock'] = self.wink.auto_lock_enabled()
alarm_mode = dict_value_to_key(ALARM_MODES_MAP, alarm_mode = dict_value_to_key(ALARM_MODES_MAP,
self.wink.alarm_mode()) self.wink.alarm_mode())
super_attrs['alarm mode'] = alarm_mode super_attrs['alarm_mode'] = alarm_mode
super_attrs['alarm enabled'] = self.wink.alarm_enabled() super_attrs['alarm_enabled'] = self.wink.alarm_enabled()
return super_attrs return super_attrs

View File

@ -15,7 +15,7 @@ from homeassistant.components.media_player import (
from homeassistant.config import load_yaml_config_file from homeassistant.config import load_yaml_config_file
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
REQUIREMENTS = ['youtube_dl==2017.9.15'] REQUIREMENTS = ['youtube_dl==2017.10.01']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@ -637,11 +637,11 @@ class MediaPlayerDevice(Entity):
return self.hass.async_add_job(self.set_volume_level, volume) return self.hass.async_add_job(self.set_volume_level, volume)
def media_play(self): def media_play(self):
"""Send play commmand.""" """Send play command."""
raise NotImplementedError() raise NotImplementedError()
def async_media_play(self): def async_media_play(self):
"""Send play commmand. """Send play command.
This method must be run in the event loop and returns a coroutine. This method must be run in the event loop and returns a coroutine.
""" """

View File

@ -96,7 +96,8 @@ class AppleTvDevice(MediaPlayerDevice):
if self._playing: if self._playing:
from pyatv import const from pyatv import const
state = self._playing.play_state state = self._playing.play_state
if state == const.PLAY_STATE_NO_MEDIA or \ if state == const.PLAY_STATE_IDLE or \
state == const.PLAY_STATE_NO_MEDIA or \
state == const.PLAY_STATE_LOADING: state == const.PLAY_STATE_LOADING:
return STATE_IDLE return STATE_IDLE
elif state == const.PLAY_STATE_PLAYING: elif state == const.PLAY_STATE_PLAYING:

View File

@ -287,7 +287,7 @@ class CastDevice(MediaPlayerDevice):
self.cast.set_volume(volume) self.cast.set_volume(volume)
def media_play(self): def media_play(self):
"""Send play commmand.""" """Send play command."""
self.cast.media_controller.play() self.cast.media_controller.play()
def media_pause(self): def media_pause(self):

View File

@ -1,5 +1,5 @@
""" """
Support for the DirecTV recievers. Support for the DirecTV receivers.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.directv/ https://home-assistant.io/components/media_player.directv/
@ -82,7 +82,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class DirecTvDevice(MediaPlayerDevice): class DirecTvDevice(MediaPlayerDevice):
"""Representation of a DirecTV reciever on the network.""" """Representation of a DirecTV receiver on the network."""
def __init__(self, name, host, port, device): def __init__(self, name, host, port, device):
"""Initialize the device.""" """Initialize the device."""

View File

@ -124,7 +124,7 @@ class OpenhomeDevice(MediaPlayerDevice):
self._device.Stop() self._device.Stop()
def media_play(self): def media_play(self):
"""Send play commmand.""" """Send play command."""
self._device.Play() self._device.Play()
def media_next_track(self): def media_next_track(self):

View File

@ -151,11 +151,11 @@ class PhilipsTV(MediaPlayerDevice):
self._state = STATE_OFF self._state = STATE_OFF
def media_previous_track(self): def media_previous_track(self):
"""Send rewind commmand.""" """Send rewind command."""
self._tv.sendKey('Previous') self._tv.sendKey('Previous')
def media_next_track(self): def media_next_track(self):
"""Send fast forward commmand.""" """Send fast forward command."""
self._tv.sendKey('Next') self._tv.sendKey('Next')
@property @property

View File

@ -711,7 +711,7 @@ class PlexClient(MediaPlayerDevice):
if ("127.0.0.1" in client.baseurl and if ("127.0.0.1" in client.baseurl and
client.machineIdentifier == self.device.machineIdentifier): client.machineIdentifier == self.device.machineIdentifier):
# point controls to server since that's where the # point controls to server since that's where the
# playback is occuring # playback is occurring
_LOGGER.debug( _LOGGER.debug(
"Local client detected, redirecting controls to " "Local client detected, redirecting controls to "
"Plex server: %s", self.entity_id) "Plex server: %s", self.entity_id)

View File

@ -135,7 +135,7 @@ class RussoundRNETDevice(MediaPlayerDevice):
def set_volume_level(self, volume): def set_volume_level(self, volume):
"""Set volume level. Volume has a range (0..1). """Set volume level. Volume has a range (0..1).
Translate this to a range of (0..100) as expected expected Translate this to a range of (0..100) as expected
by _russ.set_volume() by _russ.set_volume()
""" """
self._russ.set_volume('1', self._zone_id, volume * 100) self._russ.set_volume('1', self._zone_id, volume * 100)

View File

@ -140,7 +140,7 @@ select_source:
fields: fields:
entity_id: entity_id:
description: Name(s) of entites to change source on description: Name(s) of entities to change source on
example: 'media_player.media_player.txnr535_0009b0d81f82' example: 'media_player.media_player.txnr535_0009b0d81f82'
source: source:
description: Name of the source to switch to. Platform dependent. description: Name of the source to switch to. Platform dependent.
@ -151,7 +151,7 @@ clear_playlist:
fields: fields:
entity_id: entity_id:
description: Name(s) of entites to change source on description: Name(s) of entities to change source on
example: 'media_player.living_room_chromecast' example: 'media_player.living_room_chromecast'
shuffle_set: shuffle_set:
@ -170,7 +170,7 @@ snapcast_snapshot:
fields: fields:
entity_id: entity_id:
description: Name(s) of entites that will be snapshotted. Platform dependent. description: Name(s) of entities that will be snapshotted. Platform dependent.
example: 'media_player.living_room' example: 'media_player.living_room'
snapcast_restore: snapcast_restore:
@ -178,7 +178,7 @@ snapcast_restore:
fields: fields:
entity_id: entity_id:
description: Name(s) of entites that will be restored. Platform dependent. description: Name(s) of entities that will be restored. Platform dependent.
example: 'media_player.living_room' example: 'media_player.living_room'
sonos_join: sonos_join:
@ -190,7 +190,7 @@ sonos_join:
example: 'media_player.living_room_sonos' example: 'media_player.living_room_sonos'
entity_id: entity_id:
description: Name(s) of entites that will coordinate the grouping. Platform dependent. description: Name(s) of entities that will coordinate the grouping. Platform dependent.
example: 'media_player.living_room_sonos' example: 'media_player.living_room_sonos'
sonos_unjoin: sonos_unjoin:
@ -198,7 +198,7 @@ sonos_unjoin:
fields: fields:
entity_id: entity_id:
description: Name(s) of entites that will be unjoined from their group. Platform dependent. description: Name(s) of entities that will be unjoined from their group. Platform dependent.
example: 'media_player.living_room_sonos' example: 'media_player.living_room_sonos'
sonos_snapshot: sonos_snapshot:
@ -206,7 +206,7 @@ sonos_snapshot:
fields: fields:
entity_id: entity_id:
description: Name(s) of entites that will be snapshot. Platform dependent. description: Name(s) of entities that will be snapshot. Platform dependent.
example: 'media_player.living_room_sonos' example: 'media_player.living_room_sonos'
with_group: with_group:
@ -218,7 +218,7 @@ sonos_restore:
fields: fields:
entity_id: entity_id:
description: Name(s) of entites that will be restored. Platform dependent. description: Name(s) of entities that will be restored. Platform dependent.
example: 'media_player.living_room_sonos' example: 'media_player.living_room_sonos'
with_group: with_group:
@ -230,7 +230,7 @@ sonos_set_sleep_timer:
fields: fields:
entity_id: entity_id:
description: Name(s) of entites that will have a timer set. description: Name(s) of entities that will have a timer set.
example: 'media_player.living_room_sonos' example: 'media_player.living_room_sonos'
sleep_time: sleep_time:
description: Number of seconds to set the timer description: Number of seconds to set the timer
@ -241,7 +241,7 @@ sonos_clear_sleep_timer:
fields: fields:
entity_id: entity_id:
description: Name(s) of entites that will have the timer cleared. description: Name(s) of entities that will have the timer cleared.
example: 'media_player.living_room_sonos' example: 'media_player.living_room_sonos'

View File

@ -915,8 +915,8 @@ class SonosDevice(MediaPlayerDevice):
"""Replace queue with playlist represented by src. """Replace queue with playlist represented by src.
Playlists can't be played directly with the self._player.play_uri Playlists can't be played directly with the self._player.play_uri
API as they are actually composed of mulitple URLs. Until soco has API as they are actually composed of multiple URLs. Until soco has
suppport for playing a playlist, we'll need to parse the playlist item support for playing a playlist, we'll need to parse the playlist item
and replace the current queue in order to play it. and replace the current queue in order to play it.
""" """
import soco import soco
@ -1116,7 +1116,7 @@ class SonosDevice(MediaPlayerDevice):
return return
## ##
# old is allready master, rejoin # old is already master, rejoin
if old.coordinator.group.coordinator == old.coordinator: if old.coordinator.group.coordinator == old.coordinator:
self._player.join(old.coordinator) self._player.join(old.coordinator)
return return

View File

@ -441,7 +441,7 @@ class UniversalMediaPlayer(MediaPlayerDevice):
SERVICE_VOLUME_SET, data, allow_override=True) SERVICE_VOLUME_SET, data, allow_override=True)
def async_media_play(self): def async_media_play(self):
"""Send play commmand. """Send play command.
This method must be run in the event loop and returns a coroutine. This method must be run in the event loop and returns a coroutine.
""" """

View File

@ -137,7 +137,7 @@ class VlcDevice(MediaPlayerDevice):
self._volume = volume self._volume = volume
def media_play(self): def media_play(self):
"""Send play commmand.""" """Send play command."""
self._vlc.play() self._vlc.play()
self._state = STATE_PLAYING self._state = STATE_PLAYING

View File

@ -19,10 +19,11 @@ from homeassistant.components.media_player import (
SUPPORT_SELECT_SOURCE, SUPPORT_PLAY_MEDIA, MEDIA_TYPE_CHANNEL, SUPPORT_SELECT_SOURCE, SUPPORT_PLAY_MEDIA, MEDIA_TYPE_CHANNEL,
MediaPlayerDevice, PLATFORM_SCHEMA) MediaPlayerDevice, PLATFORM_SCHEMA)
from homeassistant.const import ( from homeassistant.const import (
CONF_HOST, CONF_MAC, CONF_CUSTOMIZE, CONF_TIMEOUT, STATE_OFF, CONF_HOST, CONF_CUSTOMIZE, CONF_TIMEOUT, STATE_OFF,
STATE_PLAYING, STATE_PAUSED, STATE_PLAYING, STATE_PAUSED,
STATE_UNKNOWN, CONF_NAME, CONF_FILENAME) STATE_UNKNOWN, CONF_NAME, CONF_FILENAME)
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.script import Script
REQUIREMENTS = ['pylgtv==0.1.7', REQUIREMENTS = ['pylgtv==0.1.7',
'websockets==3.2', 'websockets==3.2',
@ -32,6 +33,7 @@ _CONFIGURING = {} # type: Dict[str, str]
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CONF_SOURCES = 'sources' CONF_SOURCES = 'sources'
CONF_ON_ACTION = 'turn_on_action'
DEFAULT_NAME = 'LG webOS Smart TV' DEFAULT_NAME = 'LG webOS Smart TV'
@ -53,10 +55,10 @@ CUSTOMIZE_SCHEMA = vol.Schema({
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_HOST): cv.string, vol.Optional(CONF_HOST): cv.string,
vol.Optional(CONF_MAC): cv.string,
vol.Optional(CONF_CUSTOMIZE, default={}): CUSTOMIZE_SCHEMA, vol.Optional(CONF_CUSTOMIZE, default={}): CUSTOMIZE_SCHEMA,
vol.Optional(CONF_FILENAME, default=WEBOSTV_CONFIG_FILE): cv.string, vol.Optional(CONF_FILENAME, default=WEBOSTV_CONFIG_FILE): cv.string,
vol.Optional(CONF_TIMEOUT, default=10): cv.positive_int, vol.Optional(CONF_TIMEOUT, default=10): cv.positive_int,
vol.Optional(CONF_ON_ACTION): cv.SCRIPT_SCHEMA,
}) })
@ -76,15 +78,19 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if host in _CONFIGURING: if host in _CONFIGURING:
return return
mac = config.get(CONF_MAC)
name = config.get(CONF_NAME) name = config.get(CONF_NAME)
customize = config.get(CONF_CUSTOMIZE) customize = config.get(CONF_CUSTOMIZE)
timeout = config.get(CONF_TIMEOUT) timeout = config.get(CONF_TIMEOUT)
turn_on_action = config.get(CONF_ON_ACTION)
config = hass.config.path(config.get(CONF_FILENAME)) config = hass.config.path(config.get(CONF_FILENAME))
setup_tv(host, mac, name, customize, config, timeout, hass, add_devices)
setup_tv(host, name, customize, config, timeout, hass,
add_devices, turn_on_action)
def setup_tv(host, mac, name, customize, config, timeout, hass, add_devices): def setup_tv(host, name, customize, config, timeout, hass,
add_devices, turn_on_action):
"""Set up a LG WebOS TV based on host parameter.""" """Set up a LG WebOS TV based on host parameter."""
from pylgtv import WebOsClient from pylgtv import WebOsClient
from pylgtv import PyLGTVPairException from pylgtv import PyLGTVPairException
@ -108,7 +114,8 @@ def setup_tv(host, mac, name, customize, config, timeout, hass, add_devices):
# Not registered, request configuration. # Not registered, request configuration.
_LOGGER.warning("LG webOS TV %s needs to be paired", host) _LOGGER.warning("LG webOS TV %s needs to be paired", host)
request_configuration( request_configuration(
host, mac, name, customize, config, timeout, hass, add_devices) host, name, customize, config, timeout, hass,
add_devices, turn_on_action)
return return
# If we came here and configuring this host, mark as done. # If we came here and configuring this host, mark as done.
@ -117,12 +124,13 @@ def setup_tv(host, mac, name, customize, config, timeout, hass, add_devices):
configurator = hass.components.configurator configurator = hass.components.configurator
configurator.request_done(request_id) configurator.request_done(request_id)
add_devices([LgWebOSDevice(host, mac, name, customize, config, timeout)], add_devices([LgWebOSDevice(host, name, customize, config, timeout,
True) hass, turn_on_action)], True)
def request_configuration( def request_configuration(
host, mac, name, customize, config, timeout, hass, add_devices): host, name, customize, config, timeout, hass,
add_devices, turn_on_action):
"""Request configuration steps from the user.""" """Request configuration steps from the user."""
configurator = hass.components.configurator configurator = hass.components.configurator
@ -135,8 +143,8 @@ def request_configuration(
# pylint: disable=unused-argument # pylint: disable=unused-argument
def lgtv_configuration_callback(data): def lgtv_configuration_callback(data):
"""The actions to do when our configuration callback is called.""" """The actions to do when our configuration callback is called."""
setup_tv(host, mac, name, customize, config, timeout, hass, setup_tv(host, name, customize, config, timeout, hass,
add_devices) add_devices, turn_on_action)
_CONFIGURING[host] = configurator.request_config( _CONFIGURING[host] = configurator.request_config(
name, lgtv_configuration_callback, name, lgtv_configuration_callback,
@ -149,13 +157,12 @@ def request_configuration(
class LgWebOSDevice(MediaPlayerDevice): class LgWebOSDevice(MediaPlayerDevice):
"""Representation of a LG WebOS TV.""" """Representation of a LG WebOS TV."""
def __init__(self, host, mac, name, customize, config, timeout): def __init__(self, host, name, customize, config, timeout,
hass, on_action):
"""Initialize the webos device.""" """Initialize the webos device."""
from pylgtv import WebOsClient from pylgtv import WebOsClient
from wakeonlan import wol
self._client = WebOsClient(host, config, timeout) self._client = WebOsClient(host, config, timeout)
self._wol = wol self._on_script = Script(hass, on_action) if on_action else None
self._mac = mac
self._customize = customize self._customize = customize
self._name = name self._name = name
@ -273,7 +280,7 @@ class LgWebOSDevice(MediaPlayerDevice):
@property @property
def supported_features(self): def supported_features(self):
"""Flag media player features that are supported.""" """Flag media player features that are supported."""
if self._mac: if self._on_script:
return SUPPORT_WEBOSTV | SUPPORT_TURN_ON return SUPPORT_WEBOSTV | SUPPORT_TURN_ON
return SUPPORT_WEBOSTV return SUPPORT_WEBOSTV
@ -289,8 +296,8 @@ class LgWebOSDevice(MediaPlayerDevice):
def turn_on(self): def turn_on(self):
"""Turn on the media player.""" """Turn on the media player."""
if self._mac: if self._on_script:
self._wol.send_magic_packet(self._mac) self._on_script.run()
def volume_up(self): def volume_up(self):
"""Volume up the media player.""" """Volume up the media player."""

View File

@ -214,7 +214,7 @@ class YamahaDevice(MediaPlayerDevice):
self._volume = (self._receiver.volume / 100) + 1 self._volume = (self._receiver.volume / 100) + 1
def media_play(self): def media_play(self):
"""Send play commmand.""" """Send play command."""
self._call_playback_function(self._receiver.play, "play") self._call_playback_function(self._receiver.play, "play")
def media_pause(self): def media_pause(self):

View File

@ -33,7 +33,9 @@ SUPPORTED_FEATURES = (
SUPPORT_SELECT_SOURCE SUPPORT_SELECT_SOURCE
) )
REQUIREMENTS = ['pymusiccast==0.1.0'] KNOWN_HOSTS_KEY = 'data_yamaha_musiccast'
REQUIREMENTS = ['pymusiccast==0.1.2']
DEFAULT_NAME = "Yamaha Receiver" DEFAULT_NAME = "Yamaha Receiver"
DEFAULT_PORT = 5005 DEFAULT_PORT = 5005
@ -47,16 +49,48 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Yamaha MusicCast platform.""" """Set up the Yamaha MusicCast platform."""
import socket
import pymusiccast import pymusiccast
known_hosts = hass.data.get(KNOWN_HOSTS_KEY)
if known_hosts is None:
known_hosts = hass.data[KNOWN_HOSTS_KEY] = []
_LOGGER.debug("known_hosts: %s", known_hosts)
name = config.get(CONF_NAME) name = config.get(CONF_NAME)
host = config.get(CONF_HOST) host = config.get(CONF_HOST)
port = config.get(CONF_PORT) port = config.get(CONF_PORT)
receiver = pymusiccast.McDevice(host, udp_port=port) # Get IP of host to prevent duplicates
_LOGGER.debug("receiver: %s / Port: %d", receiver, port) try:
ipaddr = socket.gethostbyname(host)
except (OSError) as error:
_LOGGER.error(
"Could not communicate with %s:%d: %s", host, port, error)
return
if [item for item in known_hosts if item[0] == ipaddr]:
_LOGGER.warning("Host %s:%d already registered.", host, port)
return
if [item for item in known_hosts if item[1] == port]:
_LOGGER.warning("Port %s:%d already registered.", host, port)
return
reg_host = (ipaddr, port)
known_hosts.append(reg_host)
try:
receiver = pymusiccast.McDevice(ipaddr, udp_port=port)
except pymusiccast.exceptions.YMCInitError as err:
_LOGGER.error(err)
receiver = None
if receiver:
_LOGGER.debug("receiver: %s / Port: %d", receiver, port)
add_devices([YamahaDevice(receiver, name)], True) add_devices([YamahaDevice(receiver, name)], True)
else:
known_hosts.remove(reg_host)
class YamahaDevice(MediaPlayerDevice): class YamahaDevice(MediaPlayerDevice):

View File

@ -12,14 +12,19 @@ from homeassistant.const import MATCH_ALL
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.components.mqtt import valid_publish_topic from homeassistant.components.mqtt import valid_publish_topic
from homeassistant.helpers.event import async_track_state_change from homeassistant.helpers.event import async_track_state_change
import homeassistant.helpers.config_validation as cv
CONF_BASE_TOPIC = 'base_topic' CONF_BASE_TOPIC = 'base_topic'
CONF_PUBLISH_ATTRIBUTES = 'publish_attributes'
CONF_PUBLISH_TIMESTAMPS = 'publish_timestamps'
DEPENDENCIES = ['mqtt'] DEPENDENCIES = ['mqtt']
DOMAIN = 'mqtt_statestream' DOMAIN = 'mqtt_statestream'
CONFIG_SCHEMA = vol.Schema({ CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({ DOMAIN: vol.Schema({
vol.Required(CONF_BASE_TOPIC): valid_publish_topic vol.Required(CONF_BASE_TOPIC): valid_publish_topic,
vol.Optional(CONF_PUBLISH_ATTRIBUTES, default=False): cv.boolean,
vol.Optional(CONF_PUBLISH_TIMESTAMPS, default=False): cv.boolean
}) })
}, extra=vol.ALLOW_EXTRA) }, extra=vol.ALLOW_EXTRA)
@ -29,6 +34,8 @@ def async_setup(hass, config):
"""Set up the MQTT state feed.""" """Set up the MQTT state feed."""
conf = config.get(DOMAIN, {}) conf = config.get(DOMAIN, {})
base_topic = conf.get(CONF_BASE_TOPIC) base_topic = conf.get(CONF_BASE_TOPIC)
publish_attributes = conf.get(CONF_PUBLISH_ATTRIBUTES)
publish_timestamps = conf.get(CONF_PUBLISH_TIMESTAMPS)
if not base_topic.endswith('/'): if not base_topic.endswith('/'):
base_topic = base_topic + '/' base_topic = base_topic + '/'
@ -38,8 +45,28 @@ def async_setup(hass, config):
return return
payload = new_state.state payload = new_state.state
topic = base_topic + entity_id.replace('.', '/') + '/state' mybase = base_topic + entity_id.replace('.', '/') + '/'
hass.components.mqtt.async_publish(topic, payload, 1, True) hass.components.mqtt.async_publish(mybase + 'state', payload, 1, True)
if publish_timestamps:
if new_state.last_updated:
hass.components.mqtt.async_publish(
mybase + 'last_updated',
new_state.last_updated.isoformat(),
1,
True)
if new_state.last_changed:
hass.components.mqtt.async_publish(
mybase + 'last_changed',
new_state.last_changed.isoformat(),
1,
True)
if publish_attributes:
for key, val in new_state.attributes.items():
if val:
hass.components.mqtt.async_publish(mybase + key,
val, 1, True)
async_track_state_change(hass, MATCH_ALL, _state_publisher) async_track_state_change(hass, MATCH_ALL, _state_publisher)
return True return True

View File

@ -111,7 +111,7 @@ class ApnsDevice(object):
return self.device_disabled return self.device_disabled
def disable(self): def disable(self):
"""Disable the device from recieving notifications.""" """Disable the device from receiving notifications."""
self.device_disabled = True self.device_disabled = True
def __eq__(self, other): def __eq__(self, other):

View File

@ -0,0 +1,90 @@
"""
Clicksend audio platform for notify component.
This platform sends text to speech audio messages through clicksend
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/notify.clicksendaudio/
"""
import json
import logging
import requests
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
CONF_USERNAME, CONF_API_KEY, CONF_RECIPIENT, HTTP_HEADER_CONTENT_TYPE,
CONTENT_TYPE_JSON)
from homeassistant.components.notify import (
PLATFORM_SCHEMA, BaseNotificationService)
_LOGGER = logging.getLogger(__name__)
BASE_API_URL = 'https://rest.clicksend.com/v3'
HEADERS = {HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_JSON}
CONF_LANGUAGE = 'language'
CONF_VOICE = 'voice'
DEFAULT_LANGUAGE = 'en-us'
DEFAULT_VOICE = 'female'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_API_KEY): cv.string,
vol.Required(CONF_RECIPIENT): cv.string,
vol.Optional(CONF_LANGUAGE, default=DEFAULT_LANGUAGE): cv.string,
vol.Optional(CONF_VOICE, default=DEFAULT_VOICE): cv.string,
})
def get_service(hass, config, discovery_info=None):
"""Get the ClickSend notification service."""
if _authenticate(config) is False:
_LOGGER.error("You are not authorized to access ClickSend")
return None
return ClicksendNotificationService(config)
class ClicksendNotificationService(BaseNotificationService):
"""Implementation of a notification service for the ClickSend service."""
def __init__(self, config):
"""Initialize the service."""
self.username = config.get(CONF_USERNAME)
self.api_key = config.get(CONF_API_KEY)
self.recipient = config.get(CONF_RECIPIENT)
self.language = config.get(CONF_LANGUAGE)
self.voice = config.get(CONF_VOICE)
def send_message(self, message="", **kwargs):
"""Send a voice call to a user."""
data = ({'messages': [{'source': 'hass.notify', 'from': self.recipient,
'to': self.recipient, 'body': message,
'lang': self.language, 'voice': self.voice}]})
api_url = "{}/voice/send".format(BASE_API_URL)
resp = requests.post(api_url, data=json.dumps(data), headers=HEADERS,
auth=(self.username, self.api_key), timeout=5)
obj = json.loads(resp.text)
response_msg = obj['response_msg']
response_code = obj['response_code']
if resp.status_code != 200:
_LOGGER.error("Error %s : %s (Code %s)", resp.status_code,
response_msg, response_code)
def _authenticate(config):
"""Authenticate with ClickSend."""
api_url = '{}/account'.format(BASE_API_URL)
resp = requests.get(api_url, headers=HEADERS,
auth=(config.get(CONF_USERNAME),
config.get(CONF_API_KEY)), timeout=5)
if resp.status_code != 200:
return False
return True

View File

@ -15,7 +15,7 @@ from homeassistant.components.notify import (
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['discord.py==0.16.11'] REQUIREMENTS = ['discord.py==0.16.12']
CONF_TOKEN = 'token' CONF_TOKEN = 'token'

View File

@ -56,8 +56,15 @@ class FacebookNotificationService(BaseNotificationService):
return return
for target in targets: for target in targets:
# If the target starts with a "+", we suppose it's a phone number,
# otherwise it's a user id.
if target.startswith('+'):
recipient = {"phone_number": target}
else:
recipient = {"id": target}
body = { body = {
"recipient": {"phone_number": target}, "recipient": recipient,
"message": body_message "message": body_message
} }
import json import json

Some files were not shown because too many files have changed in this diff Show More