mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 15:47:12 +00:00
commit
8a2d7a3e11
11
.coveragerc
11
.coveragerc
@ -52,7 +52,7 @@ omit =
|
||||
|
||||
homeassistant/components/digital_ocean.py
|
||||
homeassistant/components/*/digital_ocean.py
|
||||
|
||||
|
||||
homeassistant/components/doorbird.py
|
||||
homeassistant/components/*/doorbird.py
|
||||
|
||||
@ -149,6 +149,9 @@ omit =
|
||||
homeassistant/components/rachio.py
|
||||
homeassistant/components/*/rachio.py
|
||||
|
||||
homeassistant/components/raincloud.py
|
||||
homeassistant/components/*/raincloud.py
|
||||
|
||||
homeassistant/components/raspihats.py
|
||||
homeassistant/components/*/raspihats.py
|
||||
|
||||
@ -179,6 +182,9 @@ omit =
|
||||
homeassistant/components/tesla.py
|
||||
homeassistant/components/*/tesla.py
|
||||
|
||||
homeassistant/components/thethingsnetwork.py
|
||||
homeassistant/components/*/thethingsnetwork.py
|
||||
|
||||
homeassistant/components/*/thinkingcleaner.py
|
||||
|
||||
homeassistant/components/tradfri.py
|
||||
@ -399,6 +405,7 @@ omit =
|
||||
homeassistant/components/notify/aws_sqs.py
|
||||
homeassistant/components/notify/ciscospark.py
|
||||
homeassistant/components/notify/clicksend.py
|
||||
homeassistant/components/notify/clicksendaudio.py
|
||||
homeassistant/components/notify/discord.py
|
||||
homeassistant/components/notify/facebook.py
|
||||
homeassistant/components/notify/free_mobile.py
|
||||
@ -538,6 +545,7 @@ omit =
|
||||
homeassistant/components/sensor/tank_utility.py
|
||||
homeassistant/components/sensor/ted5000.py
|
||||
homeassistant/components/sensor/temper.py
|
||||
homeassistant/components/sensor/tibber.py
|
||||
homeassistant/components/sensor/time_date.py
|
||||
homeassistant/components/sensor/torque.py
|
||||
homeassistant/components/sensor/transmission.py
|
||||
@ -581,7 +589,6 @@ omit =
|
||||
homeassistant/components/thingspeak.py
|
||||
homeassistant/components/tts/amazon_polly.py
|
||||
homeassistant/components/tts/picotts.py
|
||||
homeassistant/components/upnp.py
|
||||
homeassistant/components/vacuum/roomba.py
|
||||
homeassistant/components/weather/bom.py
|
||||
homeassistant/components/weather/buienradar.py
|
||||
|
@ -39,3 +39,7 @@ homeassistant/components/*/zwave.py @home-assistant/z-wave
|
||||
homeassistant/components/cover/template.py @PhracturedBlue
|
||||
homeassistant/components/device_tracker/automatic.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
|
||||
|
@ -11,9 +11,10 @@ MAINTAINER Paulus Schoutsen <Paulus@PaulusSchoutsen.nl>
|
||||
#ENV INSTALL_FFMPEG no
|
||||
#ENV INSTALL_LIBCEC no
|
||||
#ENV INSTALL_PHANTOMJS no
|
||||
#ENV INSTALL_COAP_CLIENT no
|
||||
#ENV INSTALL_COAP no
|
||||
#ENV INSTALL_SSOCR no
|
||||
|
||||
|
||||
VOLUME /config
|
||||
|
||||
RUN mkdir -p /usr/src/app
|
||||
@ -25,7 +26,6 @@ RUN virtualization/Docker/setup_docker_prereqs
|
||||
|
||||
# Install hass component dependencies
|
||||
COPY requirements_all.txt requirements_all.txt
|
||||
|
||||
# Uninstall enum34 because some depenndecies install it but breaks Python 3.4+.
|
||||
# See PR #8103 for more info.
|
||||
RUN pip3 install --no-cache-dir -r requirements_all.txt && \
|
||||
|
@ -117,7 +117,11 @@ def linkcode_resolve(domain, info):
|
||||
linespec = "#L%d" % (lineno + 1)
|
||||
else:
|
||||
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)
|
||||
|
||||
|
@ -83,6 +83,18 @@ def async_from_config_dict(config: Dict[str, Any],
|
||||
This method is a coroutine.
|
||||
"""
|
||||
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, {})
|
||||
|
||||
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)
|
||||
|
||||
if enable_log:
|
||||
async_enable_logging(hass, verbose, log_rotate_days, log_file)
|
||||
|
||||
hass.config.skip_pip = skip_pip
|
||||
if skip_pip:
|
||||
_LOGGER.warning("Skipping pip installation of required modules. "
|
||||
|
@ -21,7 +21,7 @@ from homeassistant.const import (ATTR_ATTRIBUTION, ATTR_DATE, ATTR_TIME,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
EVENT_HOMEASSISTANT_START)
|
||||
|
||||
REQUIREMENTS = ['abodepy==0.11.8']
|
||||
REQUIREMENTS = ['abodepy==0.11.9']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -107,7 +107,7 @@ class Concord232Alarm(alarm.AlarmControlPanel):
|
||||
newstate = STATE_ALARM_ARMED_AWAY
|
||||
|
||||
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
|
||||
return self._state
|
||||
|
||||
|
@ -18,13 +18,14 @@ from homeassistant.const import (
|
||||
CONF_NAME, STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME,
|
||||
STATE_ALARM_ARMED_AWAY, STATE_ALARM_TRIGGERED)
|
||||
|
||||
REQUIREMENTS = ['pythonegardia==1.0.20']
|
||||
REQUIREMENTS = ['pythonegardia==1.0.21']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_REPORT_SERVER_CODES = 'report_server_codes'
|
||||
CONF_REPORT_SERVER_ENABLED = 'report_server_enabled'
|
||||
CONF_REPORT_SERVER_PORT = 'report_server_port'
|
||||
CONF_REPORT_SERVER_CODES_IGNORE = 'ignore'
|
||||
|
||||
DEFAULT_NAME = 'Egardia'
|
||||
DEFAULT_PORT = 80
|
||||
@ -148,9 +149,15 @@ class EgardiaAlarm(alarm.AlarmControlPanel):
|
||||
|
||||
def parsestatus(self, status):
|
||||
"""Parse the status."""
|
||||
newstatus = ([v for k, v in STATES.items()
|
||||
if status.upper() == k][0])
|
||||
self._status = newstatus
|
||||
_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()
|
||||
if status.upper() == k][0])
|
||||
self._status = newstatus
|
||||
else:
|
||||
_LOGGER.error("Ignoring status")
|
||||
|
||||
def update(self):
|
||||
"""Update the alarm status."""
|
||||
|
@ -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/
|
||||
"""
|
||||
import asyncio
|
||||
import copy
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
@ -13,9 +14,9 @@ import voluptuous as vol
|
||||
import homeassistant.components.alarm_control_panel as alarm
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.const import (
|
||||
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
|
||||
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, CONF_PLATFORM,
|
||||
CONF_NAME, CONF_CODE, CONF_PENDING_TIME, CONF_TRIGGER_TIME,
|
||||
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT,
|
||||
STATE_ALARM_DISARMED, STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED,
|
||||
CONF_PLATFORM, CONF_NAME, CONF_CODE, CONF_PENDING_TIME, CONF_TRIGGER_TIME,
|
||||
CONF_DISARM_AFTER_TRIGGER)
|
||||
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_ARM_HOME = 'payload_arm_home'
|
||||
CONF_PAYLOAD_ARM_AWAY = 'payload_arm_away'
|
||||
CONF_PAYLOAD_ARM_NIGHT = 'payload_arm_night'
|
||||
|
||||
DEFAULT_ALARM_NAME = 'HA Alarm'
|
||||
DEFAULT_PENDING_TIME = 60
|
||||
@ -35,11 +37,32 @@ DEFAULT_TRIGGER_TIME = 120
|
||||
DEFAULT_DISARM_AFTER_TRIGGER = False
|
||||
DEFAULT_ARM_AWAY = 'ARM_AWAY'
|
||||
DEFAULT_ARM_HOME = 'ARM_HOME'
|
||||
DEFAULT_ARM_NIGHT = 'ARM_NIGHT'
|
||||
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']
|
||||
|
||||
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.Optional(CONF_NAME, default=DEFAULT_ALARM_NAME): 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.Optional(CONF_DISARM_AFTER_TRIGGER,
|
||||
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_STATE_TOPIC): mqtt.valid_subscribe_topic,
|
||||
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_NIGHT, default=DEFAULT_ARM_NIGHT): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_DISARM, default=DEFAULT_DISARM): cv.string,
|
||||
})
|
||||
}), _state_validator))
|
||||
|
||||
_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(CONF_PAYLOAD_DISARM),
|
||||
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):
|
||||
@ -89,7 +119,8 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
|
||||
def __init__(self, hass, name, code, pending_time,
|
||||
trigger_time, disarm_after_trigger,
|
||||
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."""
|
||||
self._state = STATE_ALARM_DISARMED
|
||||
self._hass = hass
|
||||
@ -101,12 +132,18 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
|
||||
self._pre_trigger_state = self._state
|
||||
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._command_topic = command_topic
|
||||
self._qos = qos
|
||||
self._payload_disarm = payload_disarm
|
||||
self._payload_arm_home = payload_arm_home
|
||||
self._payload_arm_away = payload_arm_away
|
||||
self._payload_arm_night = payload_arm_night
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
@ -121,23 +158,27 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
|
||||
@property
|
||||
def state(self):
|
||||
"""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_ts + self._pending_time > dt_util.utcnow():
|
||||
if self._within_pending_time(self._state):
|
||||
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():
|
||||
if self._disarm_after_trigger:
|
||||
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
|
||||
|
||||
def _within_pending_time(self, state):
|
||||
pending_time = self._pending_time_by_state[state]
|
||||
return self._state_ts + pending_time > dt_util.utcnow()
|
||||
|
||||
@property
|
||||
def code_format(self):
|
||||
"""One or more characters."""
|
||||
@ -157,44 +198,47 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
|
||||
if not self._validate_code(code, STATE_ALARM_ARMED_HOME):
|
||||
return
|
||||
|
||||
self._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)
|
||||
self._update_state(STATE_ALARM_ARMED_HOME)
|
||||
|
||||
def alarm_arm_away(self, code=None):
|
||||
"""Send arm away command."""
|
||||
if not self._validate_code(code, STATE_ALARM_ARMED_AWAY):
|
||||
return
|
||||
|
||||
self._state = STATE_ALARM_ARMED_AWAY
|
||||
self._state_ts = dt_util.utcnow()
|
||||
self.schedule_update_ha_state()
|
||||
self._update_state(STATE_ALARM_ARMED_AWAY)
|
||||
|
||||
if self._pending_time:
|
||||
track_point_in_time(
|
||||
self._hass, self.async_update_ha_state,
|
||||
self._state_ts + self._pending_time)
|
||||
def alarm_arm_night(self, code=None):
|
||||
"""Send arm night command."""
|
||||
if not self._validate_code(code, STATE_ALARM_ARMED_NIGHT):
|
||||
return
|
||||
|
||||
self._update_state(STATE_ALARM_ARMED_NIGHT)
|
||||
|
||||
def alarm_trigger(self, code=None):
|
||||
"""Send alarm trigger command. No code needed."""
|
||||
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.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(
|
||||
self._hass, self.async_update_ha_state,
|
||||
self._state_ts + self._pending_time)
|
||||
self._state_ts + pending_time)
|
||||
|
||||
track_point_in_time(
|
||||
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):
|
||||
"""Validate given code."""
|
||||
@ -203,6 +247,16 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
|
||||
_LOGGER.warning("Invalid code given for %s", state)
|
||||
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):
|
||||
"""Subscribe mqtt events.
|
||||
|
||||
@ -221,6 +275,8 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
|
||||
self.async_alarm_arm_home(self._code)
|
||||
elif payload == self._payload_arm_away:
|
||||
self.async_alarm_arm_away(self._code)
|
||||
elif payload == self._payload_arm_night:
|
||||
self.async_alarm_arm_night(self._code)
|
||||
else:
|
||||
_LOGGER.warning("Received unexpected payload: %s", payload)
|
||||
return
|
||||
|
@ -6,7 +6,9 @@ from uuid import uuid4
|
||||
from homeassistant.const import (
|
||||
ATTR_SUPPORTED_FEATURES, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF)
|
||||
from homeassistant.components import switch, light
|
||||
from homeassistant.util.decorator import Registry
|
||||
|
||||
HANDLERS = Registry()
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
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
|
||||
def async_handle_message(hass, message):
|
||||
"""Handle incomming API messages."""
|
||||
"""Handle incoming API messages."""
|
||||
assert int(message[ATTR_HEADER][ATTR_PAYLOAD_VERSION]) == 2
|
||||
|
||||
# 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:
|
||||
_LOGGER.warning(
|
||||
"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):
|
||||
"""Create a API formated response message.
|
||||
"""Create a API formatted response message.
|
||||
|
||||
Async friendly.
|
||||
"""
|
||||
payload = payload or {}
|
||||
return {
|
||||
ATTR_HEADER: {
|
||||
ATTR_MESSAGE_ID: uuid4(),
|
||||
ATTR_MESSAGE_ID: str(uuid4()),
|
||||
ATTR_NAME: name,
|
||||
ATTR_NAMESPACE: namespace,
|
||||
ATTR_PAYLOAD_VERSION: '2',
|
||||
@ -74,16 +62,17 @@ def api_message(name, namespace, payload=None):
|
||||
|
||||
|
||||
def api_error(request, exc='DriverInternalError'):
|
||||
"""Create a API formated error response.
|
||||
"""Create a API formatted error response.
|
||||
|
||||
Async friendly.
|
||||
"""
|
||||
return api_message(exc, request[ATTR_HEADER][ATTR_NAMESPACE])
|
||||
|
||||
|
||||
@HANDLERS.register('DiscoverAppliancesRequest')
|
||||
@asyncio.coroutine
|
||||
def async_api_discovery(hass, request):
|
||||
"""Create a API formated discovery response.
|
||||
"""Create a API formatted discovery response.
|
||||
|
||||
Async friendly.
|
||||
"""
|
||||
@ -146,6 +135,7 @@ def extract_entity(funct):
|
||||
return async_api_entity_wrapper
|
||||
|
||||
|
||||
@HANDLERS.register('TurnOnRequest')
|
||||
@extract_entity
|
||||
@asyncio.coroutine
|
||||
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')
|
||||
|
||||
|
||||
@HANDLERS.register('TurnOffRequest')
|
||||
@extract_entity
|
||||
@asyncio.coroutine
|
||||
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')
|
||||
|
||||
|
||||
@HANDLERS.register('SetPercentageRequest')
|
||||
@extract_entity
|
||||
@asyncio.coroutine
|
||||
def async_api_set_percentage(hass, request, entity):
|
||||
|
@ -18,7 +18,7 @@ from homeassistant.helpers import discovery
|
||||
from homeassistant.components.discovery import SERVICE_APPLE_TV
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
REQUIREMENTS = ['pyatv==0.3.4']
|
||||
REQUIREMENTS = ['pyatv==0.3.5']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -12,7 +12,7 @@ from requests.exceptions import HTTPError, ConnectTimeout
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
|
||||
|
||||
REQUIREMENTS = ['pyarlo==0.0.4']
|
||||
REQUIREMENTS = ['pyarlo==0.0.6']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -55,12 +55,12 @@ class InsteonPLMBinarySensorDevice(BinarySensorDevice):
|
||||
|
||||
@property
|
||||
def address(self):
|
||||
"""Return the the address of the node."""
|
||||
"""Return the address of the node."""
|
||||
return self._address
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the the name of the node."""
|
||||
"""Return the name of the node."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
|
70
homeassistant/components/binary_sensor/raincloud.py
Normal file
70
homeassistant/components/binary_sensor/raincloud.py
Normal 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)
|
@ -20,15 +20,18 @@ from homeassistant.helpers.event import async_track_state_change
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ATTR_HYSTERESIS = 'hysteresis'
|
||||
ATTR_SENSOR_VALUE = 'sensor_value'
|
||||
ATTR_THRESHOLD = 'threshold'
|
||||
ATTR_TYPE = 'type'
|
||||
|
||||
CONF_HYSTERESIS = 'hysteresis'
|
||||
CONF_LOWER = 'lower'
|
||||
CONF_THRESHOLD = 'threshold'
|
||||
CONF_UPPER = 'upper'
|
||||
|
||||
DEFAULT_NAME = 'Threshold'
|
||||
DEFAULT_HYSTERESIS = 0.0
|
||||
|
||||
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_THRESHOLD): vol.Coerce(float),
|
||||
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_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)
|
||||
name = config.get(CONF_NAME)
|
||||
threshold = config.get(CONF_THRESHOLD)
|
||||
hysteresis = config.get(CONF_HYSTERESIS)
|
||||
limit_type = config.get(CONF_TYPE)
|
||||
device_class = config.get(CONF_DEVICE_CLASS)
|
||||
|
||||
async_add_devices(
|
||||
[ThresholdSensor(hass, entity_id, name, threshold, limit_type,
|
||||
device_class)], True)
|
||||
async_add_devices([ThresholdSensor(
|
||||
hass, entity_id, name, threshold,
|
||||
hysteresis, limit_type, device_class)
|
||||
], True)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class ThresholdSensor(BinarySensorDevice):
|
||||
"""Representation of a Threshold sensor."""
|
||||
|
||||
def __init__(self, hass, entity_id, name, threshold, limit_type,
|
||||
device_class):
|
||||
def __init__(self, hass, entity_id, name, threshold,
|
||||
hysteresis, limit_type, device_class):
|
||||
"""Initialize the Threshold sensor."""
|
||||
self._hass = hass
|
||||
self._entity_id = entity_id
|
||||
self.is_upper = limit_type == 'upper'
|
||||
self._name = name
|
||||
self._threshold = threshold
|
||||
self._hysteresis = hysteresis
|
||||
self._device_class = device_class
|
||||
self._deviation = False
|
||||
self._state = False
|
||||
self.sensor_value = 0
|
||||
|
||||
@callback
|
||||
@ -97,7 +106,7 @@ class ThresholdSensor(BinarySensorDevice):
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return true if sensor is on."""
|
||||
return self._deviation
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
@ -116,13 +125,16 @@ class ThresholdSensor(BinarySensorDevice):
|
||||
ATTR_ENTITY_ID: self._entity_id,
|
||||
ATTR_SENSOR_VALUE: self.sensor_value,
|
||||
ATTR_THRESHOLD: self._threshold,
|
||||
ATTR_HYSTERESIS: self._hysteresis,
|
||||
ATTR_TYPE: CONF_UPPER if self.is_upper else CONF_LOWER,
|
||||
}
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_update(self):
|
||||
"""Get the latest data and updates the states."""
|
||||
if self.is_upper:
|
||||
self._deviation = bool(self.sensor_value > self._threshold)
|
||||
else:
|
||||
self._deviation = bool(self.sensor_value < self._threshold)
|
||||
if self._hysteresis == 0 and self.sensor_value == self._threshold:
|
||||
self._state = False
|
||||
elif self.sensor_value > (self._threshold + self._hysteresis):
|
||||
self._state = self.is_upper
|
||||
elif self.sensor_value < (self._threshold - self._hysteresis):
|
||||
self._state = not self.is_upper
|
||||
|
@ -136,8 +136,9 @@ class WinkHub(WinkBinarySensorDevice):
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
return {
|
||||
'update needed': self.wink.update_needed(),
|
||||
'firmware version': self.wink.firmware_version()
|
||||
'update_needed': self.wink.update_needed(),
|
||||
'firmware_version': self.wink.firmware_version(),
|
||||
'pairing_mode': self.wink.pairing_mode()
|
||||
}
|
||||
|
||||
|
||||
|
@ -277,7 +277,7 @@ class TodoistProjectData(object):
|
||||
"""
|
||||
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.
|
||||
|
||||
Takes an object with a 'name' field and optionally an 'id' field (either
|
||||
|
@ -62,7 +62,7 @@ class AmcrestCam(Camera):
|
||||
self._token = self._auth = authentication
|
||||
|
||||
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
|
||||
response = self._camera.snapshot(channel=self._resolution)
|
||||
return response.data
|
||||
|
@ -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.camera import Camera, PLATFORM_SCHEMA
|
||||
from homeassistant.components.ffmpeg import DATA_FFMPEG
|
||||
from homeassistant.const import ATTR_BATTERY_LEVEL
|
||||
|
||||
DEPENDENCIES = ['arlo', 'ffmpeg']
|
||||
|
||||
_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'
|
||||
|
||||
ARLO_MODE_ARMED = 'armed'
|
||||
ARLO_MODE_DISARMED = 'disarmed'
|
||||
|
||||
POWERSAVE_MODE_MAPPING = {
|
||||
1: 'best_battery_life',
|
||||
2: 'optimized',
|
||||
3: 'best_video'
|
||||
}
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string,
|
||||
})
|
||||
@ -80,6 +96,28 @@ class ArloCam(Camera):
|
||||
"""Return the name of this camera."""
|
||||
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
|
||||
def model(self):
|
||||
"""Camera model."""
|
||||
|
@ -76,6 +76,6 @@ class BlinkCamera(Camera):
|
||||
return self.data.camera_thumbs[self._name]
|
||||
|
||||
def camera_image(self):
|
||||
"""Return a still image reponse from the camera."""
|
||||
"""Return a still image response from the camera."""
|
||||
self.request_image()
|
||||
return self.response.content
|
||||
|
@ -59,7 +59,7 @@ class FoscamCam(Camera):
|
||||
self._password, verbose=False)
|
||||
|
||||
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
|
||||
# Handle exception if host is not reachable or url failed
|
||||
result, response = self._foscam_session.snap_picture_2()
|
||||
|
@ -7,44 +7,25 @@ https://home-assistant.io/components/camera.synology/
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
import requests
|
||||
import voluptuous as vol
|
||||
|
||||
import aiohttp
|
||||
import async_timeout
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_NAME, CONF_USERNAME, CONF_PASSWORD,
|
||||
CONF_URL, CONF_WHITELIST, CONF_VERIFY_SSL, CONF_TIMEOUT)
|
||||
from homeassistant.components.camera import (
|
||||
Camera, PLATFORM_SCHEMA)
|
||||
from homeassistant.helpers.aiohttp_client import (
|
||||
async_get_clientsession, async_create_clientsession,
|
||||
async_create_clientsession,
|
||||
async_aiohttp_proxy_web)
|
||||
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__)
|
||||
|
||||
DEFAULT_NAME = 'Synology Camera'
|
||||
DEFAULT_STREAM_ID = '0'
|
||||
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({
|
||||
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."""
|
||||
verify_ssl = config.get(CONF_VERIFY_SSL)
|
||||
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:
|
||||
with async_timeout.timeout(timeout, loop=hass.loop):
|
||||
query_req = yield from websession_init.get(
|
||||
syno_api_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)
|
||||
from synology.surveillance_station import SurveillanceStation
|
||||
surveillance = SurveillanceStation(
|
||||
config.get(CONF_URL),
|
||||
config.get(CONF_USERNAME),
|
||||
config.get(CONF_PASSWORD),
|
||||
verify_ssl=verify_ssl,
|
||||
timeout=timeout
|
||||
)
|
||||
except (requests.exceptions.RequestException, ValueError):
|
||||
_LOGGER.exception("Error when initializing SurveillanceStation")
|
||||
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_PASSWORD),
|
||||
syno_auth_url,
|
||||
timeout
|
||||
)
|
||||
|
||||
# init websession
|
||||
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
|
||||
|
||||
camera_resp = yield from camera_req.json(content_type=None)
|
||||
cameras = camera_resp['data']['cameras']
|
||||
cameras = surveillance.get_all_cameras()
|
||||
websession = async_create_clientsession(hass, verify_ssl)
|
||||
|
||||
# add cameras
|
||||
devices = []
|
||||
for camera in cameras:
|
||||
if not config.get(CONF_WHITELIST):
|
||||
camera_id = 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
|
||||
)
|
||||
device = SynologyCamera(websession, surveillance, camera.camera_id)
|
||||
devices.append(device)
|
||||
|
||||
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):
|
||||
"""An implementation of a Synology NAS based IP camera."""
|
||||
|
||||
def __init__(self, hass, websession, config, camera_id,
|
||||
camera_name, snapshot_path, streaming_path, camera_path,
|
||||
auth_path, timeout):
|
||||
def __init__(self, websession, surveillance, camera_id):
|
||||
"""Initialize a Synology Surveillance Station camera."""
|
||||
super().__init__()
|
||||
self.hass = hass
|
||||
self._websession = websession
|
||||
self._name = camera_name
|
||||
self._synology_url = config.get(CONF_URL)
|
||||
self._camera_name = config.get(CONF_CAMERA_NAME)
|
||||
self._stream_id = config.get(CONF_STREAM_ID)
|
||||
self._surveillance = surveillance
|
||||
self._camera_id = camera_id
|
||||
self._snapshot_path = snapshot_path
|
||||
self._streaming_path = streaming_path
|
||||
self._camera_path = camera_path
|
||||
self._auth_path = auth_path
|
||||
self._timeout = timeout
|
||||
self._camera = self._surveillance.get_camera(camera_id)
|
||||
self._motion_setting = self._surveillance.get_motion_setting(camera_id)
|
||||
self.is_streaming = self._camera.is_enabled
|
||||
|
||||
def camera_image(self):
|
||||
"""Return bytes of camera image."""
|
||||
return run_coroutine_threadsafe(
|
||||
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
|
||||
return self._surveillance.get_camera_image(self._camera_id)
|
||||
|
||||
@asyncio.coroutine
|
||||
def handle_async_mjpeg_stream(self, request):
|
||||
"""Return a MJPEG stream image response directly from the camera."""
|
||||
streaming_url = SYNO_API_URL.format(
|
||||
self._synology_url, WEBAPI_PATH, self._streaming_path)
|
||||
|
||||
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)
|
||||
streaming_url = self._camera.video_stream_url
|
||||
stream_coro = self._websession.get(streaming_url)
|
||||
|
||||
yield from async_aiohttp_proxy_web(self.hass, request, stream_coro)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""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)
|
||||
|
@ -44,6 +44,12 @@ STATE_IDLE = 'idle'
|
||||
STATE_AUTO = 'auto'
|
||||
STATE_DRY = 'dry'
|
||||
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_MAX_TEMP = 'max_temp'
|
||||
@ -147,7 +153,7 @@ def set_hold_mode(hass, hold_mode, entity_id=None):
|
||||
|
||||
@bind_hass
|
||||
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 = {
|
||||
ATTR_AUX_HEAT: aux_heat
|
||||
}
|
||||
@ -661,22 +667,22 @@ class ClimateDevice(Entity):
|
||||
return self.hass.async_add_job(self.set_hold_mode, hold_mode)
|
||||
|
||||
def turn_aux_heat_on(self):
|
||||
"""Turn auxillary heater on."""
|
||||
"""Turn auxiliary heater on."""
|
||||
raise NotImplementedError()
|
||||
|
||||
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.
|
||||
"""
|
||||
return self.hass.async_add_job(self.turn_aux_heat_on)
|
||||
|
||||
def turn_aux_heat_off(self):
|
||||
"""Turn auxillary heater off."""
|
||||
"""Turn auxiliary heater off."""
|
||||
raise NotImplementedError()
|
||||
|
||||
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.
|
||||
"""
|
||||
|
@ -114,7 +114,7 @@ class DemoClimate(ClimateDevice):
|
||||
|
||||
@property
|
||||
def is_aux_heat_on(self):
|
||||
"""Return true if away mode is on."""
|
||||
"""Return true if aux heat is on."""
|
||||
return self._aux
|
||||
|
||||
@property
|
||||
@ -183,11 +183,11 @@ class DemoClimate(ClimateDevice):
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def turn_aux_heat_on(self):
|
||||
"""Turn away auxillary heater on."""
|
||||
"""Turn auxillary heater on."""
|
||||
self._aux = True
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def turn_aux_heat_off(self):
|
||||
"""Turn auxillary heater off."""
|
||||
"""Turn auxiliary heater off."""
|
||||
self._aux = False
|
||||
self.schedule_update_ha_state()
|
||||
|
@ -27,6 +27,7 @@ ATTR_RESUME_ALL = 'resume_all'
|
||||
DEFAULT_RESUME_ALL = False
|
||||
TEMPERATURE_HOLD = 'temp'
|
||||
VACATION_HOLD = 'vacation'
|
||||
AWAY_MODE = 'awayMode'
|
||||
|
||||
DEPENDENCIES = ['ecobee']
|
||||
|
||||
@ -144,20 +145,20 @@ class Thermostat(ClimateDevice):
|
||||
@property
|
||||
def current_temperature(self):
|
||||
"""Return the current temperature."""
|
||||
return self.thermostat['runtime']['actualTemperature'] / 10
|
||||
return self.thermostat['runtime']['actualTemperature'] / 10.0
|
||||
|
||||
@property
|
||||
def target_temperature_low(self):
|
||||
"""Return the lower bound temperature we try to reach."""
|
||||
if self.current_operation == STATE_AUTO:
|
||||
return int(self.thermostat['runtime']['desiredHeat'] / 10)
|
||||
return self.thermostat['runtime']['desiredHeat'] / 10.0
|
||||
return None
|
||||
|
||||
@property
|
||||
def target_temperature_high(self):
|
||||
"""Return the upper bound temperature we try to reach."""
|
||||
if self.current_operation == STATE_AUTO:
|
||||
return int(self.thermostat['runtime']['desiredCool'] / 10)
|
||||
return self.thermostat['runtime']['desiredCool'] / 10.0
|
||||
return None
|
||||
|
||||
@property
|
||||
@ -166,9 +167,9 @@ class Thermostat(ClimateDevice):
|
||||
if self.current_operation == STATE_AUTO:
|
||||
return None
|
||||
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:
|
||||
return int(self.thermostat['runtime']['desiredCool'] / 10)
|
||||
return self.thermostat['runtime']['desiredCool'] / 10.0
|
||||
return None
|
||||
|
||||
@property
|
||||
@ -186,6 +187,11 @@ class Thermostat(ClimateDevice):
|
||||
@property
|
||||
def current_hold_mode(self):
|
||||
"""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']
|
||||
for event in events:
|
||||
if event['running']:
|
||||
@ -195,8 +201,8 @@ class Thermostat(ClimateDevice):
|
||||
int(event['startDate'][0:4]) <= 1:
|
||||
# A temporary hold from away climate is a hold
|
||||
return 'away'
|
||||
# A permanent hold from away climate is away_mode
|
||||
return None
|
||||
# A permanent hold from away climate
|
||||
return AWAY_MODE
|
||||
elif event['holdClimateRef'] != "":
|
||||
# Any other hold based on climate
|
||||
return event['holdClimateRef']
|
||||
@ -269,7 +275,7 @@ class Thermostat(ClimateDevice):
|
||||
@property
|
||||
def is_away_mode_on(self):
|
||||
"""Return true if away mode is on."""
|
||||
return self.current_hold_mode == 'away'
|
||||
return self._current_hold_mode == AWAY_MODE
|
||||
|
||||
@property
|
||||
def is_aux_heat_on(self):
|
||||
@ -277,12 +283,17 @@ class Thermostat(ClimateDevice):
|
||||
return 'auxHeat' in self.thermostat['equipmentStatus']
|
||||
|
||||
def turn_away_mode_on(self):
|
||||
"""Turn away on."""
|
||||
self.set_hold_mode('away')
|
||||
"""Turn away mode on by setting it on away hold indefinitely."""
|
||||
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):
|
||||
"""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):
|
||||
"""Set hold mode (away, home, temp, sleep, etc.)."""
|
||||
@ -299,7 +310,7 @@ class Thermostat(ClimateDevice):
|
||||
self.data.ecobee.resume_program(self.thermostat_index)
|
||||
else:
|
||||
if hold_mode == TEMPERATURE_HOLD:
|
||||
self.set_temp_hold(int(self.current_temperature))
|
||||
self.set_temp_hold(self.current_temperature)
|
||||
else:
|
||||
self.data.ecobee.set_climate_hold(
|
||||
self.thermostat_index, hold_mode, self.hold_preference())
|
||||
@ -325,15 +336,11 @@ class Thermostat(ClimateDevice):
|
||||
elif self.current_operation == STATE_COOL:
|
||||
heat_temp = temp - 20
|
||||
cool_temp = temp
|
||||
|
||||
self.data.ecobee.set_hold_temp(self.thermostat_index, cool_temp,
|
||||
heat_temp, self.hold_preference())
|
||||
_LOGGER.debug("Setting ecobee hold_temp to: low=%s, is=%s, "
|
||||
"cool=%s, is=%s", heat_temp, isinstance(
|
||||
heat_temp, (int, float)), cool_temp,
|
||||
isinstance(cool_temp, (int, float)))
|
||||
|
||||
self.update_without_throttle = True
|
||||
else:
|
||||
# In auto mode set temperature between
|
||||
heat_temp = temp - 10
|
||||
cool_temp = temp + 10
|
||||
self.set_auto_temp_hold(heat_temp, cool_temp)
|
||||
|
||||
def set_temperature(self, **kwargs):
|
||||
"""Set new target temperature."""
|
||||
@ -343,9 +350,9 @@ class Thermostat(ClimateDevice):
|
||||
|
||||
if self.current_operation == STATE_AUTO and low_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:
|
||||
self.set_temp_hold(int(temp))
|
||||
self.set_temp_hold(temp)
|
||||
else:
|
||||
_LOGGER.error(
|
||||
"Missing valid arguments for set_temperature in %s", kwargs)
|
||||
@ -364,7 +371,7 @@ class Thermostat(ClimateDevice):
|
||||
def resume_program(self, resume_all):
|
||||
"""Resume the thermostat schedule 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
|
||||
|
||||
def hold_preference(self):
|
||||
|
483
homeassistant/components/climate/mqtt.py
Normal file
483
homeassistant/components/climate/mqtt.py
Normal 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()
|
@ -1,5 +1,5 @@
|
||||
set_aux_heat:
|
||||
description: Turn auxillary heater on/off for climate device
|
||||
description: Turn auxiliary heater on/off for climate device
|
||||
|
||||
fields:
|
||||
entity_id:
|
||||
|
@ -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
|
||||
https://home-assistant.io/components/climate.wink/
|
||||
"""
|
||||
import logging
|
||||
import asyncio
|
||||
|
||||
from homeassistant.components.wink import WinkDevice, DOMAIN
|
||||
from homeassistant.components.climate import (
|
||||
STATE_AUTO, STATE_COOL, STATE_HEAT, ClimateDevice,
|
||||
ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW,
|
||||
ATTR_TEMPERATURE,
|
||||
ATTR_CURRENT_HUMIDITY)
|
||||
ATTR_TEMPERATURE, STATE_FAN_ONLY,
|
||||
ATTR_CURRENT_HUMIDITY, STATE_ECO, STATE_ELECTRIC,
|
||||
STATE_PERFORMANCE, STATE_HIGH_DEMAND,
|
||||
STATE_HEAT_PUMP, STATE_GAS)
|
||||
from homeassistant.const import (
|
||||
TEMP_CELSIUS, STATE_ON,
|
||||
STATE_OFF, STATE_UNKNOWN)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['wink']
|
||||
|
||||
STATE_AUX = 'aux'
|
||||
STATE_ECO = 'eco'
|
||||
STATE_FAN = 'fan'
|
||||
SPEED_LOW = 'low'
|
||||
SPEED_MEDIUM = 'medium'
|
||||
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_SMART_TEMPERATURE = "smart_temperature"
|
||||
ATTR_ECO_TARGET = "eco_target"
|
||||
@ -32,28 +47,26 @@ ATTR_OCCUPIED = "occupied"
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Set up the Wink thermostat."""
|
||||
"""Set up the Wink climate devices."""
|
||||
import pywink
|
||||
temp_unit = hass.config.units.temperature_unit
|
||||
for climate in pywink.get_thermostats():
|
||||
_id = climate.object_id() + climate.name()
|
||||
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():
|
||||
_id = climate.object_id() + climate.name()
|
||||
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
|
||||
class WinkThermostat(WinkDevice, ClimateDevice):
|
||||
"""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
|
||||
def async_added_to_hass(self):
|
||||
"""Callback when entity is added to hass."""
|
||||
@ -139,18 +152,12 @@ class WinkThermostat(WinkDevice, ClimateDevice):
|
||||
"""Return current operation ie. heat, cool, idle."""
|
||||
if not self.wink.is_on():
|
||||
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:
|
||||
current_op = STATE_UNKNOWN
|
||||
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
|
||||
return current_op
|
||||
|
||||
@property
|
||||
@ -199,11 +206,12 @@ class WinkThermostat(WinkDevice, ClimateDevice):
|
||||
@property
|
||||
def is_aux_heat_on(self):
|
||||
"""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 None
|
||||
|
||||
if self.wink.current_hvac_mode() == 'aux':
|
||||
return True
|
||||
elif self.wink.current_hvac_mode() == 'aux' and not self.wink.is_on():
|
||||
return False
|
||||
return None
|
||||
return False
|
||||
|
||||
def set_temperature(self, **kwargs):
|
||||
"""Set new target temperature."""
|
||||
@ -223,32 +231,27 @@ class WinkThermostat(WinkDevice, ClimateDevice):
|
||||
|
||||
def set_operation_mode(self, operation_mode):
|
||||
"""Set operation mode."""
|
||||
if operation_mode == STATE_HEAT:
|
||||
self.wink.set_operation_mode('heat_only')
|
||||
elif operation_mode == STATE_COOL:
|
||||
self.wink.set_operation_mode('cool_only')
|
||||
elif operation_mode == STATE_AUTO:
|
||||
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')
|
||||
op_mode_to_set = HA_STATE_TO_WINK.get(operation_mode)
|
||||
# The only way to disable aux heat is with the toggle
|
||||
if self.is_aux_heat_on and op_mode_to_set == STATE_HEAT:
|
||||
return
|
||||
self.wink.set_operation_mode(op_mode_to_set)
|
||||
|
||||
@property
|
||||
def operation_list(self):
|
||||
"""List of available operation modes."""
|
||||
op_list = ['off']
|
||||
modes = self.wink.hvac_modes()
|
||||
if 'cool_only' in modes:
|
||||
op_list.append(STATE_COOL)
|
||||
if 'heat_only' in modes or 'aux' in modes:
|
||||
op_list.append(STATE_HEAT)
|
||||
if 'auto' in modes:
|
||||
op_list.append(STATE_AUTO)
|
||||
if 'eco' in modes:
|
||||
op_list.append(STATE_ECO)
|
||||
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 turn_away_mode_on(self):
|
||||
@ -281,12 +284,12 @@ class WinkThermostat(WinkDevice, ClimateDevice):
|
||||
self.wink.set_fan_mode(fan.lower())
|
||||
|
||||
def turn_aux_heat_on(self):
|
||||
"""Turn auxillary heater on."""
|
||||
self.set_operation_mode(STATE_AUX)
|
||||
"""Turn auxiliary heater on."""
|
||||
self.wink.set_operation_mode('aux')
|
||||
|
||||
def turn_aux_heat_off(self):
|
||||
"""Turn auxillary heater off."""
|
||||
self.set_operation_mode(STATE_AUTO)
|
||||
"""Turn auxiliary heater off."""
|
||||
self.set_operation_mode(STATE_HEAT)
|
||||
|
||||
@property
|
||||
def min_temp(self):
|
||||
@ -344,11 +347,6 @@ class WinkThermostat(WinkDevice, ClimateDevice):
|
||||
class WinkAC(WinkDevice, ClimateDevice):
|
||||
"""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
|
||||
def temperature_unit(self):
|
||||
"""Return the unit of measurement."""
|
||||
@ -382,14 +380,10 @@ class WinkAC(WinkDevice, ClimateDevice):
|
||||
"""Return current operation ie. heat, cool, idle."""
|
||||
if not self.wink.is_on():
|
||||
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:
|
||||
current_op = STATE_UNKNOWN
|
||||
current_op = WINK_STATE_TO_HA.get(self.wink.current_hvac_mode())
|
||||
if current_op is None:
|
||||
current_op = STATE_UNKNOWN
|
||||
return current_op
|
||||
|
||||
@property
|
||||
@ -397,12 +391,14 @@ class WinkAC(WinkDevice, ClimateDevice):
|
||||
"""List of available operation modes."""
|
||||
op_list = ['off']
|
||||
modes = self.wink.modes()
|
||||
if 'cool_only' in modes:
|
||||
op_list.append(STATE_COOL)
|
||||
if 'auto_eco' in modes:
|
||||
op_list.append(STATE_ECO)
|
||||
if 'fan_only' in modes:
|
||||
op_list.append(STATE_FAN)
|
||||
for mode in modes:
|
||||
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):
|
||||
@ -412,30 +408,16 @@ class WinkAC(WinkDevice, ClimateDevice):
|
||||
|
||||
def set_operation_mode(self, operation_mode):
|
||||
"""Set operation mode."""
|
||||
if operation_mode == STATE_COOL:
|
||||
self.wink.set_operation_mode('cool_only')
|
||||
elif operation_mode == STATE_ECO:
|
||||
self.wink.set_operation_mode('auto_eco')
|
||||
elif operation_mode == STATE_OFF:
|
||||
self.wink.set_operation_mode('off')
|
||||
elif operation_mode == STATE_FAN:
|
||||
self.wink.set_operation_mode('fan_only')
|
||||
op_mode_to_set = HA_STATE_TO_WINK.get(operation_mode)
|
||||
if op_mode_to_set == 'eco':
|
||||
op_mode_to_set = 'auto_eco'
|
||||
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_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
|
||||
def current_fan_mode(self):
|
||||
"""Return the current fan mode."""
|
||||
@ -453,12 +435,97 @@ class WinkAC(WinkDevice, ClimateDevice):
|
||||
"""Return a list of available fan modes."""
|
||||
return [SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
|
||||
|
||||
def set_fan_mode(self, mode):
|
||||
def set_fan_mode(self, fan):
|
||||
"""Set fan speed."""
|
||||
if mode == SPEED_LOW:
|
||||
if fan == SPEED_LOW:
|
||||
speed = 0.4
|
||||
elif mode == SPEED_MEDIUM:
|
||||
elif fan == SPEED_MEDIUM:
|
||||
speed = 0.8
|
||||
elif mode == SPEED_HIGH:
|
||||
elif fan == SPEED_HIGH:
|
||||
speed = 1.0
|
||||
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()
|
||||
|
@ -21,8 +21,8 @@ from homeassistant.const import (
|
||||
CONF_NAME, CONF_VALUE_TEMPLATE, CONF_OPTIMISTIC, STATE_OPEN,
|
||||
STATE_CLOSED, STATE_UNKNOWN)
|
||||
from homeassistant.components.mqtt import (
|
||||
CONF_STATE_TOPIC, CONF_COMMAND_TOPIC, CONF_QOS, CONF_RETAIN,
|
||||
valid_publish_topic, valid_subscribe_topic)
|
||||
CONF_STATE_TOPIC, CONF_COMMAND_TOPIC, CONF_AVAILABILITY_TOPIC,
|
||||
CONF_QOS, CONF_RETAIN, valid_publish_topic, valid_subscribe_topic)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -37,6 +37,8 @@ CONF_SET_POSITION_TEMPLATE = 'set_position_template'
|
||||
CONF_PAYLOAD_OPEN = 'payload_open'
|
||||
CONF_PAYLOAD_CLOSE = 'payload_close'
|
||||
CONF_PAYLOAD_STOP = 'payload_stop'
|
||||
CONF_PAYLOAD_AVAILABLE = 'payload_available'
|
||||
CONF_PAYLOAD_NOT_AVAILABLE = 'payload_not_available'
|
||||
CONF_STATE_OPEN = 'state_open'
|
||||
CONF_STATE_CLOSED = 'state_closed'
|
||||
CONF_TILT_CLOSED_POSITION = 'tilt_closed_value'
|
||||
@ -50,6 +52,8 @@ DEFAULT_NAME = 'MQTT Cover'
|
||||
DEFAULT_PAYLOAD_OPEN = 'OPEN'
|
||||
DEFAULT_PAYLOAD_CLOSE = 'CLOSE'
|
||||
DEFAULT_PAYLOAD_STOP = 'STOP'
|
||||
DEFAULT_PAYLOAD_AVAILABLE = 'online'
|
||||
DEFAULT_PAYLOAD_NOT_AVAILABLE = 'offline'
|
||||
DEFAULT_OPTIMISTIC = False
|
||||
DEFAULT_RETAIN = False
|
||||
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_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
|
||||
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_NAME, default=DEFAULT_NAME): 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_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_CLOSED, default=STATE_CLOSED): cv.string,
|
||||
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_STATE_TOPIC),
|
||||
config.get(CONF_COMMAND_TOPIC),
|
||||
config.get(CONF_AVAILABILITY_TOPIC),
|
||||
config.get(CONF_TILT_COMMAND_TOPIC),
|
||||
config.get(CONF_TILT_STATUS_TOPIC),
|
||||
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_CLOSE),
|
||||
config.get(CONF_PAYLOAD_STOP),
|
||||
config.get(CONF_PAYLOAD_AVAILABLE),
|
||||
config.get(CONF_PAYLOAD_NOT_AVAILABLE),
|
||||
config.get(CONF_OPTIMISTIC),
|
||||
value_template,
|
||||
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):
|
||||
"""Representation of a cover that can be controlled using MQTT."""
|
||||
|
||||
def __init__(self, name, state_topic, command_topic, tilt_command_topic,
|
||||
tilt_status_topic, qos, retain, state_open, state_closed,
|
||||
payload_open, payload_close, payload_stop,
|
||||
def __init__(self, name, state_topic, command_topic, availability_topic,
|
||||
tilt_command_topic, tilt_status_topic, qos, retain,
|
||||
state_open, state_closed, payload_open, payload_close,
|
||||
payload_stop, payload_available, payload_not_available,
|
||||
optimistic, value_template, tilt_open_position,
|
||||
tilt_closed_position, tilt_min, tilt_max, tilt_optimistic,
|
||||
tilt_invert, position_topic, set_position_template):
|
||||
@ -143,12 +156,16 @@ class MqttCover(CoverDevice):
|
||||
self._name = name
|
||||
self._state_topic = state_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_status_topic = tilt_status_topic
|
||||
self._qos = qos
|
||||
self._payload_open = payload_open
|
||||
self._payload_close = payload_close
|
||||
self._payload_stop = payload_stop
|
||||
self._payload_available = payload_available
|
||||
self._payload_not_available = payload_not_available
|
||||
self._state_open = state_open
|
||||
self._state_closed = state_closed
|
||||
self._retain = retain
|
||||
@ -181,8 +198,8 @@ class MqttCover(CoverDevice):
|
||||
self.async_schedule_update_ha_state()
|
||||
|
||||
@callback
|
||||
def message_received(topic, payload, qos):
|
||||
"""Handle new MQTT message."""
|
||||
def state_message_received(topic, payload, qos):
|
||||
"""Handle new MQTT state messages."""
|
||||
if self._template is not None:
|
||||
payload = self._template.async_render_with_possible_json_value(
|
||||
payload)
|
||||
@ -205,12 +222,28 @@ class MqttCover(CoverDevice):
|
||||
|
||||
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:
|
||||
# Force into optimistic mode.
|
||||
self._optimistic = True
|
||||
else:
|
||||
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:
|
||||
self._tilt_optimistic = True
|
||||
@ -230,6 +263,11 @@ class MqttCover(CoverDevice):
|
||||
"""Return the name of the cover."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if cover is available."""
|
||||
return self._available
|
||||
|
||||
@property
|
||||
def is_closed(self):
|
||||
"""Return if the cover is closed."""
|
||||
|
116
homeassistant/components/cover/rflink.py
Normal file
116
homeassistant/components/cover/rflink.py
Normal 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")
|
@ -87,8 +87,8 @@ def async_setup(hass, config):
|
||||
|
||||
# Set up input boolean
|
||||
tasks.append(bootstrap.async_setup_component(
|
||||
hass, 'input_slider',
|
||||
{'input_slider': {
|
||||
hass, 'input_number',
|
||||
{'input_number': {
|
||||
'noise_allowance': {'icon': 'mdi:bell-ring',
|
||||
'min': 0,
|
||||
'max': 10,
|
||||
@ -163,7 +163,7 @@ def async_setup(hass, config):
|
||||
'scene.romantic_lights']))
|
||||
tasks2.append(group.Group.async_create_group(hass, 'Bedroom', [
|
||||
lights[0], switches[1], media_players[0],
|
||||
'input_slider.noise_allowance']))
|
||||
'input_number.noise_allowance']))
|
||||
tasks2.append(group.Group.async_create_group(hass, 'Kitchen', [
|
||||
lights[2], 'cover.kitchen_window', 'lock.kitchen_door']))
|
||||
tasks2.append(group.Group.async_create_group(hass, 'Doors', [
|
||||
|
@ -248,7 +248,7 @@ class Icloud(DeviceScanner):
|
||||
self._trusted_device, self._verification_code):
|
||||
raise PyiCloudException('Unknown failure')
|
||||
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)
|
||||
self._trusted_device = None
|
||||
self._verification_code = None
|
||||
|
@ -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
|
||||
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.components.mqtt as mqtt
|
||||
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.device_tracker import PLATFORM_SCHEMA
|
||||
|
||||
@ -25,6 +25,8 @@ REQUIREMENTS = ['libnacl==1.5.2']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
HANDLERS = decorator.Registry()
|
||||
|
||||
BEACON_DEV_ID = 'beacon'
|
||||
|
||||
CONF_MAX_GPS_ACCURACY = 'max_gps_accuracy'
|
||||
@ -32,17 +34,7 @@ CONF_SECRET = 'secret'
|
||||
CONF_WAYPOINT_IMPORT = 'waypoints'
|
||||
CONF_WAYPOINT_WHITELIST = 'waypoint_whitelist'
|
||||
|
||||
EVENT_TOPIC = 'owntracks/+/+/event'
|
||||
|
||||
LOCATION_TOPIC = 'owntracks/+/+'
|
||||
|
||||
VALIDATE_LOCATION = 'location'
|
||||
VALIDATE_TRANSITION = 'transition'
|
||||
VALIDATE_WAYPOINTS = 'waypoints'
|
||||
|
||||
WAYPOINT_LAT_KEY = 'lat'
|
||||
WAYPOINT_LON_KEY = 'lon'
|
||||
WAYPOINT_TOPIC = 'owntracks/{}/{}/waypoints'
|
||||
OWNTRACKS_TOPIC = 'owntracks/#'
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_MAX_GPS_ACCURACY): vol.Coerce(float),
|
||||
@ -72,300 +64,60 @@ def get_cipher():
|
||||
@asyncio.coroutine
|
||||
def async_setup_scanner(hass, config, async_see, discovery_info=None):
|
||||
"""Set up an OwnTracks tracker."""
|
||||
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)
|
||||
context = context_from_config(async_see, config)
|
||||
|
||||
mobile_beacons_active = defaultdict(list)
|
||||
regions_entered = defaultdict(list)
|
||||
|
||||
def decrypt_payload(topic, ciphertext):
|
||||
"""Decrypt encrypted payload."""
|
||||
@asyncio.coroutine
|
||||
def async_handle_mqtt_message(topic, payload, qos):
|
||||
"""Handle incoming OwnTracks message."""
|
||||
try:
|
||||
keylen, decrypt = get_cipher()
|
||||
except OSError:
|
||||
_LOGGER.warning(
|
||||
"Ignoring encrypted payload because libsodium not installed")
|
||||
return None
|
||||
|
||||
if isinstance(secret, dict):
|
||||
key = secret.get(topic)
|
||||
else:
|
||||
key = secret
|
||||
|
||||
if key is None:
|
||||
_LOGGER.warning(
|
||||
"Ignoring encrypted payload because no decryption key known "
|
||||
"for topic %s", topic)
|
||||
return None
|
||||
|
||||
key = key.encode("utf-8")
|
||||
key = key[:keylen]
|
||||
key = key.ljust(keylen, b'\0')
|
||||
|
||||
try:
|
||||
ciphertext = base64.b64decode(ciphertext)
|
||||
message = decrypt(ciphertext, key)
|
||||
message = message.decode("utf-8")
|
||||
_LOGGER.debug("Decrypted payload: %s", message)
|
||||
return message
|
||||
except ValueError:
|
||||
_LOGGER.warning(
|
||||
"Ignoring encrypted payload because unable to decrypt using "
|
||||
"key for topic %s", topic)
|
||||
return None
|
||||
|
||||
def validate_payload(topic, payload, data_type):
|
||||
"""Validate the OwnTracks payload."""
|
||||
try:
|
||||
data = json.loads(payload)
|
||||
message = json.loads(payload)
|
||||
except ValueError:
|
||||
# If invalid JSON
|
||||
_LOGGER.error("Unable to parse payload as JSON: %s", payload)
|
||||
return None
|
||||
|
||||
if isinstance(data, dict) and \
|
||||
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)
|
||||
message['topic'] = topic
|
||||
|
||||
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(
|
||||
"Ignoring %s update because GPS accuracy is zero: %s",
|
||||
data_type, payload)
|
||||
return None
|
||||
|
||||
return data
|
||||
|
||||
@callback
|
||||
def async_owntracks_location_update(topic, payload, qos):
|
||||
"""MQTT message received."""
|
||||
# Docs on available data:
|
||||
# http://owntracks.org/booklet/tech/json/#_typelocation
|
||||
data = validate_payload(topic, payload, VALIDATE_LOCATION)
|
||||
if not data:
|
||||
return
|
||||
|
||||
dev_id, kwargs = _parse_see_args(topic, data)
|
||||
|
||||
if regions_entered[dev_id]:
|
||||
_LOGGER.debug(
|
||||
"Location update ignored, inside region %s",
|
||||
regions_entered[-1])
|
||||
return
|
||||
|
||||
hass.async_add_job(async_see(**kwargs))
|
||||
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:
|
||||
_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 = data['desc'].lstrip("-")
|
||||
if location.lower() == 'home':
|
||||
location = STATE_HOME
|
||||
|
||||
dev_id, kwargs = _parse_see_args(topic, data)
|
||||
|
||||
def enter_event():
|
||||
"""Execute enter event."""
|
||||
zone = hass.states.get("zone.{}".format(slugify(location)))
|
||||
if zone is None and data.get('t') == 'b':
|
||||
# Not a HA zone, and a beacon so assume mobile
|
||||
beacons = mobile_beacons_active[dev_id]
|
||||
if location not in beacons:
|
||||
beacons.append(location)
|
||||
_LOGGER.info("Added beacon %s", location)
|
||||
else:
|
||||
# Normal region
|
||||
regions = regions_entered[dev_id]
|
||||
if location not in regions:
|
||||
regions.append(location)
|
||||
_LOGGER.info("Enter region %s", location)
|
||||
_set_gps_from_zone(kwargs, location, zone)
|
||||
|
||||
hass.async_add_job(async_see(**kwargs))
|
||||
async_see_beacons(dev_id, kwargs)
|
||||
|
||||
def leave_event():
|
||||
"""Execute leave event."""
|
||||
regions = regions_entered[dev_id]
|
||||
if location in regions:
|
||||
regions.remove(location)
|
||||
new_region = regions[-1] if regions else None
|
||||
|
||||
if new_region:
|
||||
# Exit to previous region
|
||||
zone = hass.states.get(
|
||||
"zone.{}".format(slugify(new_region)))
|
||||
_set_gps_from_zone(kwargs, new_region, zone)
|
||||
_LOGGER.info("Exit to %s", new_region)
|
||||
hass.async_add_job(async_see(**kwargs))
|
||||
async_see_beacons(dev_id, kwargs)
|
||||
|
||||
else:
|
||||
_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]
|
||||
if location in beacons:
|
||||
beacons.remove(location)
|
||||
_LOGGER.info("Remove beacon %s", location)
|
||||
|
||||
if data['event'] == 'enter':
|
||||
enter_event()
|
||||
elif data['event'] == 'leave':
|
||||
leave_event()
|
||||
else:
|
||||
_LOGGER.error(
|
||||
"Misformatted mqtt msgs, _type=transition, event=%s",
|
||||
data['event'])
|
||||
return
|
||||
|
||||
@callback
|
||||
def async_owntracks_waypoint_update(topic, payload, qos):
|
||||
"""List of waypoints published by a user."""
|
||||
# Docs on available data:
|
||||
# http://owntracks.org/booklet/tech/json/#_typewaypoints
|
||||
data = validate_payload(topic, payload, VALIDATE_WAYPOINTS)
|
||||
if not data:
|
||||
return
|
||||
|
||||
wayps = data['waypoints']
|
||||
_LOGGER.info("Got %d waypoints from %s", len(wayps), topic)
|
||||
for wayp in wayps:
|
||||
name = wayp['desc']
|
||||
pretty_name = parse_topic(topic, True)[1] + ' - ' + name
|
||||
lat = wayp[WAYPOINT_LAT_KEY]
|
||||
lon = wayp[WAYPOINT_LON_KEY]
|
||||
rad = wayp['rad']
|
||||
|
||||
# check zone exists
|
||||
entity_id = zone_comp.ENTITY_ID_FORMAT.format(slugify(pretty_name))
|
||||
|
||||
# Check if state already exists
|
||||
if hass.states.get(entity_id) is not None:
|
||||
continue
|
||||
|
||||
zone = zone_comp.Zone(hass, pretty_name, lat, lon, rad,
|
||||
zone_comp.ICON_IMPORT, False)
|
||||
zone.entity_id = entity_id
|
||||
hass.async_add_job(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 async_handle_message(hass, context, message)
|
||||
|
||||
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)
|
||||
hass, OWNTRACKS_TOPIC, async_handle_mqtt_message, 1)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def parse_topic(topic, pretty=False):
|
||||
def _parse_topic(topic):
|
||||
"""Parse an MQTT topic owntracks/user/dev, return (user, dev) tuple.
|
||||
|
||||
Async friendly.
|
||||
"""
|
||||
parts = topic.split('/')
|
||||
dev_id_format = ''
|
||||
if pretty:
|
||||
dev_id_format = '{} {}'
|
||||
else:
|
||||
dev_id_format = '{}_{}'
|
||||
dev_id = slugify(dev_id_format.format(parts[1], parts[2]))
|
||||
host_name = parts[1]
|
||||
return (host_name, dev_id)
|
||||
_, user, device, *_ = topic.split('/', 3)
|
||||
|
||||
return user, device
|
||||
|
||||
|
||||
def _parse_see_args(topic, data):
|
||||
def _parse_see_args(message):
|
||||
"""Parse the OwnTracks location parameters, into the format see expects.
|
||||
|
||||
Async friendly.
|
||||
"""
|
||||
(host_name, dev_id) = parse_topic(topic, False)
|
||||
user, device = _parse_topic(message['topic'])
|
||||
dev_id = slugify('{}_{}'.format(user, device))
|
||||
kwargs = {
|
||||
'dev_id': dev_id,
|
||||
'host_name': host_name,
|
||||
'gps': (data[WAYPOINT_LAT_KEY], data[WAYPOINT_LON_KEY]),
|
||||
'host_name': user,
|
||||
'gps': (message['lat'], message['lon']),
|
||||
'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']
|
||||
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
|
||||
|
||||
@ -382,3 +134,280 @@ def _set_gps_from_zone(kwargs, location, zone):
|
||||
kwargs['gps_accuracy'] = zone.attributes['radius']
|
||||
kwargs['location_name'] = location
|
||||
return kwargs
|
||||
|
||||
|
||||
def _decrypt_payload(secret, topic, ciphertext):
|
||||
"""Decrypt encrypted payload."""
|
||||
try:
|
||||
keylen, decrypt = get_cipher()
|
||||
except OSError:
|
||||
_LOGGER.warning(
|
||||
"Ignoring encrypted payload because libsodium not installed")
|
||||
return None
|
||||
|
||||
if isinstance(secret, dict):
|
||||
key = secret.get(topic)
|
||||
else:
|
||||
key = secret
|
||||
|
||||
if key is None:
|
||||
_LOGGER.warning(
|
||||
"Ignoring encrypted payload because no decryption key known "
|
||||
"for topic %s", topic)
|
||||
return None
|
||||
|
||||
key = key.encode("utf-8")
|
||||
key = key[:keylen]
|
||||
key = key.ljust(keylen, b'\0')
|
||||
|
||||
try:
|
||||
ciphertext = base64.b64decode(ciphertext)
|
||||
message = decrypt(ciphertext, key)
|
||||
message = message.decode("utf-8")
|
||||
_LOGGER.debug("Decrypted payload: %s", message)
|
||||
return message
|
||||
except ValueError:
|
||||
_LOGGER.warning(
|
||||
"Ignoring encrypted payload because unable to decrypt using "
|
||||
"key for topic %s", topic)
|
||||
return None
|
||||
|
||||
|
||||
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:
|
||||
acc = float(acc)
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
if acc == 0:
|
||||
_LOGGER.warning(
|
||||
"Ignoring %s update because GPS accuracy is zero: %s",
|
||||
message['_type'], message)
|
||||
return False
|
||||
|
||||
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
|
||||
|
||||
return True
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_see_beacons(self, 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 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
|
||||
|
||||
dev_id, kwargs = _parse_see_args(message)
|
||||
|
||||
if context.regions_entered[dev_id]:
|
||||
_LOGGER.debug(
|
||||
"Location update ignored, inside region %s",
|
||||
context.regions_entered[-1])
|
||||
return
|
||||
|
||||
yield from context.async_see(**kwargs)
|
||||
yield from context.async_see_beacons(dev_id, kwargs)
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def _async_transition_message_enter(hass, context, message, location):
|
||||
"""Execute enter event."""
|
||||
zone = hass.states.get("zone.{}".format(slugify(location)))
|
||||
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
|
||||
beacons = context.mobile_beacons_active[dev_id]
|
||||
if location not in beacons:
|
||||
beacons.append(location)
|
||||
_LOGGER.info("Added beacon %s", location)
|
||||
else:
|
||||
# Normal region
|
||||
regions = context.regions_entered[dev_id]
|
||||
if location not in regions:
|
||||
regions.append(location)
|
||||
_LOGGER.info("Enter region %s", location)
|
||||
_set_gps_from_zone(kwargs, location, zone)
|
||||
|
||||
yield from context.async_see(**kwargs)
|
||||
yield from context.async_see_beacons(dev_id, kwargs)
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def _async_transition_message_leave(hass, context, message, location):
|
||||
"""Execute leave event."""
|
||||
dev_id, kwargs = _parse_see_args(message)
|
||||
regions = context.regions_entered[dev_id]
|
||||
|
||||
if location in regions:
|
||||
regions.remove(location)
|
||||
|
||||
new_region = regions[-1] if regions else None
|
||||
|
||||
if new_region:
|
||||
# Exit to previous region
|
||||
zone = hass.states.get(
|
||||
"zone.{}".format(slugify(new_region)))
|
||||
_set_gps_from_zone(kwargs, new_region, zone)
|
||||
_LOGGER.info("Exit to %s", new_region)
|
||||
yield from context.async_see(**kwargs)
|
||||
yield from context.async_see_beacons(dev_id, kwargs)
|
||||
return
|
||||
|
||||
else:
|
||||
_LOGGER.info("Exit to GPS")
|
||||
|
||||
# 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:
|
||||
beacons.remove(location)
|
||||
_LOGGER.info("Remove beacon %s", location)
|
||||
|
||||
|
||||
@HANDLERS.register('transition')
|
||||
@asyncio.coroutine
|
||||
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:
|
||||
_LOGGER.error(
|
||||
"Misformatted mqtt msgs, _type=transition, event=%s",
|
||||
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
|
||||
|
||||
if context.waypoint_whitelist is not None:
|
||||
user = _parse_topic(message['topic'])[0]
|
||||
|
||||
if user not in context.waypoint_whitelist:
|
||||
return
|
||||
|
||||
wayps = message['waypoints']
|
||||
|
||||
_LOGGER.info("Got %d waypoints from %s", len(wayps), message['topic'])
|
||||
|
||||
name_base = ' '.join(_parse_topic(message['topic']))
|
||||
|
||||
for wayp in wayps:
|
||||
name = wayp['desc']
|
||||
pretty_name = '{} - {}'.format(name_base, name)
|
||||
lat = wayp['lat']
|
||||
lon = wayp['lon']
|
||||
rad = wayp['rad']
|
||||
|
||||
# check zone exists
|
||||
entity_id = zone_comp.ENTITY_ID_FORMAT.format(slugify(pretty_name))
|
||||
|
||||
# Check if state already exists
|
||||
if hass.states.get(entity_id) is not None:
|
||||
continue
|
||||
|
||||
zone = zone_comp.Zone(hass, pretty_name, lat, lon, rad,
|
||||
zone_comp.ICON_IMPORT, False)
|
||||
zone.entity_id = entity_id
|
||||
yield from zone.async_update_ha_state()
|
||||
|
||||
|
||||
@HANDLERS.register('encrypted')
|
||||
@asyncio.coroutine
|
||||
def async_handle_encrypted_message(hass, context, message):
|
||||
"""Handle an encrypted message."""
|
||||
plaintext_payload = _decrypt_payload(context.secret, message['topic'],
|
||||
message['data'])
|
||||
|
||||
if plaintext_payload is None:
|
||||
return
|
||||
|
||||
decrypted = json.loads(plaintext_payload)
|
||||
decrypted['topic'] = message['topic']
|
||||
|
||||
yield from async_handle_message(hass, context, decrypted)
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_handle_message(hass, context, message):
|
||||
"""Handle an OwnTracks message."""
|
||||
msgtype = message.get('_type')
|
||||
|
||||
handler = HANDLERS.get(msgtype)
|
||||
|
||||
if handler is None:
|
||||
error = 'Received unsupported message type: {}.'.format(msgtype)
|
||||
_LOGGER.warning(error)
|
||||
|
||||
yield from handler(hass, context, message)
|
||||
|
54
homeassistant/components/device_tracker/owntracks_http.py
Normal file
54
homeassistant/components/device_tracker/owntracks_http.py
Normal 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
|
@ -75,7 +75,7 @@ class SnmpScanner(DeviceScanner):
|
||||
return [client['mac'] for client in self.last_results
|
||||
if client.get('mac')]
|
||||
|
||||
# Supressing no-self-use warning
|
||||
# Suppressing no-self-use warning
|
||||
# pylint: disable=R0201
|
||||
def get_device_name(self, device):
|
||||
"""Return the name of the given device or None if we don't know."""
|
||||
|
@ -69,7 +69,7 @@ class XiaomiDeviceScanner(DeviceScanner):
|
||||
return self.mac2name.get(device.upper(), None)
|
||||
|
||||
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.
|
||||
"""
|
||||
|
@ -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
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
REQUIREMENTS = ['netdisco==1.2.0']
|
||||
REQUIREMENTS = ['netdisco==1.2.2']
|
||||
|
||||
DOMAIN = 'discovery'
|
||||
|
||||
|
@ -122,7 +122,7 @@ def setup(hass, config):
|
||||
_LOGGER.info("Downloading of %s done", url)
|
||||
|
||||
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
|
||||
if final_path and os.path.isfile(final_path):
|
||||
|
102
homeassistant/components/duckdns.py
Normal file
102
homeassistant/components/duckdns.py
Normal 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
|
@ -15,7 +15,7 @@ from homeassistant.helpers import discovery
|
||||
from homeassistant.const import CONF_API_KEY
|
||||
from homeassistant.util import Throttle
|
||||
|
||||
REQUIREMENTS = ['python-ecobee-api==0.0.9']
|
||||
REQUIREMENTS = ['python-ecobee-api==0.0.10']
|
||||
|
||||
_CONFIGURING = {}
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -16,6 +16,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.components.http import REQUIREMENTS # NOQA
|
||||
from homeassistant.components.http import HomeAssistantWSGI
|
||||
from homeassistant.helpers.deprecation import get_deprecated
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from .hue_api import (
|
||||
HueUsernameView, HueAllLightsStateView, HueOneLightStateView,
|
||||
@ -66,6 +67,7 @@ CONFIG_SCHEMA = vol.Schema({
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
ATTR_EMULATED_HUE = 'emulated_hue'
|
||||
ATTR_EMULATED_HUE_HIDDEN = 'emulated_hue_hidden'
|
||||
|
||||
|
||||
def setup(hass, yaml_config):
|
||||
@ -148,7 +150,7 @@ class Config(object):
|
||||
self.listen_port)
|
||||
|
||||
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")
|
||||
|
||||
# Get whether or not UPNP binds to multicast address (239.255.255.250)
|
||||
@ -223,7 +225,15 @@ class Config(object):
|
||||
|
||||
domain = entity.domain.lower()
|
||||
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 = \
|
||||
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
|
||||
# exposed, or if the entity is explicitly 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):
|
||||
"""Set up helper method to load numbers json."""
|
||||
|
@ -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 socket
|
||||
import logging
|
||||
@ -123,14 +123,14 @@ USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1
|
||||
if ssdp_socket in read:
|
||||
data, addr = ssdp_socket.recvfrom(1024)
|
||||
else:
|
||||
# most likely the timeout, so check for interupt
|
||||
# most likely the timeout, so check for interrupt
|
||||
continue
|
||||
except socket.error as ex:
|
||||
if self._interrupted:
|
||||
clean_socket_close(ssdp_socket)
|
||||
return
|
||||
|
||||
_LOGGER.error("UPNP Responder socket exception occured: %s",
|
||||
_LOGGER.error("UPNP Responder socket exception occurred: %s",
|
||||
ex.__str__)
|
||||
# without the following continue, a second exception occurs
|
||||
# because the data object has not been initialized
|
||||
|
@ -137,7 +137,7 @@ class InsteonLocalFanDevice(FanEntity):
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the the name of the node."""
|
||||
"""Return the name of the node."""
|
||||
return self.node.deviceName
|
||||
|
||||
@property
|
||||
|
@ -3,10 +3,10 @@
|
||||
FINGERPRINTS = {
|
||||
"compatibility.js": "1686167ff210e001f063f5c606b2e74b",
|
||||
"core.js": "2a7d01e45187c7d4635da05065b5e54e",
|
||||
"frontend.html": "7e13ce36d3141182a62a5b061e87e77a",
|
||||
"mdi.html": "89074face5529f5fe6fbae49ecb3e88b",
|
||||
"frontend.html": "2de1bde3b4a6c6c47dd95504fc098906",
|
||||
"mdi.html": "2e848b4da029bf73d426d5ba058a088d",
|
||||
"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-info.html": "b0e55eb657fd75f21aba2426ac0cedc0",
|
||||
"panels/ha-panel-dev-mqtt.html": "94b222b013a98583842de3e72d5888c6",
|
||||
|
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -1 +1 @@
|
||||
Subproject commit a46e6b4cfa24d99011a9755e2588d761d78af152
|
||||
Subproject commit b0791abb9a61216476cb3a637c410cdddef7e91c
|
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -37,7 +37,7 @@
|
||||
/* eslint-disable indent, no-unused-vars, no-multiple-empty-lines, max-nested-callbacks, space-before-function-paren, quotes, comma-spacing */
|
||||
'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 : '');
|
||||
|
||||
|
||||
|
Binary file not shown.
@ -7,6 +7,13 @@
|
||||
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) 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.
|
||||
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
|
||||
@ -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
|
||||
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) 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;
|
||||
(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=
|
||||
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);
|
||||
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,
|
||||
!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,
|
||||
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,
|
||||
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=
|
||||
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]);
|
||||
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])}}})}
|
||||
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)&&
|
||||
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&&
|
||||
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});
|
||||
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
|
||||
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`.");
|
||||
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;
|
||||
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__=
|
||||
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||
|
||||
(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=
|
||||
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,
|
||||
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)},
|
||||
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,
|
||||
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."));
|
||||
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"))}
|
||||
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();
|
||||
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()();
|
||||
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"&";case "<":return"<";case ">":return">";case '"':return""";case "\u00a0":return" "}}
|
||||
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=
|
||||
"\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=
|
||||
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,
|
||||
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;
|
||||
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&&
|
||||
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.__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: "+
|
||||
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);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=
|
||||
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,
|
||||
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,
|
||||
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)||
|
||||
-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,
|
||||
"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||
|
||||
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=
|
||||
"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=
|
||||
!!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.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[];
|
||||
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),
|
||||
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,
|
||||
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,
|
||||
$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?
|
||||
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=
|
||||
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,
|
||||
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,
|
||||
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=
|
||||
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);
|
||||
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(");
|
||||
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")||
|
||||
""):(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,
|
||||
!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._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=
|
||||
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=
|
||||
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,
|
||||
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;
|
||||
/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"));
|
||||
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"&";case "<":return"<";case ">":return">";case "\u00a0":return" "}},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,
|
||||
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");
|
||||
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);
|
||||
"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=
|
||||
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]"===
|
||||
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),
|
||||
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=
|
||||
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"!==
|
||||
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");
|
||||
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"!==
|
||||
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",
|
||||
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||
|
||||
("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,
|
||||
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}},
|
||||
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")}},
|
||||
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();
|
||||
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"]');
|
||||
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()))};
|
||||
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,
|
||||
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!==
|
||||
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=
|
||||
"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",
|
||||
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=
|
||||
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]=
|
||||
!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||
|
||||
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=
|
||||
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,
|
||||
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(" ")),
|
||||
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,
|
||||
"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},
|
||||
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!==
|
||||
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=
|
||||
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&&
|
||||
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;
|
||||
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"===
|
||||
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=
|
||||
!!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=
|
||||
{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,
|
||||
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);
|
||||
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,
|
||||
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=
|
||||
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&&
|
||||
(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];
|
||||
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<
|
||||
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=
|
||||
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=
|
||||
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=
|
||||
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,
|
||||
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,
|
||||
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
|
||||
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,
|
||||
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}},
|
||||
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=
|
||||
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=
|
||||
!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&&
|
||||
"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);
|
||||
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=
|
||||
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&&"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.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=
|
||||
!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)}}});
|
||||
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===
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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"),
|
||||
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=
|
||||
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,
|
||||
"+"));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,
|
||||
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===
|
||||
$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=
|
||||
/(.*):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,
|
||||
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)};
|
||||
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||"")+
|
||||
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;
|
||||
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,
|
||||
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=
|
||||
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,
|
||||
"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||
|
||||
"":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,
|
||||
"").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]!==
|
||||
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)};
|
||||
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||
|
||||
(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;
|
||||
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=
|
||||
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));
|
||||
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&&
|
||||
(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));
|
||||
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=
|
||||
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=
|
||||
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=
|
||||
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))?
|
||||
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=
|
||||
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}}});
|
||||
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=
|
||||
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}))})});
|
||||
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);
|
||||
'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 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)}
|
||||
function Sd(g){for(var t,R=[];!(t=g.next()).done;)R.push(t.value);return R}
|
||||
(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=
|
||||
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);
|
||||
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?
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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=
|
||||
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=
|
||||
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]);
|
||||
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=
|
||||
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=
|
||||
[],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,
|
||||
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;
|
||||
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,
|
||||
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;
|
||||
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 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,
|
||||
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||
|
||||
(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):
|
||||
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=
|
||||
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),
|
||||
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 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);
|
||||
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}else if(b)return a}).filter(function(a){return a})}function sc(a){switch(a){case "&":return"&";case "<":return"<";case ">":return">";case '"':return""";case "\u00a0":return" "}}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,
|
||||
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=
|
||||
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),
|
||||
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,
|
||||
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=
|
||||
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&&
|
||||
(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=
|
||||
!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=
|
||||
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.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()}
|
||||
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()}
|
||||
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||
|
||||
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&&
|
||||
(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=
|
||||
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},
|
||||
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||
|
||||
{},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]&&
|
||||
(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-
|
||||
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]+
|
||||
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]),
|
||||
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,
|
||||
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);
|
||||
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"===
|
||||
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-
|
||||
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,
|
||||
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=
|
||||
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&&
|
||||
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=
|
||||
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(),
|
||||
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();
|
||||
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=
|
||||
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?
|
||||
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);
|
||||
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]=
|
||||
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)});
|
||||
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=
|
||||
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
|
||||
DocumentFragment)}}();if(b){var h=function(a){switch(a){case "&":return"&";case "<":return"<";case ">":return">";case "\u00a0":return" "}},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)},
|
||||
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,
|
||||
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,
|
||||
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&&
|
||||
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,
|
||||
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,
|
||||
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};
|
||||
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"===
|
||||
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])"),
|
||||
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,
|
||||
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"),
|
||||
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=
|
||||
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=
|
||||
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)};
|
||||
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()})};
|
||||
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"]');
|
||||
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()))};
|
||||
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,
|
||||
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!==
|
||||
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=
|
||||
"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",
|
||||
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=
|
||||
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]=
|
||||
!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||
|
||||
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)})}};
|
||||
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,
|
||||
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=
|
||||
/[&\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,
|
||||
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},
|
||||
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&&
|
||||
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=
|
||||
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=
|
||||
[],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=
|
||||
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},
|
||||
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):
|
||||
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),
|
||||
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,
|
||||
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,
|
||||
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=
|
||||
"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=
|
||||
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=
|
||||
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=
|
||||
[];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;
|
||||
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)===
|
||||
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)):
|
||||
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,
|
||||
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=
|
||||
{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,
|
||||
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)||
|
||||
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"===
|
||||
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,
|
||||
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,
|
||||
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=
|
||||
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,
|
||||
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);
|
||||
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=
|
||||
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)&&
|
||||
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.");
|
||||
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.");
|
||||
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=
|
||||
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("'"+
|
||||
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;
|
||||
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,
|
||||
"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,
|
||||
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});
|
||||
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},
|
||||
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(.*)/,
|
||||
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=
|
||||
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,
|
||||
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&&
|
||||
(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+'"]'});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)};
|
||||
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?
|
||||
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);
|
||||
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=
|
||||
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=
|
||||
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=
|
||||
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!==
|
||||
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>+~])");
|
||||
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,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));
|
||||
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"}}});
|
||||
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]=
|
||||
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)};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};
|
||||
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=
|
||||
!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")||
|
||||
"":""};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){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||
|
||||
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=
|
||||
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=
|
||||
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;
|
||||
(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):
|
||||
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");
|
||||
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;
|
||||
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
|
||||
|
Binary file not shown.
@ -24,7 +24,7 @@ from homeassistant.helpers.event import track_time_change
|
||||
from homeassistant.util import convert, dt
|
||||
|
||||
REQUIREMENTS = [
|
||||
'google-api-python-client==1.6.2',
|
||||
'google-api-python-client==1.6.4',
|
||||
'oauth2client==4.0.0',
|
||||
]
|
||||
|
||||
@ -99,10 +99,10 @@ def do_authentication(hass, config):
|
||||
from oauth2client.file import Storage
|
||||
|
||||
oauth = OAuth2WebServerFlow(
|
||||
config[CONF_CLIENT_ID],
|
||||
config[CONF_CLIENT_SECRET],
|
||||
'https://www.googleapis.com/auth/calendar.readonly',
|
||||
'Home-Assistant.io',
|
||||
client_id=config[CONF_CLIENT_ID],
|
||||
client_secret=config[CONF_CLIENT_SECRET],
|
||||
scope='https://www.googleapis.com/auth/calendar.readonly',
|
||||
redirect_uri='Home-Assistant.io',
|
||||
)
|
||||
|
||||
try:
|
||||
|
@ -269,7 +269,7 @@ def async_setup(hass, config):
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_RELOAD, reload_service_handler,
|
||||
descriptions[DOMAIN][SERVICE_RELOAD], schema=RELOAD_SERVICE_SCHEMA)
|
||||
descriptions[SERVICE_RELOAD], schema=RELOAD_SERVICE_SCHEMA)
|
||||
|
||||
@asyncio.coroutine
|
||||
def groups_service_handler(service):
|
||||
@ -346,11 +346,11 @@ def async_setup(hass, config):
|
||||
|
||||
hass.services.async_register(
|
||||
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(
|
||||
DOMAIN, SERVICE_REMOVE, groups_service_handler,
|
||||
descriptions[DOMAIN][SERVICE_REMOVE], schema=REMOVE_SERVICE_SCHEMA)
|
||||
descriptions[SERVICE_REMOVE], schema=REMOVE_SERVICE_SCHEMA)
|
||||
|
||||
@asyncio.coroutine
|
||||
def visibility_service_handler(service):
|
||||
@ -368,7 +368,7 @@ def async_setup(hass, config):
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_VISIBILITY, visibility_service_handler,
|
||||
descriptions[DOMAIN][SERVICE_SET_VISIBILITY],
|
||||
descriptions[SERVICE_SET_VISIBILITY],
|
||||
schema=SET_VISIBILITY_SERVICE_SCHEMA)
|
||||
|
||||
return True
|
59
homeassistant/components/group/services.yaml
Normal file
59
homeassistant/components/group/services.yaml
Normal 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'
|
||||
|
@ -14,9 +14,13 @@ from aiohttp import web
|
||||
from aiohttp.web_exceptions import HTTPBadGateway
|
||||
from aiohttp.hdrs import CONTENT_TYPE
|
||||
import async_timeout
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import CONTENT_TYPE_TEXT_PLAIN
|
||||
from homeassistant.components.http import HomeAssistantView, KEY_AUTHENTICATED
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
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.components.frontend import register_built_in_panel
|
||||
|
||||
@ -25,16 +29,42 @@ _LOGGER = logging.getLogger(__name__)
|
||||
DOMAIN = 'hassio'
|
||||
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 = {
|
||||
re.compile(r'^homeassistant/update$'), re.compile(r'^host/update$'),
|
||||
re.compile(r'^supervisor/update$'), re.compile(r'^addons/[^/]*/update$'),
|
||||
re.compile(r'^addons/[^/]*/install$')
|
||||
re.compile(r'^homeassistant/update$'),
|
||||
re.compile(r'^host/update$'),
|
||||
re.compile(r'^supervisor/update$'),
|
||||
re.compile(r'^addons/[^/]*/update$'),
|
||||
re.compile(r'^addons/[^/]*/install$'),
|
||||
re.compile(r'^addons/[^/]*/rebuild$')
|
||||
}
|
||||
|
||||
NO_AUTH = {
|
||||
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
|
||||
def async_setup(hass, config):
|
||||
@ -48,8 +78,7 @@ def async_setup(hass, config):
|
||||
websession = async_get_clientsession(hass)
|
||||
hassio = HassIO(hass.loop, websession, host)
|
||||
|
||||
api_ok = yield from hassio.is_connected()
|
||||
if not api_ok:
|
||||
if not (yield from hassio.is_connected()):
|
||||
_LOGGER.error("Not connected with HassIO!")
|
||||
return False
|
||||
|
||||
@ -59,6 +88,23 @@ def async_setup(hass, config):
|
||||
register_built_in_panel(hass, 'hassio', 'Hass.io',
|
||||
'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
|
||||
|
||||
|
||||
@ -71,30 +117,52 @@ class HassIO(object):
|
||||
self.websession = websession
|
||||
self._ip = ip
|
||||
|
||||
@asyncio.coroutine
|
||||
def is_connected(self):
|
||||
"""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.
|
||||
"""
|
||||
try:
|
||||
with async_timeout.timeout(10, loop=self.loop):
|
||||
request = yield from self.websession.get(
|
||||
"http://{}{}".format(self._ip, "/supervisor/ping")
|
||||
)
|
||||
with async_timeout.timeout(timeout, loop=self.loop):
|
||||
request = yield from self.websession.request(
|
||||
method, "http://{}{}".format(self._ip, command),
|
||||
json=payload)
|
||||
|
||||
if request.status != 200:
|
||||
_LOGGER.error("Ping return code %d.", request.status)
|
||||
_LOGGER.error(
|
||||
"%s return code %d.", command, request.status)
|
||||
return False
|
||||
|
||||
answer = yield from request.json()
|
||||
return answer and answer['result'] == 'ok'
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
_LOGGER.error("Timeout on ping request")
|
||||
_LOGGER.error("Timeout on %s request", command)
|
||||
|
||||
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
|
||||
|
||||
|
@ -41,6 +41,7 @@ def last_recorder_run(hass):
|
||||
|
||||
with session_scope(hass=hass) as session:
|
||||
res = (session.query(RecorderRuns)
|
||||
.filter(RecorderRuns.end.isnot(None))
|
||||
.order_by(RecorderRuns.end.desc()).first())
|
||||
if res is None:
|
||||
return None
|
||||
@ -48,8 +49,8 @@ def last_recorder_run(hass):
|
||||
return res
|
||||
|
||||
|
||||
def get_significant_states(hass, start_time, end_time=None, entity_id=None,
|
||||
filters=None):
|
||||
def get_significant_states(hass, start_time, end_time=None, entity_ids=None,
|
||||
filters=None, include_start_time_state=True):
|
||||
"""
|
||||
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()
|
||||
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:
|
||||
query = session.query(States).filter(
|
||||
(States.domain.in_(SIGNIFICANT_DOMAINS) |
|
||||
@ -86,7 +85,9 @@ def get_significant_states(hass, start_time, end_time=None, entity_id=None,
|
||||
_LOGGER.debug(
|
||||
'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,
|
||||
@ -105,10 +106,12 @@ def state_changes_during_period(hass, start_time, end_time=None,
|
||||
if entity_id is not None:
|
||||
query = query.filter_by(entity_id=entity_id.lower())
|
||||
|
||||
entity_ids = [entity_id] if entity_id is not None else None
|
||||
|
||||
states = execute(
|
||||
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,
|
||||
@ -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)]
|
||||
|
||||
|
||||
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.
|
||||
|
||||
This takes our state list and turns it into a JSON friendly data
|
||||
@ -197,14 +206,13 @@ def states_to_json(hass, states, start_time, entity_id, filters=None):
|
||||
"""
|
||||
result = defaultdict(list)
|
||||
|
||||
entity_ids = [entity_id] if entity_id is not None else None
|
||||
|
||||
# Get the states at the start time
|
||||
timer_start = time.perf_counter()
|
||||
for state in get_states(hass, start_time, entity_ids, filters=filters):
|
||||
state.last_changed = start_time
|
||||
state.last_updated = start_time
|
||||
result[state.entity_id].append(state)
|
||||
if include_start_time_state:
|
||||
for state in get_states(hass, start_time, entity_ids, filters=filters):
|
||||
state.last_changed = start_time
|
||||
state.last_updated = start_time
|
||||
result[state.entity_id].append(state)
|
||||
|
||||
if _LOGGER.isEnabledFor(logging.DEBUG):
|
||||
elapsed = time.perf_counter() - timer_start
|
||||
@ -250,7 +258,7 @@ class HistoryPeriodView(HomeAssistantView):
|
||||
extra_urls = ['/api/history/period/{datetime}']
|
||||
|
||||
def __init__(self, filters):
|
||||
"""Initilalize the history period view."""
|
||||
"""Initialize the history period view."""
|
||||
self.filters = filters
|
||||
|
||||
@asyncio.coroutine
|
||||
@ -276,17 +284,21 @@ class HistoryPeriodView(HomeAssistantView):
|
||||
|
||||
end_time = request.query.get('end_time')
|
||||
if end_time:
|
||||
end_time = dt_util.as_utc(
|
||||
dt_util.parse_datetime(end_time))
|
||||
if end_time is None:
|
||||
end_time = dt_util.parse_datetime(end_time)
|
||||
if end_time:
|
||||
end_time = dt_util.as_utc(end_time)
|
||||
else:
|
||||
return self.json_message('Invalid end_time', HTTP_BAD_REQUEST)
|
||||
else:
|
||||
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(
|
||||
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()
|
||||
if _LOGGER.isEnabledFor(logging.DEBUG):
|
||||
elapsed = time.perf_counter() - timer_start
|
||||
|
87
homeassistant/components/history_graph.py
Normal file
87
homeassistant/components/history_graph.py
Normal 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
|
@ -18,7 +18,7 @@ from homeassistant.const import (
|
||||
CONF_PLATFORM, CONF_HOSTS, CONF_NAME, ATTR_ENTITY_ID)
|
||||
from homeassistant.helpers import discovery
|
||||
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
|
||||
|
||||
REQUIREMENTS = ['pyhomematic==0.1.32']
|
||||
@ -292,7 +292,7 @@ def setup(hass, config):
|
||||
entity_hubs = []
|
||||
for _, hub_data in hosts.items():
|
||||
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
|
||||
descriptions = load_yaml_config_file(
|
||||
@ -571,8 +571,9 @@ def _device_from_servicecall(hass, service):
|
||||
class HMHub(Entity):
|
||||
"""The HomeMatic hub. (CCU2/HomeGear)."""
|
||||
|
||||
def __init__(self, homematic, name, use_variables):
|
||||
def __init__(self, hass, homematic, name, use_variables):
|
||||
"""Initialize HomeMatic hub."""
|
||||
self.hass = hass
|
||||
self.entity_id = "{}.{}".format(DOMAIN, name.lower())
|
||||
self._homematic = homematic
|
||||
self._variables = {}
|
||||
@ -580,18 +581,15 @@ class HMHub(Entity):
|
||||
self._state = STATE_UNKNOWN
|
||||
self._use_variables = use_variables
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_added_to_hass(self):
|
||||
"""Load data init callbacks."""
|
||||
# Load data
|
||||
async_track_time_interval(
|
||||
track_time_interval(
|
||||
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:
|
||||
async_track_time_interval(
|
||||
track_time_interval(
|
||||
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
|
||||
def name(self):
|
||||
@ -621,10 +619,12 @@ class HMHub(Entity):
|
||||
|
||||
def _update_hub(self, now):
|
||||
"""Retrieve latest state."""
|
||||
state = self._homematic.getServiceMessages(self._name)
|
||||
self._state = STATE_UNKNOWN if state is None else len(state)
|
||||
service_message = self._homematic.getServiceMessages(self._name)
|
||||
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()
|
||||
|
||||
def _update_variables(self, now):
|
||||
@ -641,7 +641,7 @@ class HMHub(Entity):
|
||||
state_change = True
|
||||
self._variables.update({key: value})
|
||||
|
||||
if state_change and now:
|
||||
if state_change:
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def hm_set_variable(self, name, value):
|
||||
|
@ -1,8 +1,11 @@
|
||||
"""Authentication for HTTP component."""
|
||||
import asyncio
|
||||
import base64
|
||||
import hmac
|
||||
import logging
|
||||
|
||||
from aiohttp import hdrs
|
||||
|
||||
from homeassistant.const import HTTP_HEADER_HA_AUTH
|
||||
from .util import get_real_ip
|
||||
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])):
|
||||
authenticated = True
|
||||
|
||||
elif (hdrs.AUTHORIZATION in request.headers and
|
||||
validate_authorization_header(request)):
|
||||
authenticated = True
|
||||
|
||||
elif is_trusted_ip(request):
|
||||
authenticated = True
|
||||
|
||||
@ -64,3 +71,22 @@ def validate_password(request, api_password):
|
||||
"""Test if password is valid."""
|
||||
return hmac.compare_digest(
|
||||
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)
|
||||
|
@ -17,7 +17,7 @@ from homeassistant.components.image_processing import (
|
||||
ImageProcessingEntity)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
REQUIREMENTS = ['numpy==1.13.1']
|
||||
REQUIREMENTS = ['numpy==1.13.3']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -28,7 +28,7 @@ CASCADE_URL = \
|
||||
'https://raw.githubusercontent.com/opencv/opencv/master/data/' + \
|
||||
'lbpcascades/lbpcascade_frontalface.xml'
|
||||
|
||||
CONF_CLASSIFIER = 'classifer'
|
||||
CONF_CLASSIFIER = 'classifier'
|
||||
CONF_FILE = 'file'
|
||||
CONF_MIN_SIZE = 'min_size'
|
||||
CONF_NEIGHBORS = 'neighbors'
|
||||
|
@ -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
|
||||
https://home-assistant.io/components/image_processing.seven_segments/
|
||||
|
@ -18,7 +18,7 @@ from homeassistant.helpers import state as state_helper
|
||||
from homeassistant.helpers.entity_values import EntityValues
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
REQUIREMENTS = ['influxdb==3.0.0']
|
||||
REQUIREMENTS = ['influxdb==4.1.1']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
227
homeassistant/components/input_datetime.py
Normal file
227
homeassistant/components/input_datetime.py
Normal 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()
|
@ -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
|
||||
at https://home-assistant.io/components/input_slider/
|
||||
at https://home-assistant.io/components/input_number/
|
||||
"""
|
||||
import asyncio
|
||||
import logging
|
||||
@ -19,29 +19,34 @@ from homeassistant.helpers.restore_state import async_get_last_state
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = 'input_slider'
|
||||
DOMAIN = 'input_number'
|
||||
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||
|
||||
CONF_INITIAL = 'initial'
|
||||
CONF_MIN = 'min'
|
||||
CONF_MAX = 'max'
|
||||
CONF_MODE = 'mode'
|
||||
CONF_STEP = 'step'
|
||||
|
||||
MODE_SLIDER = 'slider'
|
||||
MODE_BOX = 'box'
|
||||
|
||||
ATTR_VALUE = 'value'
|
||||
ATTR_MIN = 'min'
|
||||
ATTR_MAX = 'max'
|
||||
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.Required(ATTR_VALUE): vol.Coerce(float),
|
||||
})
|
||||
|
||||
|
||||
def _cv_input_slider(cfg):
|
||||
"""Configure validation helper for input slider (voluptuous)."""
|
||||
def _cv_input_number(cfg):
|
||||
"""Configure validation helper for input number (voluptuous)."""
|
||||
minimum = cfg.get(CONF_MIN)
|
||||
maximum = cfg.get(CONF_MAX)
|
||||
if minimum >= maximum:
|
||||
@ -64,16 +69,18 @@ CONFIG_SCHEMA = vol.Schema({
|
||||
vol.Optional(CONF_STEP, default=1):
|
||||
vol.All(vol.Coerce(float), vol.Range(min=1e-3)),
|
||||
vol.Optional(CONF_ICON): cv.icon,
|
||||
vol.Optional(ATTR_UNIT_OF_MEASUREMENT): cv.string
|
||||
}, _cv_input_slider)
|
||||
vol.Optional(ATTR_UNIT_OF_MEASUREMENT): cv.string,
|
||||
vol.Optional(CONF_MODE, default=MODE_SLIDER):
|
||||
vol.In([MODE_BOX, MODE_SLIDER]),
|
||||
}, _cv_input_number)
|
||||
})
|
||||
}, required=True, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
|
||||
@bind_hass
|
||||
def select_value(hass, entity_id, value):
|
||||
"""Set input_slider to value."""
|
||||
hass.services.call(DOMAIN, SERVICE_SELECT_VALUE, {
|
||||
def set_value(hass, entity_id, value):
|
||||
"""Set input_number to value."""
|
||||
hass.services.call(DOMAIN, SERVICE_SET_VALUE, {
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
ATTR_VALUE: value,
|
||||
})
|
||||
@ -94,37 +101,39 @@ def async_setup(hass, config):
|
||||
step = cfg.get(CONF_STEP)
|
||||
icon = cfg.get(CONF_ICON)
|
||||
unit = cfg.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
mode = cfg.get(CONF_MODE)
|
||||
|
||||
entities.append(InputSlider(
|
||||
object_id, name, initial, minimum, maximum, step, icon, unit))
|
||||
entities.append(InputNumber(
|
||||
object_id, name, initial, minimum, maximum, step, icon, unit,
|
||||
mode))
|
||||
|
||||
if not entities:
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_select_value_service(call):
|
||||
def async_set_value_service(call):
|
||||
"""Handle a calls to the input slider services."""
|
||||
target_inputs = component.async_extract_from_service(call)
|
||||
|
||||
tasks = [input_slider.async_select_value(call.data[ATTR_VALUE])
|
||||
for input_slider in target_inputs]
|
||||
tasks = [input_number.async_set_value(call.data[ATTR_VALUE])
|
||||
for input_number in target_inputs]
|
||||
if tasks:
|
||||
yield from asyncio.wait(tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SELECT_VALUE, async_select_value_service,
|
||||
schema=SERVICE_SELECT_VALUE_SCHEMA)
|
||||
DOMAIN, SERVICE_SET_VALUE, async_set_value_service,
|
||||
schema=SERVICE_SET_VALUE_SCHEMA)
|
||||
|
||||
yield from component.async_add_entities(entities)
|
||||
return True
|
||||
|
||||
|
||||
class InputSlider(Entity):
|
||||
class InputNumber(Entity):
|
||||
"""Represent an slider."""
|
||||
|
||||
def __init__(self, object_id, name, initial, minimum, maximum, step, icon,
|
||||
unit):
|
||||
"""Initialize a select input."""
|
||||
unit, mode):
|
||||
"""Initialize an input number."""
|
||||
self.entity_id = ENTITY_ID_FORMAT.format(object_id)
|
||||
self._name = name
|
||||
self._current_value = initial
|
||||
@ -133,6 +142,7 @@ class InputSlider(Entity):
|
||||
self._step = step
|
||||
self._icon = icon
|
||||
self._unit = unit
|
||||
self._mode = mode
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
@ -141,7 +151,7 @@ class InputSlider(Entity):
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the select input slider."""
|
||||
"""Return the name of the input slider."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
@ -165,7 +175,8 @@ class InputSlider(Entity):
|
||||
return {
|
||||
ATTR_MIN: self._minimum,
|
||||
ATTR_MAX: self._maximum,
|
||||
ATTR_STEP: self._step
|
||||
ATTR_STEP: self._step,
|
||||
ATTR_MODE: self._mode,
|
||||
}
|
||||
|
||||
@asyncio.coroutine
|
||||
@ -184,8 +195,8 @@ class InputSlider(Entity):
|
||||
self._current_value = self._minimum
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_select_value(self, value):
|
||||
"""Select new value."""
|
||||
def async_set_value(self, value):
|
||||
"""Set new value."""
|
||||
num_value = float(value)
|
||||
if num_value < self._minimum or num_value > self._maximum:
|
||||
_LOGGER.warning("Invalid value: %s (range %s - %s)",
|
@ -217,7 +217,7 @@ class KNXModule(object):
|
||||
|
||||
@asyncio.coroutine
|
||||
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
|
||||
attr_payload = call.data.get(SERVICE_KNX_ATTR_PAYLOAD)
|
||||
attr_address = call.data.get(SERVICE_KNX_ATTR_ADDRESS)
|
||||
|
@ -17,7 +17,7 @@ from homeassistant.components.light import (
|
||||
SUPPORT_RGB_COLOR, Light, PLATFORM_SCHEMA)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
REQUIREMENTS = ['flux_led==0.19']
|
||||
REQUIREMENTS = ['flux_led==0.20']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -206,7 +206,7 @@ def setup_bridge(host, hass, add_devices, filename, allow_unreachable,
|
||||
|
||||
if not skip_groups:
|
||||
# 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/
|
||||
# groups-api#21_get_all_groups
|
||||
_LOGGER.debug("Getting group 0 from bridge")
|
||||
|
@ -134,7 +134,7 @@ class InsteonLocalDimmerDevice(Light):
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the the name of the node."""
|
||||
"""Return the name of the node."""
|
||||
return self.node.deviceName
|
||||
|
||||
@property
|
||||
|
@ -60,12 +60,12 @@ class InsteonPLMDimmerDevice(Light):
|
||||
|
||||
@property
|
||||
def address(self):
|
||||
"""Return the the address of the node."""
|
||||
"""Return the address of the node."""
|
||||
return self._address
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the the name of the node."""
|
||||
"""Return the name of the node."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
|
@ -48,7 +48,7 @@ PLATFORM_SCHEMA = vol.Schema({
|
||||
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_FIRE_EVENT): cv.boolean,
|
||||
vol.Optional(CONF_SIGNAL_REPETITIONS): vol.Coerce(int),
|
||||
vol.Optional(CONF_GROUP, default=True): cv.boolean,
|
||||
# deprecated config options
|
||||
@ -123,7 +123,7 @@ def devices_from_config(domain_config, hass=None):
|
||||
_LOGGER.warning(
|
||||
"Hybrid type for %s not compatible with signal "
|
||||
"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)
|
||||
devices.append(device)
|
||||
|
@ -56,7 +56,7 @@ class TellstickLight(TellstickDevice, Light):
|
||||
return kwargs.get(ATTR_BRIGHTNESS)
|
||||
|
||||
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:
|
||||
brightness = int(tellcore_data)
|
||||
return brightness
|
||||
|
@ -4,15 +4,18 @@ Support for the IKEA Tradfri platform.
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.tradfri/
|
||||
"""
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_RGB_COLOR, SUPPORT_BRIGHTNESS,
|
||||
SUPPORT_COLOR_TEMP, SUPPORT_RGB_COLOR, Light)
|
||||
from homeassistant.components.light import (
|
||||
PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA)
|
||||
from homeassistant.components.tradfri import (
|
||||
KEY_GATEWAY, KEY_TRADFRI_GROUPS, KEY_API)
|
||||
ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_RGB_COLOR, ATTR_TRANSITION,
|
||||
SUPPORT_BRIGHTNESS, SUPPORT_TRANSITION, SUPPORT_COLOR_TEMP,
|
||||
SUPPORT_RGB_COLOR, Light)
|
||||
from homeassistant.components.light import \
|
||||
PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA
|
||||
from homeassistant.components.tradfri import KEY_GATEWAY, KEY_TRADFRI_GROUPS, \
|
||||
KEY_API
|
||||
from homeassistant.util import color as color_util
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -20,10 +23,13 @@ _LOGGER = logging.getLogger(__name__)
|
||||
DEPENDENCIES = ['tradfri']
|
||||
PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA
|
||||
IKEA = 'IKEA of Sweden'
|
||||
TRADFRI_LIGHT_MANAGER = 'Tradfri Light Manager'
|
||||
SUPPORTED_FEATURES = (SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION)
|
||||
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."""
|
||||
if discovery_info is None:
|
||||
return
|
||||
@ -31,14 +37,21 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
gateway_id = discovery_info['gateway']
|
||||
api = hass.data[KEY_API][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]
|
||||
add_devices(Tradfri(light, api) for light in lights)
|
||||
|
||||
devices_command = gateway.get_devices()
|
||||
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]
|
||||
if allow_tradfri_groups:
|
||||
groups = api(gateway.get_groups())
|
||||
add_devices(TradfriGroup(group, api) for group in groups)
|
||||
groups_command = gateway.get_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):
|
||||
@ -46,14 +59,26 @@ class TradfriGroup(Light):
|
||||
|
||||
def __init__(self, light, api):
|
||||
"""Initialize a Group."""
|
||||
self._group = api(light)
|
||||
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
|
||||
def supported_features(self):
|
||||
"""Flag supported features."""
|
||||
return SUPPORT_BRIGHTNESS
|
||||
return SUPPORTED_FEATURES
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
@ -70,49 +95,68 @@ class TradfriGroup(Light):
|
||||
"""Return the brightness of the group lights."""
|
||||
return self._group.dimmer
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
@asyncio.coroutine
|
||||
def async_turn_off(self, **kwargs):
|
||||
"""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."""
|
||||
keys = {}
|
||||
if ATTR_TRANSITION in kwargs:
|
||||
keys['transition_time'] = int(kwargs[ATTR_TRANSITION])
|
||||
|
||||
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:
|
||||
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:
|
||||
self._api(self._group.update())
|
||||
except RequestTimeout:
|
||||
_LOGGER.warning("Tradfri update request timed out")
|
||||
cmd = self._group.observe(callback=self._observe_update,
|
||||
err_callback=self._async_start_observe,
|
||||
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):
|
||||
"""The platform class required by Home Asisstant."""
|
||||
class TradfriLight(Light):
|
||||
"""The platform class required by Home Assistant."""
|
||||
|
||||
def __init__(self, light, api):
|
||||
"""Initialize a Light."""
|
||||
self._light = api(light)
|
||||
self._api = api
|
||||
|
||||
# Caching of LightControl and light object
|
||||
self._light_control = self._light.light_control
|
||||
self._light_data = self._light_control.lights[0]
|
||||
self._name = self._light.name
|
||||
self._light = None
|
||||
self._light_control = None
|
||||
self._light_data = None
|
||||
self._name = 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:
|
||||
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
|
||||
self._refresh(light)
|
||||
|
||||
@property
|
||||
def min_mireds(self):
|
||||
@ -126,6 +170,30 @@ class Tradfri(Light):
|
||||
from pytradfri.color import 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
|
||||
def supported_features(self):
|
||||
"""Flag supported features."""
|
||||
@ -151,7 +219,7 @@ class Tradfri(Light):
|
||||
"""Return the CT color value in mireds."""
|
||||
if (self._light_data.kelvin_color is None or
|
||||
self.supported_features & SUPPORT_COLOR_TEMP == 0 or
|
||||
not self._ok_temps):
|
||||
not self._temp_supported):
|
||||
return None
|
||||
return color_util.color_temperature_kelvin_to_mired(
|
||||
self._light_data.kelvin_color
|
||||
@ -162,42 +230,90 @@ class Tradfri(Light):
|
||||
"""RGB color of the light."""
|
||||
return self._rgb_color
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
@asyncio.coroutine
|
||||
def async_turn_off(self, **kwargs):
|
||||
"""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.
|
||||
|
||||
After adding "self._light_data.hexcolor is not None"
|
||||
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:
|
||||
self._api(self._light.light_control.set_rgb_color(
|
||||
*kwargs[ATTR_RGB_COLOR]))
|
||||
self.hass.async_add_job(self._api(
|
||||
self._light.light_control.set_rgb_color(
|
||||
*kwargs[ATTR_RGB_COLOR])))
|
||||
|
||||
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(
|
||||
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:
|
||||
self._api(self._light.update())
|
||||
except RequestTimeout as exception:
|
||||
_LOGGER.warning("Tradfri update request timed out: %s", exception)
|
||||
cmd = self._light.observe(callback=self._observe_update,
|
||||
err_callback=self._async_start_observe,
|
||||
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
|
||||
# hex_color is 0 when bulb is unreachable
|
||||
if self._light_data.hex_color not in (None, '0'):
|
||||
self._rgb_color = color_util.rgb_hex_to_rgb_list(
|
||||
self._light_data.hex_color)
|
||||
|
||||
self.hass.async_add_job(self.async_update_ha_state())
|
||||
|
@ -23,7 +23,7 @@ from homeassistant.components.light import (
|
||||
Light, PLATFORM_SCHEMA)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
REQUIREMENTS = ['yeelight==0.3.2']
|
||||
REQUIREMENTS = ['yeelight==0.3.3']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -434,7 +434,10 @@ class YeelightLight(Light):
|
||||
def turn_off(self, **kwargs) -> None:
|
||||
"""Turn off."""
|
||||
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:
|
||||
self._bulb.turn_off()
|
||||
self._bulb.turn_off(duration=duration)
|
||||
except yeelight.BulbException as ex:
|
||||
_LOGGER.error("Unable to turn the bulb off: %s", ex)
|
||||
|
@ -17,7 +17,7 @@ get_usercode:
|
||||
description: Node id of the lock
|
||||
example: 18
|
||||
code_slot:
|
||||
description: Code slot to retrive a code from
|
||||
description: Code slot to retrieve a code from
|
||||
example: 1
|
||||
|
||||
nuki_lock_n_go:
|
||||
@ -83,7 +83,7 @@ wink_set_lock_vacation_mode:
|
||||
description: Name of lock to unlock
|
||||
example: 'lock.front_door'
|
||||
enabled:
|
||||
description: enable or disable. true or false.
|
||||
description: enable or disable. true or false.
|
||||
example: true
|
||||
|
||||
wink_set_lock_alarm_mode:
|
||||
@ -94,7 +94,7 @@ wink_set_lock_alarm_mode:
|
||||
description: Name of lock to unlock
|
||||
example: 'lock.front_door'
|
||||
mode:
|
||||
description: One of tamper, activity, or forced_entry
|
||||
description: One of tamper, activity, or forced_entry
|
||||
example: tamper
|
||||
|
||||
wink_set_lock_alarm_sensitivity:
|
||||
@ -105,7 +105,7 @@ wink_set_lock_alarm_sensitivity:
|
||||
description: Name of lock to unlock
|
||||
example: 'lock.front_door'
|
||||
sensitivity:
|
||||
description: One of low, medium_low, medium, medium_high, high
|
||||
description: One of low, medium_low, medium, medium_high, high
|
||||
example: medium
|
||||
|
||||
wink_set_lock_alarm_state:
|
||||
@ -116,7 +116,7 @@ wink_set_lock_alarm_state:
|
||||
description: Name of lock to unlock
|
||||
example: 'lock.front_door'
|
||||
enabled:
|
||||
description: enable or disable. true or false.
|
||||
description: enable or disable. true or false.
|
||||
example: true
|
||||
|
||||
wink_set_lock_beeper_state:
|
||||
@ -127,6 +127,19 @@ wink_set_lock_beeper_state:
|
||||
description: Name of lock to unlock
|
||||
example: 'lock.front_door'
|
||||
enabled:
|
||||
description: enable or disable. true or false.
|
||||
description: enable or disable. true or false.
|
||||
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
|
||||
|
@ -13,7 +13,7 @@ import voluptuous as vol
|
||||
from homeassistant.components.lock import LockDevice
|
||||
from homeassistant.components.wink import WinkDevice, DOMAIN
|
||||
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
|
||||
|
||||
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_STATE = 'wink_set_lock_alarm_state'
|
||||
SERVICE_SET_BEEPER_STATE = 'wink_set_lock_beeper_state'
|
||||
SERVICE_ADD_KEY = 'wink_add_new_lock_key_code'
|
||||
|
||||
ATTR_ENABLED = 'enabled'
|
||||
ATTR_SENSITIVITY = 'sensitivity'
|
||||
ATTR_MODE = 'mode'
|
||||
ATTR_NAME = 'name'
|
||||
|
||||
ALARM_SENSITIVITY_MAP = {"low": 0.2, "medium_low": 0.4,
|
||||
"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)
|
||||
})
|
||||
|
||||
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):
|
||||
"""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))
|
||||
elif service.service == SERVICE_SET_ALARM_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(
|
||||
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),
|
||||
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):
|
||||
"""Representation of a Wink lock."""
|
||||
@ -149,6 +166,10 @@ class WinkLockDevice(WinkDevice, LockDevice):
|
||||
"""Set lock's beeper mode."""
|
||||
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):
|
||||
"""
|
||||
Set lock's alarm sensitivity.
|
||||
@ -176,14 +197,14 @@ class WinkLockDevice(WinkDevice, LockDevice):
|
||||
super_attrs = super().device_state_attributes
|
||||
sensitivity = dict_value_to_key(ALARM_SENSITIVITY_MAP,
|
||||
self.wink.alarm_sensitivity())
|
||||
super_attrs['alarm sensitivity'] = sensitivity
|
||||
super_attrs['vacation mode'] = self.wink.vacation_mode_enabled()
|
||||
super_attrs['beeper mode'] = self.wink.beeper_enabled()
|
||||
super_attrs['auto lock'] = self.wink.auto_lock_enabled()
|
||||
super_attrs['alarm_sensitivity'] = sensitivity
|
||||
super_attrs['vacation_mode'] = self.wink.vacation_mode_enabled()
|
||||
super_attrs['beeper_mode'] = self.wink.beeper_enabled()
|
||||
super_attrs['auto_lock'] = self.wink.auto_lock_enabled()
|
||||
alarm_mode = dict_value_to_key(ALARM_MODES_MAP,
|
||||
self.wink.alarm_mode())
|
||||
super_attrs['alarm mode'] = alarm_mode
|
||||
super_attrs['alarm enabled'] = self.wink.alarm_enabled()
|
||||
super_attrs['alarm_mode'] = alarm_mode
|
||||
super_attrs['alarm_enabled'] = self.wink.alarm_enabled()
|
||||
return super_attrs
|
||||
|
||||
|
||||
|
@ -15,7 +15,7 @@ from homeassistant.components.media_player import (
|
||||
from homeassistant.config import load_yaml_config_file
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
|
||||
REQUIREMENTS = ['youtube_dl==2017.9.15']
|
||||
REQUIREMENTS = ['youtube_dl==2017.10.01']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -637,11 +637,11 @@ class MediaPlayerDevice(Entity):
|
||||
return self.hass.async_add_job(self.set_volume_level, volume)
|
||||
|
||||
def media_play(self):
|
||||
"""Send play commmand."""
|
||||
"""Send play command."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def async_media_play(self):
|
||||
"""Send play commmand.
|
||||
"""Send play command.
|
||||
|
||||
This method must be run in the event loop and returns a coroutine.
|
||||
"""
|
||||
|
@ -96,7 +96,8 @@ class AppleTvDevice(MediaPlayerDevice):
|
||||
if self._playing:
|
||||
from pyatv import const
|
||||
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:
|
||||
return STATE_IDLE
|
||||
elif state == const.PLAY_STATE_PLAYING:
|
||||
|
@ -287,7 +287,7 @@ class CastDevice(MediaPlayerDevice):
|
||||
self.cast.set_volume(volume)
|
||||
|
||||
def media_play(self):
|
||||
"""Send play commmand."""
|
||||
"""Send play command."""
|
||||
self.cast.media_controller.play()
|
||||
|
||||
def media_pause(self):
|
||||
|
@ -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
|
||||
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):
|
||||
"""Representation of a DirecTV reciever on the network."""
|
||||
"""Representation of a DirecTV receiver on the network."""
|
||||
|
||||
def __init__(self, name, host, port, device):
|
||||
"""Initialize the device."""
|
||||
|
@ -124,7 +124,7 @@ class OpenhomeDevice(MediaPlayerDevice):
|
||||
self._device.Stop()
|
||||
|
||||
def media_play(self):
|
||||
"""Send play commmand."""
|
||||
"""Send play command."""
|
||||
self._device.Play()
|
||||
|
||||
def media_next_track(self):
|
||||
|
@ -151,11 +151,11 @@ class PhilipsTV(MediaPlayerDevice):
|
||||
self._state = STATE_OFF
|
||||
|
||||
def media_previous_track(self):
|
||||
"""Send rewind commmand."""
|
||||
"""Send rewind command."""
|
||||
self._tv.sendKey('Previous')
|
||||
|
||||
def media_next_track(self):
|
||||
"""Send fast forward commmand."""
|
||||
"""Send fast forward command."""
|
||||
self._tv.sendKey('Next')
|
||||
|
||||
@property
|
||||
|
@ -711,7 +711,7 @@ class PlexClient(MediaPlayerDevice):
|
||||
if ("127.0.0.1" in client.baseurl and
|
||||
client.machineIdentifier == self.device.machineIdentifier):
|
||||
# point controls to server since that's where the
|
||||
# playback is occuring
|
||||
# playback is occurring
|
||||
_LOGGER.debug(
|
||||
"Local client detected, redirecting controls to "
|
||||
"Plex server: %s", self.entity_id)
|
||||
|
@ -135,7 +135,7 @@ class RussoundRNETDevice(MediaPlayerDevice):
|
||||
def set_volume_level(self, volume):
|
||||
"""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()
|
||||
"""
|
||||
self._russ.set_volume('1', self._zone_id, volume * 100)
|
||||
|
@ -140,7 +140,7 @@ select_source:
|
||||
|
||||
fields:
|
||||
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'
|
||||
source:
|
||||
description: Name of the source to switch to. Platform dependent.
|
||||
@ -151,7 +151,7 @@ clear_playlist:
|
||||
|
||||
fields:
|
||||
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'
|
||||
|
||||
shuffle_set:
|
||||
@ -170,7 +170,7 @@ snapcast_snapshot:
|
||||
|
||||
fields:
|
||||
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'
|
||||
|
||||
snapcast_restore:
|
||||
@ -178,7 +178,7 @@ snapcast_restore:
|
||||
|
||||
fields:
|
||||
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_join:
|
||||
@ -190,7 +190,7 @@ sonos_join:
|
||||
example: 'media_player.living_room_sonos'
|
||||
|
||||
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'
|
||||
|
||||
sonos_unjoin:
|
||||
@ -198,7 +198,7 @@ sonos_unjoin:
|
||||
|
||||
fields:
|
||||
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'
|
||||
|
||||
sonos_snapshot:
|
||||
@ -206,7 +206,7 @@ sonos_snapshot:
|
||||
|
||||
fields:
|
||||
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'
|
||||
|
||||
with_group:
|
||||
@ -218,7 +218,7 @@ sonos_restore:
|
||||
|
||||
fields:
|
||||
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'
|
||||
|
||||
with_group:
|
||||
@ -230,7 +230,7 @@ sonos_set_sleep_timer:
|
||||
|
||||
fields:
|
||||
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'
|
||||
sleep_time:
|
||||
description: Number of seconds to set the timer
|
||||
@ -241,7 +241,7 @@ sonos_clear_sleep_timer:
|
||||
|
||||
fields:
|
||||
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'
|
||||
|
||||
|
||||
|
@ -915,8 +915,8 @@ class SonosDevice(MediaPlayerDevice):
|
||||
"""Replace queue with playlist represented by src.
|
||||
|
||||
Playlists can't be played directly with the self._player.play_uri
|
||||
API as they are actually composed of mulitple URLs. Until soco has
|
||||
suppport for playing a playlist, we'll need to parse the playlist item
|
||||
API as they are actually composed of multiple URLs. Until soco has
|
||||
support for playing a playlist, we'll need to parse the playlist item
|
||||
and replace the current queue in order to play it.
|
||||
"""
|
||||
import soco
|
||||
@ -1116,7 +1116,7 @@ class SonosDevice(MediaPlayerDevice):
|
||||
return
|
||||
|
||||
##
|
||||
# old is allready master, rejoin
|
||||
# old is already master, rejoin
|
||||
if old.coordinator.group.coordinator == old.coordinator:
|
||||
self._player.join(old.coordinator)
|
||||
return
|
||||
|
@ -441,7 +441,7 @@ class UniversalMediaPlayer(MediaPlayerDevice):
|
||||
SERVICE_VOLUME_SET, data, allow_override=True)
|
||||
|
||||
def async_media_play(self):
|
||||
"""Send play commmand.
|
||||
"""Send play command.
|
||||
|
||||
This method must be run in the event loop and returns a coroutine.
|
||||
"""
|
||||
|
@ -137,7 +137,7 @@ class VlcDevice(MediaPlayerDevice):
|
||||
self._volume = volume
|
||||
|
||||
def media_play(self):
|
||||
"""Send play commmand."""
|
||||
"""Send play command."""
|
||||
self._vlc.play()
|
||||
self._state = STATE_PLAYING
|
||||
|
||||
|
@ -19,10 +19,11 @@ from homeassistant.components.media_player import (
|
||||
SUPPORT_SELECT_SOURCE, SUPPORT_PLAY_MEDIA, MEDIA_TYPE_CHANNEL,
|
||||
MediaPlayerDevice, PLATFORM_SCHEMA)
|
||||
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_UNKNOWN, CONF_NAME, CONF_FILENAME)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.script import Script
|
||||
|
||||
REQUIREMENTS = ['pylgtv==0.1.7',
|
||||
'websockets==3.2',
|
||||
@ -32,6 +33,7 @@ _CONFIGURING = {} # type: Dict[str, str]
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_SOURCES = 'sources'
|
||||
CONF_ON_ACTION = 'turn_on_action'
|
||||
|
||||
DEFAULT_NAME = 'LG webOS Smart TV'
|
||||
|
||||
@ -53,10 +55,10 @@ CUSTOMIZE_SCHEMA = vol.Schema({
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_MAC): cv.string,
|
||||
vol.Optional(CONF_CUSTOMIZE, default={}): CUSTOMIZE_SCHEMA,
|
||||
vol.Optional(CONF_FILENAME, default=WEBOSTV_CONFIG_FILE): cv.string,
|
||||
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:
|
||||
return
|
||||
|
||||
mac = config.get(CONF_MAC)
|
||||
name = config.get(CONF_NAME)
|
||||
customize = config.get(CONF_CUSTOMIZE)
|
||||
timeout = config.get(CONF_TIMEOUT)
|
||||
turn_on_action = config.get(CONF_ON_ACTION)
|
||||
|
||||
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."""
|
||||
from pylgtv import WebOsClient
|
||||
from pylgtv import PyLGTVPairException
|
||||
@ -108,7 +114,8 @@ def setup_tv(host, mac, name, customize, config, timeout, hass, add_devices):
|
||||
# Not registered, request configuration.
|
||||
_LOGGER.warning("LG webOS TV %s needs to be paired", host)
|
||||
request_configuration(
|
||||
host, mac, name, customize, config, timeout, hass, add_devices)
|
||||
host, name, customize, config, timeout, hass,
|
||||
add_devices, turn_on_action)
|
||||
return
|
||||
|
||||
# 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.request_done(request_id)
|
||||
|
||||
add_devices([LgWebOSDevice(host, mac, name, customize, config, timeout)],
|
||||
True)
|
||||
add_devices([LgWebOSDevice(host, name, customize, config, timeout,
|
||||
hass, turn_on_action)], True)
|
||||
|
||||
|
||||
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."""
|
||||
configurator = hass.components.configurator
|
||||
|
||||
@ -135,8 +143,8 @@ def request_configuration(
|
||||
# pylint: disable=unused-argument
|
||||
def lgtv_configuration_callback(data):
|
||||
"""The actions to do when our configuration callback is called."""
|
||||
setup_tv(host, mac, name, customize, config, timeout, hass,
|
||||
add_devices)
|
||||
setup_tv(host, name, customize, config, timeout, hass,
|
||||
add_devices, turn_on_action)
|
||||
|
||||
_CONFIGURING[host] = configurator.request_config(
|
||||
name, lgtv_configuration_callback,
|
||||
@ -149,13 +157,12 @@ def request_configuration(
|
||||
class LgWebOSDevice(MediaPlayerDevice):
|
||||
"""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."""
|
||||
from pylgtv import WebOsClient
|
||||
from wakeonlan import wol
|
||||
self._client = WebOsClient(host, config, timeout)
|
||||
self._wol = wol
|
||||
self._mac = mac
|
||||
self._on_script = Script(hass, on_action) if on_action else None
|
||||
self._customize = customize
|
||||
|
||||
self._name = name
|
||||
@ -273,7 +280,7 @@ class LgWebOSDevice(MediaPlayerDevice):
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Flag media player features that are supported."""
|
||||
if self._mac:
|
||||
if self._on_script:
|
||||
return SUPPORT_WEBOSTV | SUPPORT_TURN_ON
|
||||
return SUPPORT_WEBOSTV
|
||||
|
||||
@ -289,8 +296,8 @@ class LgWebOSDevice(MediaPlayerDevice):
|
||||
|
||||
def turn_on(self):
|
||||
"""Turn on the media player."""
|
||||
if self._mac:
|
||||
self._wol.send_magic_packet(self._mac)
|
||||
if self._on_script:
|
||||
self._on_script.run()
|
||||
|
||||
def volume_up(self):
|
||||
"""Volume up the media player."""
|
||||
|
@ -214,7 +214,7 @@ class YamahaDevice(MediaPlayerDevice):
|
||||
self._volume = (self._receiver.volume / 100) + 1
|
||||
|
||||
def media_play(self):
|
||||
"""Send play commmand."""
|
||||
"""Send play command."""
|
||||
self._call_playback_function(self._receiver.play, "play")
|
||||
|
||||
def media_pause(self):
|
||||
|
@ -33,7 +33,9 @@ SUPPORTED_FEATURES = (
|
||||
SUPPORT_SELECT_SOURCE
|
||||
)
|
||||
|
||||
REQUIREMENTS = ['pymusiccast==0.1.0']
|
||||
KNOWN_HOSTS_KEY = 'data_yamaha_musiccast'
|
||||
|
||||
REQUIREMENTS = ['pymusiccast==0.1.2']
|
||||
|
||||
DEFAULT_NAME = "Yamaha Receiver"
|
||||
DEFAULT_PORT = 5005
|
||||
@ -47,16 +49,48 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Set up the Yamaha MusicCast platform."""
|
||||
import socket
|
||||
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)
|
||||
host = config.get(CONF_HOST)
|
||||
port = config.get(CONF_PORT)
|
||||
|
||||
receiver = pymusiccast.McDevice(host, udp_port=port)
|
||||
_LOGGER.debug("receiver: %s / Port: %d", receiver, port)
|
||||
# Get IP of host to prevent duplicates
|
||||
try:
|
||||
ipaddr = socket.gethostbyname(host)
|
||||
except (OSError) as error:
|
||||
_LOGGER.error(
|
||||
"Could not communicate with %s:%d: %s", host, port, error)
|
||||
return
|
||||
|
||||
add_devices([YamahaDevice(receiver, name)], True)
|
||||
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)
|
||||
else:
|
||||
known_hosts.remove(reg_host)
|
||||
|
||||
|
||||
class YamahaDevice(MediaPlayerDevice):
|
||||
|
@ -12,14 +12,19 @@ from homeassistant.const import MATCH_ALL
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.components.mqtt import valid_publish_topic
|
||||
from homeassistant.helpers.event import async_track_state_change
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
CONF_BASE_TOPIC = 'base_topic'
|
||||
CONF_PUBLISH_ATTRIBUTES = 'publish_attributes'
|
||||
CONF_PUBLISH_TIMESTAMPS = 'publish_timestamps'
|
||||
DEPENDENCIES = ['mqtt']
|
||||
DOMAIN = 'mqtt_statestream'
|
||||
|
||||
CONFIG_SCHEMA = 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)
|
||||
|
||||
@ -29,6 +34,8 @@ def async_setup(hass, config):
|
||||
"""Set up the MQTT state feed."""
|
||||
conf = config.get(DOMAIN, {})
|
||||
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('/'):
|
||||
base_topic = base_topic + '/'
|
||||
|
||||
@ -38,8 +45,28 @@ def async_setup(hass, config):
|
||||
return
|
||||
payload = new_state.state
|
||||
|
||||
topic = base_topic + entity_id.replace('.', '/') + '/state'
|
||||
hass.components.mqtt.async_publish(topic, payload, 1, True)
|
||||
mybase = base_topic + entity_id.replace('.', '/') + '/'
|
||||
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)
|
||||
return True
|
||||
|
@ -111,7 +111,7 @@ class ApnsDevice(object):
|
||||
return self.device_disabled
|
||||
|
||||
def disable(self):
|
||||
"""Disable the device from recieving notifications."""
|
||||
"""Disable the device from receiving notifications."""
|
||||
self.device_disabled = True
|
||||
|
||||
def __eq__(self, other):
|
||||
|
90
homeassistant/components/notify/clicksendaudio.py
Normal file
90
homeassistant/components/notify/clicksendaudio.py
Normal 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
|
@ -15,7 +15,7 @@ from homeassistant.components.notify import (
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
REQUIREMENTS = ['discord.py==0.16.11']
|
||||
REQUIREMENTS = ['discord.py==0.16.12']
|
||||
|
||||
CONF_TOKEN = 'token'
|
||||
|
||||
|
@ -56,8 +56,15 @@ class FacebookNotificationService(BaseNotificationService):
|
||||
return
|
||||
|
||||
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 = {
|
||||
"recipient": {"phone_number": target},
|
||||
"recipient": recipient,
|
||||
"message": body_message
|
||||
}
|
||||
import json
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user