Use constants for HTTP headers (#10313)
* Use constants for HTTP headers * Fix ordering * Move 'no-cache' to platform
This commit is contained in:
parent
e64803e701
commit
de9d19d6f4
36 changed files with 408 additions and 444 deletions
|
@ -7,25 +7,32 @@ https://home-assistant.io/components/binary_sensor.aurora/
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from aiohttp.hdrs import USER_AGENT
|
||||||
import requests
|
import requests
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor \
|
from homeassistant.components.binary_sensor import (
|
||||||
import (BinarySensorDevice, PLATFORM_SCHEMA)
|
PLATFORM_SCHEMA, BinarySensorDevice)
|
||||||
from homeassistant.const import (CONF_NAME)
|
from homeassistant.const import CONF_NAME, ATTR_ATTRIBUTION
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
|
|
||||||
CONF_THRESHOLD = "forecast_threshold"
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CONF_ATTRIBUTION = "Data provided by the National Oceanic and Atmospheric" \
|
||||||
|
"Administration"
|
||||||
|
CONF_THRESHOLD = 'forecast_threshold'
|
||||||
|
|
||||||
|
DEFAULT_DEVICE_CLASS = 'visible'
|
||||||
DEFAULT_NAME = 'Aurora Visibility'
|
DEFAULT_NAME = 'Aurora Visibility'
|
||||||
DEFAULT_DEVICE_CLASS = "visible"
|
|
||||||
DEFAULT_THRESHOLD = 75
|
DEFAULT_THRESHOLD = 75
|
||||||
|
|
||||||
|
HA_USER_AGENT = "Home Assistant Aurora Tracker v.0.1.0"
|
||||||
|
|
||||||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5)
|
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5)
|
||||||
|
|
||||||
|
URL = "http://services.swpc.noaa.gov/text/aurora-nowcast-map.txt"
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||||
vol.Optional(CONF_THRESHOLD, default=DEFAULT_THRESHOLD): cv.positive_int,
|
vol.Optional(CONF_THRESHOLD, default=DEFAULT_THRESHOLD): cv.positive_int,
|
||||||
|
@ -43,10 +50,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
aurora_data = AuroraData(
|
aurora_data = AuroraData(
|
||||||
hass.config.latitude,
|
hass.config.latitude, hass.config.longitude, threshold)
|
||||||
hass.config.longitude,
|
|
||||||
threshold
|
|
||||||
)
|
|
||||||
aurora_data.update()
|
aurora_data.update()
|
||||||
except requests.exceptions.HTTPError as error:
|
except requests.exceptions.HTTPError as error:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
|
@ -85,9 +89,9 @@ class AuroraSensor(BinarySensorDevice):
|
||||||
attrs = {}
|
attrs = {}
|
||||||
|
|
||||||
if self.aurora_data:
|
if self.aurora_data:
|
||||||
attrs["visibility_level"] = self.aurora_data.visibility_level
|
attrs['visibility_level'] = self.aurora_data.visibility_level
|
||||||
attrs["message"] = self.aurora_data.is_visible_text
|
attrs['message'] = self.aurora_data.is_visible_text
|
||||||
|
attrs[ATTR_ATTRIBUTION] = CONF_ATTRIBUTION
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
@ -104,10 +108,7 @@ class AuroraData(object):
|
||||||
self.longitude = longitude
|
self.longitude = longitude
|
||||||
self.number_of_latitude_intervals = 513
|
self.number_of_latitude_intervals = 513
|
||||||
self.number_of_longitude_intervals = 1024
|
self.number_of_longitude_intervals = 1024
|
||||||
self.api_url = \
|
self.headers = {USER_AGENT: HA_USER_AGENT}
|
||||||
"http://services.swpc.noaa.gov/text/aurora-nowcast-map.txt"
|
|
||||||
self.headers = {"User-Agent": "Home Assistant Aurora Tracker v.0.1.0"}
|
|
||||||
|
|
||||||
self.threshold = int(threshold)
|
self.threshold = int(threshold)
|
||||||
self.is_visible = None
|
self.is_visible = None
|
||||||
self.is_visible_text = None
|
self.is_visible_text = None
|
||||||
|
@ -132,14 +133,14 @@ class AuroraData(object):
|
||||||
|
|
||||||
def get_aurora_forecast(self):
|
def get_aurora_forecast(self):
|
||||||
"""Get forecast data and parse for given long/lat."""
|
"""Get forecast data and parse for given long/lat."""
|
||||||
raw_data = requests.get(self.api_url, headers=self.headers).text
|
raw_data = requests.get(URL, headers=self.headers, timeout=5).text
|
||||||
forecast_table = [
|
forecast_table = [
|
||||||
row.strip(" ").split(" ")
|
row.strip(" ").split(" ")
|
||||||
for row in raw_data.split("\n")
|
for row in raw_data.split("\n")
|
||||||
if not row.startswith("#")
|
if not row.startswith("#")
|
||||||
]
|
]
|
||||||
|
|
||||||
# convert lat and long for data points in table
|
# Convert lat and long for data points in table
|
||||||
converted_latitude = round((self.latitude / 180)
|
converted_latitude = round((self.latitude / 180)
|
||||||
* self.number_of_latitude_intervals)
|
* self.number_of_latitude_intervals)
|
||||||
converted_longitude = round((self.longitude / 360)
|
converted_longitude = round((self.longitude / 360)
|
||||||
|
|
|
@ -4,16 +4,17 @@ Support for BloomSky weather station.
|
||||||
For more details about this component, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
https://home-assistant.io/components/bloomsky/
|
https://home-assistant.io/components/bloomsky/
|
||||||
"""
|
"""
|
||||||
import logging
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from aiohttp.hdrs import AUTHORIZATION
|
||||||
import requests
|
import requests
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.const import CONF_API_KEY
|
from homeassistant.const import CONF_API_KEY
|
||||||
from homeassistant.helpers import discovery
|
from homeassistant.helpers import discovery
|
||||||
from homeassistant.util import Throttle
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.util import Throttle
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -68,7 +69,7 @@ class BloomSky(object):
|
||||||
"""Use the API to retrieve a list of devices."""
|
"""Use the API to retrieve a list of devices."""
|
||||||
_LOGGER.debug("Fetching BloomSky update")
|
_LOGGER.debug("Fetching BloomSky update")
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
self.API_URL, headers={"Authorization": self._api_key}, timeout=10)
|
self.API_URL, headers={AUTHORIZATION: self._api_key}, timeout=10)
|
||||||
if response.status_code == 401:
|
if response.status_code == 401:
|
||||||
raise RuntimeError("Invalid API_KEY")
|
raise RuntimeError("Invalid API_KEY")
|
||||||
elif response.status_code != 200:
|
elif response.status_code != 200:
|
||||||
|
|
|
@ -6,13 +6,14 @@ https://home-assistant.io/components/device_tracker.swisscom/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from aiohttp.hdrs import CONTENT_TYPE
|
||||||
import requests
|
import requests
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.components.device_tracker import (
|
from homeassistant.components.device_tracker import (
|
||||||
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
|
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
|
||||||
from homeassistant.const import CONF_HOST
|
from homeassistant.const import CONF_HOST
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -77,7 +78,7 @@ class SwisscomDeviceScanner(DeviceScanner):
|
||||||
def get_swisscom_data(self):
|
def get_swisscom_data(self):
|
||||||
"""Retrieve data from Swisscom and return parsed result."""
|
"""Retrieve data from Swisscom and return parsed result."""
|
||||||
url = 'http://{}/ws'.format(self.host)
|
url = 'http://{}/ws'.format(self.host)
|
||||||
headers = {'Content-Type': 'application/x-sah-ws-4-call+json'}
|
headers = {CONTENT_TYPE: 'application/x-sah-ws-4-call+json'}
|
||||||
data = """
|
data = """
|
||||||
{"service":"Devices", "method":"get",
|
{"service":"Devices", "method":"get",
|
||||||
"parameters":{"expression":"lan and not self"}}"""
|
"parameters":{"expression":"lan and not self"}}"""
|
||||||
|
|
|
@ -5,21 +5,27 @@ For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/device_tracker.tplink/
|
https://home-assistant.io/components/device_tracker.tplink/
|
||||||
"""
|
"""
|
||||||
import base64
|
import base64
|
||||||
|
from datetime import datetime
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
|
from aiohttp.hdrs import (
|
||||||
|
ACCEPT, COOKIE, PRAGMA, REFERER, CONNECTION, KEEP_ALIVE, USER_AGENT,
|
||||||
|
CONTENT_TYPE, CACHE_CONTROL, ACCEPT_ENCODING, ACCEPT_LANGUAGE)
|
||||||
import requests
|
import requests
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.components.device_tracker import (
|
from homeassistant.components.device_tracker import (
|
||||||
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
|
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
|
||||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
from homeassistant.const import (
|
||||||
|
CONF_HOST, CONF_PASSWORD, CONF_USERNAME, HTTP_HEADER_X_REQUESTED_WITH)
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
HTTP_HEADER_NO_CACHE = 'no-cache'
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
vol.Required(CONF_HOST): cv.string,
|
vol.Required(CONF_HOST): cv.string,
|
||||||
vol.Required(CONF_PASSWORD): cv.string,
|
vol.Required(CONF_PASSWORD): cv.string,
|
||||||
|
@ -78,7 +84,7 @@ class TplinkDeviceScanner(DeviceScanner):
|
||||||
referer = 'http://{}'.format(self.host)
|
referer = 'http://{}'.format(self.host)
|
||||||
page = requests.get(
|
page = requests.get(
|
||||||
url, auth=(self.username, self.password),
|
url, auth=(self.username, self.password),
|
||||||
headers={'referer': referer}, timeout=4)
|
headers={REFERER: referer}, timeout=4)
|
||||||
|
|
||||||
result = self.parse_macs.findall(page.text)
|
result = self.parse_macs.findall(page.text)
|
||||||
|
|
||||||
|
@ -123,7 +129,7 @@ class Tplink2DeviceScanner(TplinkDeviceScanner):
|
||||||
.format(b64_encoded_username_password)
|
.format(b64_encoded_username_password)
|
||||||
|
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
url, headers={'referer': referer, 'cookie': cookie},
|
url, headers={REFERER: referer, COOKIE: cookie},
|
||||||
timeout=4)
|
timeout=4)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -174,11 +180,11 @@ class Tplink3DeviceScanner(TplinkDeviceScanner):
|
||||||
.format(self.host)
|
.format(self.host)
|
||||||
referer = 'http://{}/webpages/login.html'.format(self.host)
|
referer = 'http://{}/webpages/login.html'.format(self.host)
|
||||||
|
|
||||||
# If possible implement rsa encryption of password here.
|
# If possible implement RSA encryption of password here.
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
url, params={'operation': 'login', 'username': self.username,
|
url, params={'operation': 'login', 'username': self.username,
|
||||||
'password': self.password},
|
'password': self.password},
|
||||||
headers={'referer': referer}, timeout=4)
|
headers={REFERER: referer}, timeout=4)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.stok = response.json().get('data').get('stok')
|
self.stok = response.json().get('data').get('stok')
|
||||||
|
@ -207,11 +213,9 @@ class Tplink3DeviceScanner(TplinkDeviceScanner):
|
||||||
'form=statistics').format(self.host, self.stok)
|
'form=statistics').format(self.host, self.stok)
|
||||||
referer = 'http://{}/webpages/index.html'.format(self.host)
|
referer = 'http://{}/webpages/index.html'.format(self.host)
|
||||||
|
|
||||||
response = requests.post(url,
|
response = requests.post(
|
||||||
params={'operation': 'load'},
|
url, params={'operation': 'load'}, headers={REFERER: referer},
|
||||||
headers={'referer': referer},
|
cookies={'sysauth': self.sysauth}, timeout=5)
|
||||||
cookies={'sysauth': self.sysauth},
|
|
||||||
timeout=5)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
json_response = response.json()
|
json_response = response.json()
|
||||||
|
@ -248,10 +252,9 @@ class Tplink3DeviceScanner(TplinkDeviceScanner):
|
||||||
'form=logout').format(self.host, self.stok)
|
'form=logout').format(self.host, self.stok)
|
||||||
referer = 'http://{}/webpages/index.html'.format(self.host)
|
referer = 'http://{}/webpages/index.html'.format(self.host)
|
||||||
|
|
||||||
requests.post(url,
|
requests.post(
|
||||||
params={'operation': 'write'},
|
url, params={'operation': 'write'}, headers={REFERER: referer},
|
||||||
headers={'referer': referer},
|
cookies={'sysauth': self.sysauth})
|
||||||
cookies={'sysauth': self.sysauth})
|
|
||||||
self.stok = ''
|
self.stok = ''
|
||||||
self.sysauth = ''
|
self.sysauth = ''
|
||||||
|
|
||||||
|
@ -292,7 +295,7 @@ class Tplink4DeviceScanner(TplinkDeviceScanner):
|
||||||
# Create the authorization cookie.
|
# Create the authorization cookie.
|
||||||
cookie = 'Authorization=Basic {}'.format(self.credentials)
|
cookie = 'Authorization=Basic {}'.format(self.credentials)
|
||||||
|
|
||||||
response = requests.get(url, headers={'cookie': cookie})
|
response = requests.get(url, headers={COOKIE: cookie})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = re.search(r'window.parent.location.href = '
|
result = re.search(r'window.parent.location.href = '
|
||||||
|
@ -326,8 +329,8 @@ class Tplink4DeviceScanner(TplinkDeviceScanner):
|
||||||
cookie = 'Authorization=Basic {}'.format(self.credentials)
|
cookie = 'Authorization=Basic {}'.format(self.credentials)
|
||||||
|
|
||||||
page = requests.get(url, headers={
|
page = requests.get(url, headers={
|
||||||
'cookie': cookie,
|
COOKIE: cookie,
|
||||||
'referer': referer
|
REFERER: referer,
|
||||||
})
|
})
|
||||||
mac_results.extend(self.parse_macs.findall(page.text))
|
mac_results.extend(self.parse_macs.findall(page.text))
|
||||||
|
|
||||||
|
@ -361,31 +364,31 @@ class Tplink5DeviceScanner(TplinkDeviceScanner):
|
||||||
base_url = 'http://{}'.format(self.host)
|
base_url = 'http://{}'.format(self.host)
|
||||||
|
|
||||||
header = {
|
header = {
|
||||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12;"
|
USER_AGENT:
|
||||||
" rv:53.0) Gecko/20100101 Firefox/53.0",
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12;"
|
||||||
"Accept": "application/json, text/javascript, */*; q=0.01",
|
" rv:53.0) Gecko/20100101 Firefox/53.0",
|
||||||
"Accept-Language": "Accept-Language: en-US,en;q=0.5",
|
ACCEPT: "application/json, text/javascript, */*; q=0.01",
|
||||||
"Accept-Encoding": "gzip, deflate",
|
ACCEPT_LANGUAGE: "Accept-Language: en-US,en;q=0.5",
|
||||||
"Content-Type": "application/x-www-form-urlencoded; "
|
ACCEPT_ENCODING: "gzip, deflate",
|
||||||
"charset=UTF-8",
|
CONTENT_TYPE: "application/x-www-form-urlencoded; charset=UTF-8",
|
||||||
"X-Requested-With": "XMLHttpRequest",
|
HTTP_HEADER_X_REQUESTED_WITH: "XMLHttpRequest",
|
||||||
"Referer": "http://" + self.host + "/",
|
REFERER: "http://{}/".format(self.host),
|
||||||
"Connection": "keep-alive",
|
CONNECTION: KEEP_ALIVE,
|
||||||
"Pragma": "no-cache",
|
PRAGMA: HTTP_HEADER_NO_CACHE,
|
||||||
"Cache-Control": "no-cache"
|
CACHE_CONTROL: HTTP_HEADER_NO_CACHE,
|
||||||
}
|
}
|
||||||
|
|
||||||
password_md5 = hashlib.md5(
|
password_md5 = hashlib.md5(
|
||||||
self.password.encode('utf')).hexdigest().upper()
|
self.password.encode('utf')).hexdigest().upper()
|
||||||
|
|
||||||
# create a session to handle cookie easier
|
# Create a session to handle cookie easier
|
||||||
session = requests.session()
|
session = requests.session()
|
||||||
session.get(base_url, headers=header)
|
session.get(base_url, headers=header)
|
||||||
|
|
||||||
login_data = {"username": self.username, "password": password_md5}
|
login_data = {"username": self.username, "password": password_md5}
|
||||||
session.post(base_url, login_data, headers=header)
|
session.post(base_url, login_data, headers=header)
|
||||||
|
|
||||||
# a timestamp is required to be sent as get parameter
|
# A timestamp is required to be sent as get parameter
|
||||||
timestamp = int(datetime.now().timestamp() * 1e3)
|
timestamp = int(datetime.now().timestamp() * 1e3)
|
||||||
|
|
||||||
client_list_url = '{}/data/monitor.client.client.json'.format(
|
client_list_url = '{}/data/monitor.client.client.json'.format(
|
||||||
|
@ -393,18 +396,17 @@ class Tplink5DeviceScanner(TplinkDeviceScanner):
|
||||||
|
|
||||||
get_params = {
|
get_params = {
|
||||||
'operation': 'load',
|
'operation': 'load',
|
||||||
'_': timestamp
|
'_': timestamp,
|
||||||
}
|
}
|
||||||
|
|
||||||
response = session.get(client_list_url,
|
response = session.get(
|
||||||
headers=header,
|
client_list_url, headers=header, params=get_params)
|
||||||
params=get_params)
|
|
||||||
session.close()
|
session.close()
|
||||||
try:
|
try:
|
||||||
list_of_devices = response.json()
|
list_of_devices = response.json()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
_LOGGER.error("AP didn't respond with JSON. "
|
_LOGGER.error("AP didn't respond with JSON. "
|
||||||
"Check if credentials are correct.")
|
"Check if credentials are correct")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if list_of_devices:
|
if list_of_devices:
|
||||||
|
|
|
@ -8,28 +8,28 @@ import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
from aiohttp.hdrs import REFERER, USER_AGENT
|
||||||
import async_timeout
|
import async_timeout
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.components.device_tracker import (
|
from homeassistant.components.device_tracker import (
|
||||||
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
|
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
|
||||||
from homeassistant.const import CONF_HOST
|
from homeassistant.const import CONF_HOST, HTTP_HEADER_X_REQUESTED_WITH
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
REQUIREMENTS = ['defusedxml==0.5.0']
|
REQUIREMENTS = ['defusedxml==0.5.0']
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CMD_DEVICES = 123
|
||||||
|
|
||||||
DEFAULT_IP = '192.168.0.1'
|
DEFAULT_IP = '192.168.0.1'
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
vol.Optional(CONF_HOST, default=DEFAULT_IP): cv.string,
|
vol.Optional(CONF_HOST, default=DEFAULT_IP): cv.string,
|
||||||
})
|
})
|
||||||
|
|
||||||
CMD_DEVICES = 123
|
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_get_scanner(hass, config):
|
def async_get_scanner(hass, config):
|
||||||
|
@ -52,11 +52,11 @@ class UPCDeviceScanner(DeviceScanner):
|
||||||
self.token = None
|
self.token = None
|
||||||
|
|
||||||
self.headers = {
|
self.headers = {
|
||||||
'X-Requested-With': 'XMLHttpRequest',
|
HTTP_HEADER_X_REQUESTED_WITH: 'XMLHttpRequest',
|
||||||
'Referer': "http://{}/index.html".format(self.host),
|
REFERER: "http://{}/index.html".format(self.host),
|
||||||
'User-Agent': ("Mozilla/5.0 (Windows NT 10.0; WOW64) "
|
USER_AGENT: ("Mozilla/5.0 (Windows NT 10.0; WOW64) "
|
||||||
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||||
"Chrome/47.0.2526.106 Safari/537.36")
|
"Chrome/47.0.2526.106 Safari/537.36")
|
||||||
}
|
}
|
||||||
|
|
||||||
self.websession = async_get_clientsession(hass)
|
self.websession = async_get_clientsession(hass)
|
||||||
|
@ -95,8 +95,7 @@ class UPCDeviceScanner(DeviceScanner):
|
||||||
with async_timeout.timeout(10, loop=self.hass.loop):
|
with async_timeout.timeout(10, loop=self.hass.loop):
|
||||||
response = yield from self.websession.get(
|
response = yield from self.websession.get(
|
||||||
"http://{}/common_page/login.html".format(self.host),
|
"http://{}/common_page/login.html".format(self.host),
|
||||||
headers=self.headers
|
headers=self.headers)
|
||||||
)
|
|
||||||
|
|
||||||
yield from response.text()
|
yield from response.text()
|
||||||
|
|
||||||
|
@ -118,17 +117,15 @@ class UPCDeviceScanner(DeviceScanner):
|
||||||
response = yield from self.websession.post(
|
response = yield from self.websession.post(
|
||||||
"http://{}/xml/getter.xml".format(self.host),
|
"http://{}/xml/getter.xml".format(self.host),
|
||||||
data="token={}&fun={}".format(self.token, function),
|
data="token={}&fun={}".format(self.token, function),
|
||||||
headers=self.headers,
|
headers=self.headers, allow_redirects=False)
|
||||||
allow_redirects=False
|
|
||||||
)
|
|
||||||
|
|
||||||
# error?
|
# Error?
|
||||||
if response.status != 200:
|
if response.status != 200:
|
||||||
_LOGGER.warning("Receive http code %d", response.status)
|
_LOGGER.warning("Receive http code %d", response.status)
|
||||||
self.token = None
|
self.token = None
|
||||||
return
|
return
|
||||||
|
|
||||||
# load data, store token for next request
|
# Load data, store token for next request
|
||||||
self.token = response.cookies['sessionToken'].value
|
self.token = response.cookies['sessionToken'].value
|
||||||
return (yield from response.text())
|
return (yield from response.text())
|
||||||
|
|
||||||
|
|
|
@ -7,27 +7,24 @@ https://home-assistant.io/components/google_assistant/
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from typing import Any, Dict # NOQA
|
||||||
|
|
||||||
|
from aiohttp.hdrs import AUTHORIZATION
|
||||||
|
from aiohttp.web import Request, Response # NOQA
|
||||||
|
|
||||||
# Typing imports
|
# Typing imports
|
||||||
# pylint: disable=using-constant-test,unused-import,ungrouped-imports
|
# pylint: disable=using-constant-test,unused-import,ungrouped-imports
|
||||||
# if False:
|
# if False:
|
||||||
|
from homeassistant.components.http import HomeAssistantView
|
||||||
|
from homeassistant.const import HTTP_BAD_REQUEST, HTTP_UNAUTHORIZED
|
||||||
from homeassistant.core import HomeAssistant # NOQA
|
from homeassistant.core import HomeAssistant # NOQA
|
||||||
from aiohttp.web import Request, Response # NOQA
|
|
||||||
from typing import Dict, Tuple, Any # NOQA
|
|
||||||
from homeassistant.helpers.entity import Entity # NOQA
|
from homeassistant.helpers.entity import Entity # NOQA
|
||||||
|
|
||||||
from homeassistant.components.http import HomeAssistantView
|
|
||||||
|
|
||||||
from homeassistant.const import (HTTP_BAD_REQUEST, HTTP_UNAUTHORIZED)
|
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
GOOGLE_ASSISTANT_API_ENDPOINT,
|
CONF_ACCESS_TOKEN, CONF_EXPOSED_DOMAINS, ATTR_GOOGLE_ASSISTANT,
|
||||||
CONF_ACCESS_TOKEN,
|
CONF_EXPOSE_BY_DEFAULT, DEFAULT_EXPOSED_DOMAINS, DEFAULT_EXPOSE_BY_DEFAULT,
|
||||||
DEFAULT_EXPOSE_BY_DEFAULT,
|
GOOGLE_ASSISTANT_API_ENDPOINT)
|
||||||
DEFAULT_EXPOSED_DOMAINS,
|
from .smart_home import query_device, entity_to_device, determine_service
|
||||||
CONF_EXPOSE_BY_DEFAULT,
|
|
||||||
CONF_EXPOSED_DOMAINS,
|
|
||||||
ATTR_GOOGLE_ASSISTANT)
|
|
||||||
from .smart_home import entity_to_device, query_device, determine_service
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -140,7 +137,7 @@ class GoogleAssistantView(HomeAssistantView):
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def post(self, request: Request) -> Response:
|
def post(self, request: Request) -> Response:
|
||||||
"""Handle Google Assistant requests."""
|
"""Handle Google Assistant requests."""
|
||||||
auth = request.headers.get('Authorization', None)
|
auth = request.headers.get(AUTHORIZATION, None)
|
||||||
if 'Bearer {}'.format(self.access_token) != auth:
|
if 'Bearer {}'.format(self.access_token) != auth:
|
||||||
return self.json_message(
|
return self.json_message(
|
||||||
"missing authorization", status_code=HTTP_UNAUTHORIZED)
|
"missing authorization", status_code=HTTP_UNAUTHORIZED)
|
||||||
|
|
|
@ -5,37 +5,43 @@ For more details about this component, please refer to the documentation at
|
||||||
https://home-assistant.io/components/http/
|
https://home-assistant.io/components/http/
|
||||||
"""
|
"""
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
import logging
|
|
||||||
import ssl
|
|
||||||
from ipaddress import ip_network
|
from ipaddress import ip_network
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import voluptuous as vol
|
import ssl
|
||||||
from aiohttp import web
|
|
||||||
from aiohttp.web_exceptions import HTTPUnauthorized, HTTPMovedPermanently
|
|
||||||
|
|
||||||
|
from aiohttp import web
|
||||||
|
from aiohttp.hdrs import ACCEPT, ORIGIN, CONTENT_TYPE
|
||||||
|
from aiohttp.web_exceptions import HTTPUnauthorized, HTTPMovedPermanently
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.const import (
|
||||||
|
SERVER_PORT, CONTENT_TYPE_JSON, HTTP_HEADER_HA_AUTH,
|
||||||
|
EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START,
|
||||||
|
HTTP_HEADER_X_REQUESTED_WITH)
|
||||||
|
from homeassistant.core import is_callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
import homeassistant.remote as rem
|
import homeassistant.remote as rem
|
||||||
import homeassistant.util as hass_util
|
import homeassistant.util as hass_util
|
||||||
from homeassistant.const import (
|
|
||||||
SERVER_PORT, CONTENT_TYPE_JSON, ALLOWED_CORS_HEADERS,
|
|
||||||
EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START)
|
|
||||||
from homeassistant.core import is_callback
|
|
||||||
from homeassistant.util.logging import HideSensitiveDataFilter
|
from homeassistant.util.logging import HideSensitiveDataFilter
|
||||||
|
|
||||||
from .auth import auth_middleware
|
from .auth import auth_middleware
|
||||||
from .ban import ban_middleware
|
from .ban import ban_middleware
|
||||||
from .const import (
|
from .const import (
|
||||||
KEY_USE_X_FORWARDED_FOR, KEY_TRUSTED_NETWORKS, KEY_BANS_ENABLED,
|
KEY_BANS_ENABLED, KEY_AUTHENTICATED, KEY_LOGIN_THRESHOLD,
|
||||||
KEY_LOGIN_THRESHOLD, KEY_AUTHENTICATED)
|
KEY_TRUSTED_NETWORKS, KEY_USE_X_FORWARDED_FOR)
|
||||||
from .static import (
|
from .static import (
|
||||||
staticresource_middleware, CachingFileResponse, CachingStaticResource)
|
CachingFileResponse, CachingStaticResource, staticresource_middleware)
|
||||||
from .util import get_real_ip
|
from .util import get_real_ip
|
||||||
|
|
||||||
REQUIREMENTS = ['aiohttp_cors==0.5.3']
|
REQUIREMENTS = ['aiohttp_cors==0.5.3']
|
||||||
|
|
||||||
|
ALLOWED_CORS_HEADERS = [
|
||||||
|
ORIGIN, ACCEPT, HTTP_HEADER_X_REQUESTED_WITH, CONTENT_TYPE,
|
||||||
|
HTTP_HEADER_HA_AUTH]
|
||||||
|
|
||||||
DOMAIN = 'http'
|
DOMAIN = 'http'
|
||||||
|
|
||||||
CONF_API_PASSWORD = 'api_password'
|
CONF_API_PASSWORD = 'api_password'
|
||||||
|
|
|
@ -12,27 +12,27 @@ import logging
|
||||||
import os
|
import os
|
||||||
from random import SystemRandom
|
from random import SystemRandom
|
||||||
|
|
||||||
from aiohttp import web, hdrs
|
from aiohttp import web
|
||||||
|
from aiohttp.hdrs import CONTENT_TYPE, CACHE_CONTROL
|
||||||
import async_timeout
|
import async_timeout
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.components.http import KEY_AUTHENTICATED, HomeAssistantView
|
||||||
from homeassistant.config import load_yaml_config_file
|
from homeassistant.config import load_yaml_config_file
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.const import (
|
||||||
from homeassistant.helpers.entity import Entity
|
STATE_OFF, STATE_IDLE, STATE_PLAYING, STATE_UNKNOWN, ATTR_ENTITY_ID,
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
SERVICE_TOGGLE, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_VOLUME_UP,
|
||||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
SERVICE_MEDIA_PLAY, SERVICE_MEDIA_SEEK, SERVICE_MEDIA_STOP,
|
||||||
from homeassistant.components.http import HomeAssistantView, KEY_AUTHENTICATED
|
SERVICE_VOLUME_SET, SERVICE_MEDIA_PAUSE, SERVICE_SHUFFLE_SET,
|
||||||
|
SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE, SERVICE_MEDIA_NEXT_TRACK,
|
||||||
|
SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PREVIOUS_TRACK)
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||||
|
from homeassistant.helpers.entity import Entity
|
||||||
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
|
from homeassistant.loader import bind_hass
|
||||||
from homeassistant.util.async import run_coroutine_threadsafe
|
from homeassistant.util.async import run_coroutine_threadsafe
|
||||||
from homeassistant.const import (
|
|
||||||
STATE_OFF, STATE_UNKNOWN, STATE_PLAYING, STATE_IDLE,
|
|
||||||
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON,
|
|
||||||
SERVICE_VOLUME_UP, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_SET,
|
|
||||||
SERVICE_VOLUME_MUTE, SERVICE_TOGGLE, SERVICE_MEDIA_STOP,
|
|
||||||
SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE,
|
|
||||||
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_SEEK,
|
|
||||||
SERVICE_SHUFFLE_SET)
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
_RND = SystemRandom()
|
_RND = SystemRandom()
|
||||||
|
@ -53,8 +53,6 @@ ENTITY_IMAGE_CACHE = {
|
||||||
ATTR_CACHE_MAXSIZE: 16
|
ATTR_CACHE_MAXSIZE: 16
|
||||||
}
|
}
|
||||||
|
|
||||||
CONTENT_TYPE_HEADER = 'Content-Type'
|
|
||||||
|
|
||||||
SERVICE_PLAY_MEDIA = 'play_media'
|
SERVICE_PLAY_MEDIA = 'play_media'
|
||||||
SERVICE_SELECT_SOURCE = 'select_source'
|
SERVICE_SELECT_SOURCE = 'select_source'
|
||||||
SERVICE_CLEAR_PLAYLIST = 'clear_playlist'
|
SERVICE_CLEAR_PLAYLIST = 'clear_playlist'
|
||||||
|
@ -911,7 +909,7 @@ def _async_fetch_image(hass, url):
|
||||||
|
|
||||||
if response.status == 200:
|
if response.status == 200:
|
||||||
content = yield from response.read()
|
content = yield from response.read()
|
||||||
content_type = response.headers.get(CONTENT_TYPE_HEADER)
|
content_type = response.headers.get(CONTENT_TYPE)
|
||||||
if content_type:
|
if content_type:
|
||||||
content_type = content_type.split(';')[0]
|
content_type = content_type.split(';')[0]
|
||||||
|
|
||||||
|
@ -965,8 +963,6 @@ class MediaPlayerImageView(HomeAssistantView):
|
||||||
if data is None:
|
if data is None:
|
||||||
return web.Response(status=500)
|
return web.Response(status=500)
|
||||||
|
|
||||||
headers = {hdrs.CACHE_CONTROL: 'max-age=3600'}
|
headers = {CACHE_CONTROL: 'max-age=3600'}
|
||||||
return web.Response(
|
return web.Response(
|
||||||
body=data,
|
body=data, content_type=content_type, headers=headers)
|
||||||
content_type=content_type,
|
|
||||||
headers=headers)
|
|
||||||
|
|
|
@ -4,33 +4,37 @@ Bluesound.
|
||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/media_player.bluesound/
|
https://home-assistant.io/components/media_player.bluesound/
|
||||||
"""
|
"""
|
||||||
import logging
|
|
||||||
from datetime import timedelta
|
|
||||||
from asyncio.futures import CancelledError
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import voluptuous as vol
|
from asyncio.futures import CancelledError
|
||||||
from aiohttp.client_exceptions import ClientError
|
from datetime import timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
from aiohttp.client_exceptions import ClientError
|
||||||
|
from aiohttp.hdrs import CONNECTION, KEEP_ALIVE
|
||||||
import async_timeout
|
import async_timeout
|
||||||
from homeassistant.helpers.event import async_track_time_interval
|
import voluptuous as vol
|
||||||
from homeassistant.core import callback
|
|
||||||
from homeassistant.util import Throttle
|
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
|
||||||
import homeassistant.util.dt as dt_util
|
|
||||||
|
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK,
|
SUPPORT_PLAY, SUPPORT_SEEK, SUPPORT_STOP, SUPPORT_PAUSE, PLATFORM_SCHEMA,
|
||||||
SUPPORT_PLAY_MEDIA, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_STOP,
|
MEDIA_TYPE_MUSIC, SUPPORT_NEXT_TRACK, SUPPORT_PLAY_MEDIA,
|
||||||
SUPPORT_PLAY, MediaPlayerDevice, PLATFORM_SCHEMA, MEDIA_TYPE_MUSIC,
|
SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_STEP,
|
||||||
SUPPORT_CLEAR_PLAYLIST, SUPPORT_SELECT_SOURCE, SUPPORT_VOLUME_STEP)
|
SUPPORT_SELECT_SOURCE, SUPPORT_CLEAR_PLAYLIST, SUPPORT_PREVIOUS_TRACK,
|
||||||
|
MediaPlayerDevice)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
|
CONF_HOST, CONF_NAME, CONF_PORT, CONF_HOSTS, STATE_IDLE, STATE_PAUSED,
|
||||||
STATE_PLAYING, STATE_PAUSED, STATE_IDLE, CONF_HOSTS,
|
STATE_PLAYING, EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START)
|
||||||
CONF_HOST, CONF_PORT, CONF_NAME)
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.helpers.event import async_track_time_interval
|
||||||
|
from homeassistant.util import Throttle
|
||||||
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
REQUIREMENTS = ['xmltodict==0.11.0']
|
REQUIREMENTS = ['xmltodict==0.11.0']
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
STATE_OFFLINE = 'offline'
|
STATE_OFFLINE = 'offline'
|
||||||
ATTR_MODEL = 'model'
|
ATTR_MODEL = 'model'
|
||||||
ATTR_MODEL_NAME = 'model_name'
|
ATTR_MODEL_NAME = 'model_name'
|
||||||
|
@ -46,8 +50,6 @@ UPDATE_PRESETS_INTERVAL = timedelta(minutes=30)
|
||||||
NODE_OFFLINE_CHECK_TIMEOUT = 180
|
NODE_OFFLINE_CHECK_TIMEOUT = 180
|
||||||
NODE_RETRY_INITIATION = timedelta(minutes=3)
|
NODE_RETRY_INITIATION = timedelta(minutes=3)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
vol.Optional(CONF_HOSTS): vol.All(cv.ensure_list, [{
|
vol.Optional(CONF_HOSTS): vol.All(cv.ensure_list, [{
|
||||||
vol.Required(CONF_HOST): cv.string,
|
vol.Required(CONF_HOST): cv.string,
|
||||||
|
@ -80,20 +82,15 @@ def _add_player(hass, async_add_devices, host, port=None, name=None):
|
||||||
def _add_player_cb():
|
def _add_player_cb():
|
||||||
"""Add player after first sync fetch."""
|
"""Add player after first sync fetch."""
|
||||||
async_add_devices([player])
|
async_add_devices([player])
|
||||||
_LOGGER.info('Added Bluesound device with name: %s', player.name)
|
_LOGGER.info("Added device with name: %s", player.name)
|
||||||
|
|
||||||
if hass.is_running:
|
if hass.is_running:
|
||||||
_start_polling()
|
_start_polling()
|
||||||
else:
|
else:
|
||||||
hass.bus.async_listen_once(
|
hass.bus.async_listen_once(
|
||||||
EVENT_HOMEASSISTANT_START,
|
EVENT_HOMEASSISTANT_START, _start_polling)
|
||||||
_start_polling
|
|
||||||
)
|
|
||||||
|
|
||||||
hass.bus.async_listen_once(
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _stop_polling)
|
||||||
EVENT_HOMEASSISTANT_STOP,
|
|
||||||
_stop_polling
|
|
||||||
)
|
|
||||||
|
|
||||||
player = BluesoundPlayer(hass, host, port, name, _add_player_cb)
|
player = BluesoundPlayer(hass, host, port, name, _add_player_cb)
|
||||||
hass.data[DATA_BLUESOUND].append(player)
|
hass.data[DATA_BLUESOUND].append(player)
|
||||||
|
@ -101,10 +98,7 @@ def _add_player(hass, async_add_devices, host, port=None, name=None):
|
||||||
if hass.is_running:
|
if hass.is_running:
|
||||||
_init_player()
|
_init_player()
|
||||||
else:
|
else:
|
||||||
hass.bus.async_listen_once(
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, _init_player)
|
||||||
EVENT_HOMEASSISTANT_START,
|
|
||||||
_init_player
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
@ -121,11 +115,9 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||||
hosts = config.get(CONF_HOSTS, None)
|
hosts = config.get(CONF_HOSTS, None)
|
||||||
if hosts:
|
if hosts:
|
||||||
for host in hosts:
|
for host in hosts:
|
||||||
_add_player(hass,
|
_add_player(
|
||||||
async_add_devices,
|
hass, async_add_devices, host.get(CONF_HOST),
|
||||||
host.get(CONF_HOST),
|
host.get(CONF_PORT), host.get(CONF_NAME, None))
|
||||||
host.get(CONF_PORT, None),
|
|
||||||
host.get(CONF_NAME, None))
|
|
||||||
|
|
||||||
|
|
||||||
class BluesoundPlayer(MediaPlayerDevice):
|
class BluesoundPlayer(MediaPlayerDevice):
|
||||||
|
@ -137,7 +129,7 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self._port = port
|
self._port = port
|
||||||
self._polling_session = async_get_clientsession(hass)
|
self._polling_session = async_get_clientsession(hass)
|
||||||
self._polling_task = None # The actuall polling task.
|
self._polling_task = None # The actual polling task.
|
||||||
self._name = name
|
self._name = name
|
||||||
self._brand = None
|
self._brand = None
|
||||||
self._model = None
|
self._model = None
|
||||||
|
@ -156,7 +148,6 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||||
if self._port is None:
|
if self._port is None:
|
||||||
self._port = DEFAULT_PORT
|
self._port = DEFAULT_PORT
|
||||||
|
|
||||||
# Internal methods
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _try_get_index(string, seach_string):
|
def _try_get_index(string, seach_string):
|
||||||
try:
|
try:
|
||||||
|
@ -165,13 +156,12 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def _internal_update_sync_status(self, on_updated_cb=None,
|
def _internal_update_sync_status(
|
||||||
raise_timeout=False):
|
self, on_updated_cb=None, raise_timeout=False):
|
||||||
resp = None
|
resp = None
|
||||||
try:
|
try:
|
||||||
resp = yield from self.send_bluesound_command(
|
resp = yield from self.send_bluesound_command(
|
||||||
'SyncStatus',
|
'SyncStatus', raise_timeout, raise_timeout)
|
||||||
raise_timeout, raise_timeout)
|
|
||||||
except:
|
except:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
@ -193,9 +183,7 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||||
if on_updated_cb:
|
if on_updated_cb:
|
||||||
on_updated_cb()
|
on_updated_cb()
|
||||||
return True
|
return True
|
||||||
# END Internal methods
|
|
||||||
|
|
||||||
# Poll functionality
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def _start_poll_command(self):
|
def _start_poll_command(self):
|
||||||
""""Loop which polls the status of the player."""
|
""""Loop which polls the status of the player."""
|
||||||
|
@ -204,14 +192,13 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||||
yield from self.async_update_status()
|
yield from self.async_update_status()
|
||||||
|
|
||||||
except (asyncio.TimeoutError, ClientError):
|
except (asyncio.TimeoutError, ClientError):
|
||||||
_LOGGER.info("Bluesound node %s is offline, retrying later",
|
_LOGGER.info("Node %s is offline, retrying later", self._name)
|
||||||
self._name)
|
yield from asyncio.sleep(
|
||||||
yield from asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT,
|
NODE_OFFLINE_CHECK_TIMEOUT, loop=self._hass.loop)
|
||||||
loop=self._hass.loop)
|
|
||||||
self.start_polling()
|
self.start_polling()
|
||||||
|
|
||||||
except CancelledError:
|
except CancelledError:
|
||||||
_LOGGER.debug("Stopping bluesound polling of node %s", self._name)
|
_LOGGER.debug("Stopping the polling of node %s", self._name)
|
||||||
except:
|
except:
|
||||||
_LOGGER.exception("Unexpected error in %s", self._name)
|
_LOGGER.exception("Unexpected error in %s", self._name)
|
||||||
raise
|
raise
|
||||||
|
@ -224,9 +211,7 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||||
def stop_polling(self):
|
def stop_polling(self):
|
||||||
"""Stop the polling task."""
|
"""Stop the polling task."""
|
||||||
self._polling_task.cancel()
|
self._polling_task.cancel()
|
||||||
# END Poll functionality
|
|
||||||
|
|
||||||
# Initiator
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_init(self):
|
def async_init(self):
|
||||||
"""Initiate the player async."""
|
"""Initiate the player async."""
|
||||||
|
@ -235,22 +220,17 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||||
self._retry_remove()
|
self._retry_remove()
|
||||||
self._retry_remove = None
|
self._retry_remove = None
|
||||||
|
|
||||||
yield from self._internal_update_sync_status(self._init_callback,
|
yield from self._internal_update_sync_status(
|
||||||
True)
|
self._init_callback, True)
|
||||||
except (asyncio.TimeoutError, ClientError):
|
except (asyncio.TimeoutError, ClientError):
|
||||||
_LOGGER.info("Bluesound node %s is offline, retrying later",
|
_LOGGER.info("Node %s is offline, retrying later", self.host)
|
||||||
self.host)
|
|
||||||
self._retry_remove = async_track_time_interval(
|
self._retry_remove = async_track_time_interval(
|
||||||
self._hass,
|
self._hass, self.async_init, NODE_RETRY_INITIATION)
|
||||||
self.async_init,
|
|
||||||
NODE_RETRY_INITIATION)
|
|
||||||
except:
|
except:
|
||||||
_LOGGER.exception("Unexpected when initiating error in %s",
|
_LOGGER.exception("Unexpected when initiating error in %s",
|
||||||
self.host)
|
self.host)
|
||||||
raise
|
raise
|
||||||
# END Initiator
|
|
||||||
|
|
||||||
# Status updates fetchers
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_update(self):
|
def async_update(self):
|
||||||
"""Update internal status of the entity."""
|
"""Update internal status of the entity."""
|
||||||
|
@ -275,7 +255,7 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||||
method = method[1:]
|
method = method[1:]
|
||||||
url = "http://{}:{}/{}".format(self.host, self._port, method)
|
url = "http://{}:{}/{}".format(self.host, self._port, method)
|
||||||
|
|
||||||
_LOGGER.info("calling URL: %s", url)
|
_LOGGER.debug("Calling URL: %s", url)
|
||||||
response = None
|
response = None
|
||||||
try:
|
try:
|
||||||
websession = async_get_clientsession(self._hass)
|
websession = async_get_clientsession(self._hass)
|
||||||
|
@ -294,11 +274,10 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||||
|
|
||||||
except (asyncio.TimeoutError, aiohttp.ClientError):
|
except (asyncio.TimeoutError, aiohttp.ClientError):
|
||||||
if raise_timeout:
|
if raise_timeout:
|
||||||
_LOGGER.info("Timeout with Bluesound: %s", self.host)
|
_LOGGER.info("Timeout: %s", self.host)
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
_LOGGER.debug("Failed communicating with Bluesound: %s",
|
_LOGGER.debug("Failed communicating: %s", self.host)
|
||||||
self.host)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
@ -315,17 +294,17 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||||
etag = self._status.get('@etag', '')
|
etag = self._status.get('@etag', '')
|
||||||
|
|
||||||
if etag != '':
|
if etag != '':
|
||||||
url = 'Status?etag='+etag+'&timeout=60.0'
|
url = 'Status?etag={}&timeout=60.0'.format(etag)
|
||||||
url = "http://{}:{}/{}".format(self.host, self._port, url)
|
url = "http://{}:{}/{}".format(self.host, self._port, url)
|
||||||
|
|
||||||
_LOGGER.debug("calling URL: %s", url)
|
_LOGGER.debug("Calling URL: %s", url)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
with async_timeout.timeout(65, loop=self._hass.loop):
|
with async_timeout.timeout(65, loop=self._hass.loop):
|
||||||
response = yield from self._polling_session.get(
|
response = yield from self._polling_session.get(
|
||||||
url,
|
url,
|
||||||
headers={'connection': 'keep-alive'})
|
headers={CONNECTION: KEEP_ALIVE})
|
||||||
|
|
||||||
if response.status != 200:
|
if response.status != 200:
|
||||||
_LOGGER.error("Error %s on %s", response.status, url)
|
_LOGGER.error("Error %s on %s", response.status, url)
|
||||||
|
@ -350,8 +329,8 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||||
def async_update_sync_status(self, on_updated_cb=None,
|
def async_update_sync_status(self, on_updated_cb=None,
|
||||||
raise_timeout=False):
|
raise_timeout=False):
|
||||||
"""Update sync status."""
|
"""Update sync status."""
|
||||||
yield from self._internal_update_sync_status(on_updated_cb,
|
yield from self._internal_update_sync_status(
|
||||||
raise_timeout=False)
|
on_updated_cb, raise_timeout=False)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
@Throttle(UPDATE_CAPTURE_INTERVAL)
|
@Throttle(UPDATE_CAPTURE_INTERVAL)
|
||||||
|
@ -436,9 +415,7 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||||
_create_service_item(resp['services']['service'])
|
_create_service_item(resp['services']['service'])
|
||||||
|
|
||||||
return self._services_items
|
return self._services_items
|
||||||
# END Status updates fetchers
|
|
||||||
|
|
||||||
# Media player (and core) properties
|
|
||||||
@property
|
@property
|
||||||
def should_poll(self):
|
def should_poll(self):
|
||||||
"""No need to poll information."""
|
"""No need to poll information."""
|
||||||
|
@ -611,17 +588,17 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||||
stream_url = self._status.get('streamUrl', '')
|
stream_url = self._status.get('streamUrl', '')
|
||||||
|
|
||||||
if self._status.get('is_preset', '') == '1' and stream_url != '':
|
if self._status.get('is_preset', '') == '1' and stream_url != '':
|
||||||
# this check doesn't work with all presets, for example playlists.
|
# This check doesn't work with all presets, for example playlists.
|
||||||
# But it works with radio service_items will catch playlists
|
# But it works with radio service_items will catch playlists.
|
||||||
items = [x for x in self._preset_items if 'url2' in x and
|
items = [x for x in self._preset_items if 'url2' in x and
|
||||||
parse.unquote(x['url2']) == stream_url]
|
parse.unquote(x['url2']) == stream_url]
|
||||||
if len(items) > 0:
|
if len(items) > 0:
|
||||||
return items[0]['title']
|
return items[0]['title']
|
||||||
|
|
||||||
# this could be a bit difficult to detect. Bluetooth could be named
|
# This could be a bit difficult to detect. Bluetooth could be named
|
||||||
# different things and there is not any way to match chooses in
|
# different things and there is not any way to match chooses in
|
||||||
# capture list to current playing. It's a bit of guesswork.
|
# capture list to current playing. It's a bit of guesswork.
|
||||||
# This method will be needing some tweaking over time
|
# This method will be needing some tweaking over time.
|
||||||
title = self._status.get('title1', '').lower()
|
title = self._status.get('title1', '').lower()
|
||||||
if title == 'bluetooth' or stream_url == 'Capture:hw:2,0/44100/16/2':
|
if title == 'bluetooth' or stream_url == 'Capture:hw:2,0/44100/16/2':
|
||||||
items = [x for x in self._capture_items
|
items = [x for x in self._capture_items
|
||||||
|
@ -660,7 +637,7 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||||
return items[0]['title']
|
return items[0]['title']
|
||||||
|
|
||||||
if self._status.get('streamUrl', '') != '':
|
if self._status.get('streamUrl', '') != '':
|
||||||
_LOGGER.debug("Couldn't find source of stream url: %s",
|
_LOGGER.debug("Couldn't find source of stream URL: %s",
|
||||||
self._status.get('streamUrl', ''))
|
self._status.get('streamUrl', ''))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -695,9 +672,7 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||||
ATTR_MODEL_NAME: self._model_name,
|
ATTR_MODEL_NAME: self._model_name,
|
||||||
ATTR_BRAND: self._brand,
|
ATTR_BRAND: self._brand,
|
||||||
}
|
}
|
||||||
# END Media player (and core) properties
|
|
||||||
|
|
||||||
# Media player commands
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_select_source(self, source):
|
def async_select_source(self, source):
|
||||||
"""Select input source."""
|
"""Select input source."""
|
||||||
|
@ -712,8 +687,8 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||||
return
|
return
|
||||||
|
|
||||||
selected_source = items[0]
|
selected_source = items[0]
|
||||||
url = 'Play?url={}&preset_id&image={}'.format(selected_source['url'],
|
url = 'Play?url={}&preset_id&image={}'.format(
|
||||||
selected_source['image'])
|
selected_source['url'], selected_source['image'])
|
||||||
|
|
||||||
if 'is_raw_url' in selected_source and selected_source['is_raw_url']:
|
if 'is_raw_url' in selected_source and selected_source['is_raw_url']:
|
||||||
url = selected_source['url']
|
url = selected_source['url']
|
||||||
|
@ -806,4 +781,3 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||||
else:
|
else:
|
||||||
return self.send_bluesound_command(
|
return self.send_bluesound_command(
|
||||||
'Volume?level=' + str(float(self._lastvol) * 100))
|
'Volume?level=' + str(float(self._lastvol) * 100))
|
||||||
# END Media player commands
|
|
||||||
|
|
|
@ -6,18 +6,18 @@ https://home-assistant.io/components/no_ip/
|
||||||
"""
|
"""
|
||||||
import asyncio
|
import asyncio
|
||||||
import base64
|
import base64
|
||||||
import logging
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
from aiohttp.hdrs import USER_AGENT, AUTHORIZATION
|
||||||
import async_timeout
|
import async_timeout
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_DOMAIN, CONF_PASSWORD, CONF_TIMEOUT, CONF_USERNAME, HTTP_HEADER_AUTH,
|
CONF_DOMAIN, CONF_TIMEOUT, CONF_PASSWORD, CONF_USERNAME)
|
||||||
HTTP_HEADER_USER_AGENT)
|
|
||||||
from homeassistant.helpers.aiohttp_client import SERVER_SOFTWARE
|
from homeassistant.helpers.aiohttp_client import SERVER_SOFTWARE
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ NO_IP_ERRORS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
UPDATE_URL = 'https://dynupdate.noip.com/nic/update'
|
UPDATE_URL = 'https://dynupdate.noip.com/nic/update'
|
||||||
USER_AGENT = "{} {}".format(SERVER_SOFTWARE, EMAIL)
|
HA_USER_AGENT = "{} {}".format(SERVER_SOFTWARE, EMAIL)
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema({
|
CONFIG_SCHEMA = vol.Schema({
|
||||||
DOMAIN: vol.Schema({
|
DOMAIN: vol.Schema({
|
||||||
|
@ -92,8 +92,8 @@ def _update_no_ip(hass, session, domain, auth_str, timeout):
|
||||||
}
|
}
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
HTTP_HEADER_AUTH: "Basic {}".format(auth_str.decode('utf-8')),
|
AUTHORIZATION: "Basic {}".format(auth_str.decode('utf-8')),
|
||||||
HTTP_HEADER_USER_AGENT: USER_AGENT,
|
USER_AGENT: HA_USER_AGENT,
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -6,22 +6,22 @@ https://home-assistant.io/components/notify.clicksend/
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import requests
|
|
||||||
|
|
||||||
|
from aiohttp.hdrs import CONTENT_TYPE
|
||||||
|
import requests
|
||||||
import voluptuous as vol
|
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 (
|
from homeassistant.components.notify import (
|
||||||
PLATFORM_SCHEMA, BaseNotificationService)
|
PLATFORM_SCHEMA, BaseNotificationService)
|
||||||
|
from homeassistant.const import (
|
||||||
|
CONF_API_KEY, CONF_USERNAME, CONF_RECIPIENT, CONTENT_TYPE_JSON)
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
BASE_API_URL = 'https://rest.clicksend.com/v3'
|
BASE_API_URL = 'https://rest.clicksend.com/v3'
|
||||||
|
|
||||||
HEADERS = {HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_JSON}
|
HEADERS = {CONTENT_TYPE: CONTENT_TYPE_JSON}
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
vol.Required(CONF_USERNAME): cv.string,
|
vol.Required(CONF_USERNAME): cv.string,
|
||||||
|
|
|
@ -8,22 +8,22 @@ https://home-assistant.io/components/notify.clicksend_tts/
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import requests
|
|
||||||
|
|
||||||
|
from aiohttp.hdrs import CONTENT_TYPE
|
||||||
|
import requests
|
||||||
import voluptuous as vol
|
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 (
|
from homeassistant.components.notify import (
|
||||||
PLATFORM_SCHEMA, BaseNotificationService)
|
PLATFORM_SCHEMA, BaseNotificationService)
|
||||||
|
from homeassistant.const import (
|
||||||
|
CONF_API_KEY, CONF_USERNAME, CONF_RECIPIENT, CONTENT_TYPE_JSON)
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
BASE_API_URL = 'https://rest.clicksend.com/v3'
|
BASE_API_URL = 'https://rest.clicksend.com/v3'
|
||||||
|
|
||||||
HEADERS = {HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_JSON}
|
HEADERS = {CONTENT_TYPE: CONTENT_TYPE_JSON}
|
||||||
|
|
||||||
CONF_LANGUAGE = 'language'
|
CONF_LANGUAGE = 'language'
|
||||||
CONF_VOICE = 'voice'
|
CONF_VOICE = 'voice'
|
||||||
|
|
|
@ -6,14 +6,14 @@ https://home-assistant.io/components/notify.facebook/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from aiohttp.hdrs import CONTENT_TYPE
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.components.notify import (
|
from homeassistant.components.notify import (
|
||||||
ATTR_TARGET, ATTR_DATA, PLATFORM_SCHEMA, BaseNotificationService)
|
ATTR_DATA, ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService)
|
||||||
from homeassistant.const import CONTENT_TYPE_JSON
|
from homeassistant.const import CONTENT_TYPE_JSON
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ class FacebookNotificationService(BaseNotificationService):
|
||||||
import json
|
import json
|
||||||
resp = requests.post(BASE_URL, data=json.dumps(body),
|
resp = requests.post(BASE_URL, data=json.dumps(body),
|
||||||
params=payload,
|
params=payload,
|
||||||
headers={'Content-Type': CONTENT_TYPE_JSON},
|
headers={CONTENT_TYPE: CONTENT_TYPE_JSON},
|
||||||
timeout=10)
|
timeout=10)
|
||||||
if resp.status_code != 200:
|
if resp.status_code != 200:
|
||||||
obj = resp.json()
|
obj = resp.json()
|
||||||
|
|
|
@ -5,25 +5,26 @@ For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/notify.html5/
|
https://home-assistant.io/components/notify.html5/
|
||||||
"""
|
"""
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
|
||||||
import logging
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
import datetime
|
import datetime
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from aiohttp.hdrs import AUTHORIZATION
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from voluptuous.humanize import humanize_error
|
from voluptuous.humanize import humanize_error
|
||||||
|
|
||||||
from homeassistant.const import (HTTP_BAD_REQUEST, HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
HTTP_UNAUTHORIZED, URL_ROOT)
|
|
||||||
from homeassistant.util import ensure_unique_string
|
|
||||||
from homeassistant.components.notify import (
|
|
||||||
ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, ATTR_DATA,
|
|
||||||
BaseNotificationService, PLATFORM_SCHEMA)
|
|
||||||
from homeassistant.components.http import HomeAssistantView
|
|
||||||
from homeassistant.components.frontend import add_manifest_json_key
|
from homeassistant.components.frontend import add_manifest_json_key
|
||||||
|
from homeassistant.components.http import HomeAssistantView
|
||||||
|
from homeassistant.components.notify import (
|
||||||
|
ATTR_DATA, ATTR_TITLE, ATTR_TARGET, PLATFORM_SCHEMA, ATTR_TITLE_DEFAULT,
|
||||||
|
BaseNotificationService)
|
||||||
|
from homeassistant.const import (
|
||||||
|
URL_ROOT, HTTP_BAD_REQUEST, HTTP_UNAUTHORIZED, HTTP_INTERNAL_SERVER_ERROR)
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
|
from homeassistant.util import ensure_unique_string
|
||||||
|
|
||||||
REQUIREMENTS = ['pywebpush==1.1.0', 'PyJWT==1.5.3']
|
REQUIREMENTS = ['pywebpush==1.1.0', 'PyJWT==1.5.3']
|
||||||
|
|
||||||
|
@ -62,24 +63,25 @@ ATTR_JWT = 'jwt'
|
||||||
# is valid.
|
# is valid.
|
||||||
JWT_VALID_DAYS = 7
|
JWT_VALID_DAYS = 7
|
||||||
|
|
||||||
KEYS_SCHEMA = vol.All(dict,
|
KEYS_SCHEMA = vol.All(
|
||||||
vol.Schema({
|
dict, vol.Schema({
|
||||||
vol.Required(ATTR_AUTH): cv.string,
|
vol.Required(ATTR_AUTH): cv.string,
|
||||||
vol.Required(ATTR_P256DH): cv.string
|
vol.Required(ATTR_P256DH): cv.string,
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
|
|
||||||
SUBSCRIPTION_SCHEMA = vol.All(dict,
|
SUBSCRIPTION_SCHEMA = vol.All(
|
||||||
vol.Schema({
|
dict, vol.Schema({
|
||||||
# pylint: disable=no-value-for-parameter
|
# pylint: disable=no-value-for-parameter
|
||||||
vol.Required(ATTR_ENDPOINT): vol.Url(),
|
vol.Required(ATTR_ENDPOINT): vol.Url(),
|
||||||
vol.Required(ATTR_KEYS): KEYS_SCHEMA,
|
vol.Required(ATTR_KEYS): KEYS_SCHEMA,
|
||||||
vol.Optional(ATTR_EXPIRATIONTIME):
|
vol.Optional(ATTR_EXPIRATIONTIME): vol.Any(None, cv.positive_int),
|
||||||
vol.Any(None, cv.positive_int)
|
})
|
||||||
}))
|
)
|
||||||
|
|
||||||
REGISTER_SCHEMA = vol.Schema({
|
REGISTER_SCHEMA = vol.Schema({
|
||||||
vol.Required(ATTR_SUBSCRIPTION): SUBSCRIPTION_SCHEMA,
|
vol.Required(ATTR_SUBSCRIPTION): SUBSCRIPTION_SCHEMA,
|
||||||
vol.Required(ATTR_BROWSER): vol.In(['chrome', 'firefox'])
|
vol.Required(ATTR_BROWSER): vol.In(['chrome', 'firefox']),
|
||||||
})
|
})
|
||||||
|
|
||||||
CALLBACK_EVENT_PAYLOAD_SCHEMA = vol.Schema({
|
CALLBACK_EVENT_PAYLOAD_SCHEMA = vol.Schema({
|
||||||
|
@ -145,7 +147,7 @@ class JSONBytesDecoder(json.JSONEncoder):
|
||||||
|
|
||||||
# pylint: disable=method-hidden
|
# pylint: disable=method-hidden
|
||||||
def default(self, obj):
|
def default(self, obj):
|
||||||
"""Decode object if it's a bytes object, else defer to baseclass."""
|
"""Decode object if it's a bytes object, else defer to base class."""
|
||||||
if isinstance(obj, bytes):
|
if isinstance(obj, bytes):
|
||||||
return obj.decode()
|
return obj.decode()
|
||||||
return json.JSONEncoder.default(self, obj)
|
return json.JSONEncoder.default(self, obj)
|
||||||
|
@ -158,7 +160,7 @@ def _save_config(filename, config):
|
||||||
fdesc.write(json.dumps(
|
fdesc.write(json.dumps(
|
||||||
config, cls=JSONBytesDecoder, indent=4, sort_keys=True))
|
config, cls=JSONBytesDecoder, indent=4, sort_keys=True))
|
||||||
except (IOError, TypeError) as error:
|
except (IOError, TypeError) as error:
|
||||||
_LOGGER.error("Saving config file failed: %s", error)
|
_LOGGER.error("Saving configuration file failed: %s", error)
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -266,7 +268,7 @@ class HTML5PushCallbackView(HomeAssistantView):
|
||||||
def check_authorization_header(self, request):
|
def check_authorization_header(self, request):
|
||||||
"""Check the authorization header."""
|
"""Check the authorization header."""
|
||||||
import jwt
|
import jwt
|
||||||
auth = request.headers.get('Authorization', None)
|
auth = request.headers.get(AUTHORIZATION, None)
|
||||||
if not auth:
|
if not auth:
|
||||||
return self.json_message('Authorization header is expected',
|
return self.json_message('Authorization header is expected',
|
||||||
status_code=HTTP_UNAUTHORIZED)
|
status_code=HTTP_UNAUTHORIZED)
|
||||||
|
@ -323,8 +325,7 @@ class HTML5PushCallbackView(HomeAssistantView):
|
||||||
event_name = '{}.{}'.format(NOTIFY_CALLBACK_EVENT,
|
event_name = '{}.{}'.format(NOTIFY_CALLBACK_EVENT,
|
||||||
event_payload[ATTR_TYPE])
|
event_payload[ATTR_TYPE])
|
||||||
request.app['hass'].bus.fire(event_name, event_payload)
|
request.app['hass'].bus.fire(event_name, event_payload)
|
||||||
return self.json({'status': 'ok',
|
return self.json({'status': 'ok', 'event': event_payload[ATTR_TYPE]})
|
||||||
'event': event_payload[ATTR_TYPE]})
|
|
||||||
|
|
||||||
|
|
||||||
class HTML5NotificationService(BaseNotificationService):
|
class HTML5NotificationService(BaseNotificationService):
|
||||||
|
@ -413,6 +414,6 @@ class HTML5NotificationService(BaseNotificationService):
|
||||||
if not _save_config(self.registrations_json_path,
|
if not _save_config(self.registrations_json_path,
|
||||||
self.registrations):
|
self.registrations):
|
||||||
self.registrations[target] = reg
|
self.registrations[target] = reg
|
||||||
_LOGGER.error("Error saving registration.")
|
_LOGGER.error("Error saving registration")
|
||||||
else:
|
else:
|
||||||
_LOGGER.info("Configuration saved")
|
_LOGGER.info("Configuration saved")
|
||||||
|
|
|
@ -7,14 +7,14 @@ https://home-assistant.io/components/notify.instapush/
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from aiohttp.hdrs import CONTENT_TYPE
|
||||||
import requests
|
import requests
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.components.notify import (
|
from homeassistant.components.notify import (
|
||||||
ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService)
|
ATTR_TITLE, PLATFORM_SCHEMA, ATTR_TITLE_DEFAULT, BaseNotificationService)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import CONF_API_KEY, CONTENT_TYPE_JSON
|
||||||
CONF_API_KEY, HTTP_HEADER_CONTENT_TYPE, CONTENT_TYPE_JSON)
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
_RESOURCE = 'https://api.instapush.im/v1/'
|
_RESOURCE = 'https://api.instapush.im/v1/'
|
||||||
|
@ -76,7 +76,7 @@ class InstapushNotificationService(BaseNotificationService):
|
||||||
self._headers = {
|
self._headers = {
|
||||||
HTTP_HEADER_APPID: self._api_key,
|
HTTP_HEADER_APPID: self._api_key,
|
||||||
HTTP_HEADER_APPSECRET: self._app_secret,
|
HTTP_HEADER_APPSECRET: self._app_secret,
|
||||||
HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_JSON,
|
CONTENT_TYPE: CONTENT_TYPE_JSON,
|
||||||
}
|
}
|
||||||
|
|
||||||
def send_message(self, message="", **kwargs):
|
def send_message(self, message="", **kwargs):
|
||||||
|
|
|
@ -10,7 +10,8 @@ import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.notify import (
|
from homeassistant.components.notify import (
|
||||||
ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService)
|
ATTR_TITLE, ATTR_TITLE_DEFAULT, PLATFORM_SCHEMA, BaseNotificationService)
|
||||||
from homeassistant.const import (CONF_API_KEY, CONF_SENDER, CONF_RECIPIENT)
|
from homeassistant.const import (
|
||||||
|
CONF_API_KEY, CONF_SENDER, CONF_RECIPIENT, CONTENT_TYPE_TEXT_PLAIN)
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
REQUIREMENTS = ['sendgrid==5.3.0']
|
REQUIREMENTS = ['sendgrid==5.3.0']
|
||||||
|
@ -67,7 +68,7 @@ class SendgridNotificationService(BaseNotificationService):
|
||||||
},
|
},
|
||||||
"content": [
|
"content": [
|
||||||
{
|
{
|
||||||
"type": "text/plain",
|
"type": CONTENT_TYPE_TEXT_PLAIN,
|
||||||
"value": message
|
"value": message
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,12 +6,13 @@ https://home-assistant.io/components/notify.telstra/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from aiohttp.hdrs import CONTENT_TYPE, AUTHORIZATION
|
||||||
import requests
|
import requests
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.notify import (
|
from homeassistant.components.notify import (
|
||||||
BaseNotificationService, ATTR_TITLE, PLATFORM_SCHEMA)
|
ATTR_TITLE, PLATFORM_SCHEMA, BaseNotificationService)
|
||||||
from homeassistant.const import CONTENT_TYPE_JSON, HTTP_HEADER_CONTENT_TYPE
|
from homeassistant.const import CONTENT_TYPE_JSON
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -73,8 +74,8 @@ class TelstraNotificationService(BaseNotificationService):
|
||||||
}
|
}
|
||||||
message_resource = 'https://api.telstra.com/v1/sms/messages'
|
message_resource = 'https://api.telstra.com/v1/sms/messages'
|
||||||
message_headers = {
|
message_headers = {
|
||||||
HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_JSON,
|
CONTENT_TYPE: CONTENT_TYPE_JSON,
|
||||||
'Authorization': 'Bearer ' + token_response['access_token'],
|
AUTHORIZATION: 'Bearer {}'.format(token_response['access_token']),
|
||||||
}
|
}
|
||||||
message_response = requests.post(
|
message_response = requests.post(
|
||||||
message_resource, headers=message_headers, json=message_data,
|
message_resource, headers=message_headers, json=message_data,
|
||||||
|
|
|
@ -9,6 +9,7 @@ import time
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
from aiohttp.hdrs import CONTENT_TYPE
|
||||||
|
|
||||||
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONTENT_TYPE_JSON
|
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONTENT_TYPE_JSON
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
@ -55,8 +56,10 @@ class OctoPrintAPI(object):
|
||||||
def __init__(self, api_url, key, bed, number_of_tools):
|
def __init__(self, api_url, key, bed, number_of_tools):
|
||||||
"""Initialize OctoPrint API and set headers needed later."""
|
"""Initialize OctoPrint API and set headers needed later."""
|
||||||
self.api_url = api_url
|
self.api_url = api_url
|
||||||
self.headers = {'content-type': CONTENT_TYPE_JSON,
|
self.headers = {
|
||||||
'X-Api-Key': key}
|
CONTENT_TYPE: CONTENT_TYPE_JSON,
|
||||||
|
'X-Api-Key': key,
|
||||||
|
}
|
||||||
self.printer_last_reading = [{}, None]
|
self.printer_last_reading = [{}, None]
|
||||||
self.job_last_reading = [{}, None]
|
self.job_last_reading = [{}, None]
|
||||||
self.job_available = False
|
self.job_available = False
|
||||||
|
|
|
@ -7,15 +7,15 @@ https://home-assistant.io/components/scene.lifx_cloud/
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
|
from aiohttp.hdrs import AUTHORIZATION
|
||||||
|
import async_timeout
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
import async_timeout
|
|
||||||
|
|
||||||
from homeassistant.components.scene import Scene
|
from homeassistant.components.scene import Scene
|
||||||
from homeassistant.const import (CONF_PLATFORM, CONF_TOKEN, CONF_TIMEOUT)
|
from homeassistant.const import CONF_TOKEN, CONF_TIMEOUT, CONF_PLATFORM
|
||||||
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.aiohttp_client import (async_get_clientsession)
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||||
timeout = config.get(CONF_TIMEOUT)
|
timeout = config.get(CONF_TIMEOUT)
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
"Authorization": "Bearer %s" % token,
|
AUTHORIZATION: "Bearer {}".format(token),
|
||||||
}
|
}
|
||||||
|
|
||||||
url = LIFX_API_URL.format('scenes')
|
url = LIFX_API_URL.format('scenes')
|
||||||
|
|
|
@ -6,8 +6,8 @@ https://home-assistant.io/components/scene.lutron_caseta/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.components.scene import Scene
|
|
||||||
from homeassistant.components.lutron_caseta import LUTRON_CASETA_SMARTBRIDGE
|
from homeassistant.components.lutron_caseta import LUTRON_CASETA_SMARTBRIDGE
|
||||||
|
from homeassistant.components.scene import Scene
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
@ -7,24 +7,28 @@ https://home-assistant.io/components/sensor.haveibeenpwned/
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import voluptuous as vol
|
from aiohttp.hdrs import USER_AGENT
|
||||||
import requests
|
import requests
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||||
from homeassistant.const import (STATE_UNKNOWN, CONF_EMAIL)
|
from homeassistant.const import CONF_EMAIL
|
||||||
from homeassistant.helpers.entity import Entity
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.helpers.entity import Entity
|
||||||
|
from homeassistant.helpers.event import track_point_in_time
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
from homeassistant.helpers.event import track_point_in_time
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DATE_STR_FORMAT = "%Y-%m-%d %H:%M:%S"
|
DATE_STR_FORMAT = "%Y-%m-%d %H:%M:%S"
|
||||||
USER_AGENT = "Home Assistant HaveIBeenPwned Sensor Component"
|
|
||||||
|
|
||||||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15)
|
HA_USER_AGENT = "Home Assistant HaveIBeenPwned Sensor Component"
|
||||||
|
|
||||||
MIN_TIME_BETWEEN_FORCED_UPDATES = timedelta(seconds=5)
|
MIN_TIME_BETWEEN_FORCED_UPDATES = timedelta(seconds=5)
|
||||||
|
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15)
|
||||||
|
|
||||||
|
URL = 'https://haveibeenpwned.com/api/v2/breachedaccount/'
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
vol.Required(CONF_EMAIL): vol.All(cv.ensure_list, [cv.string]),
|
vol.Required(CONF_EMAIL): vol.All(cv.ensure_list, [cv.string]),
|
||||||
|
@ -33,7 +37,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
"""Set up the HaveIBeenPwnedSensor sensor."""
|
"""Set up the HaveIBeenPwned sensor."""
|
||||||
emails = config.get(CONF_EMAIL)
|
emails = config.get(CONF_EMAIL)
|
||||||
data = HaveIBeenPwnedData(emails)
|
data = HaveIBeenPwnedData(emails)
|
||||||
|
|
||||||
|
@ -50,11 +54,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
|
||||||
|
|
||||||
class HaveIBeenPwnedSensor(Entity):
|
class HaveIBeenPwnedSensor(Entity):
|
||||||
"""Implementation of a HaveIBeenPwnedSensor."""
|
"""Implementation of a HaveIBeenPwned sensor."""
|
||||||
|
|
||||||
def __init__(self, data, hass, email):
|
def __init__(self, data, hass, email):
|
||||||
"""Initialize the HaveIBeenPwnedSensor sensor."""
|
"""Initialize the HaveIBeenPwned sensor."""
|
||||||
self._state = STATE_UNKNOWN
|
self._state = None
|
||||||
self._data = data
|
self._data = data
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self._email = email
|
self._email = email
|
||||||
|
@ -77,7 +81,7 @@ class HaveIBeenPwnedSensor(Entity):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
"""Return the atrributes of the sensor."""
|
"""Return the attributes of the sensor."""
|
||||||
val = {}
|
val = {}
|
||||||
if self._email not in self._data.data:
|
if self._email not in self._data.data:
|
||||||
return val
|
return val
|
||||||
|
@ -143,17 +147,16 @@ class HaveIBeenPwnedData(object):
|
||||||
def update(self, **kwargs):
|
def update(self, **kwargs):
|
||||||
"""Get the latest data for current email from REST service."""
|
"""Get the latest data for current email from REST service."""
|
||||||
try:
|
try:
|
||||||
url = "https://haveibeenpwned.com/api/v2/breachedaccount/{}". \
|
url = "{}{}".format(URL, self._email)
|
||||||
format(self._email)
|
|
||||||
|
|
||||||
_LOGGER.info("Checking for breaches for email %s", self._email)
|
_LOGGER.debug("Checking for breaches for email: %s", self._email)
|
||||||
|
|
||||||
req = requests.get(url, headers={"User-agent": USER_AGENT},
|
req = requests.get(
|
||||||
allow_redirects=True, timeout=5)
|
url, headers={USER_AGENT: HA_USER_AGENT}, allow_redirects=True,
|
||||||
|
timeout=5)
|
||||||
|
|
||||||
except requests.exceptions.RequestException:
|
except requests.exceptions.RequestException:
|
||||||
_LOGGER.error("Failed fetching HaveIBeenPwned Data for %s",
|
_LOGGER.error("Failed fetching data for %s", self._email)
|
||||||
self._email)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if req.status_code == 200:
|
if req.status_code == 200:
|
||||||
|
@ -161,7 +164,7 @@ class HaveIBeenPwnedData(object):
|
||||||
key=lambda k: k["AddedDate"],
|
key=lambda k: k["AddedDate"],
|
||||||
reverse=True)
|
reverse=True)
|
||||||
|
|
||||||
# only goto next email if we had data so that
|
# Only goto next email if we had data so that
|
||||||
# the forced updates try this current email again
|
# the forced updates try this current email again
|
||||||
self.set_next_email()
|
self.set_next_email()
|
||||||
|
|
||||||
|
@ -173,6 +176,6 @@ class HaveIBeenPwnedData(object):
|
||||||
self.set_next_email()
|
self.set_next_email()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
_LOGGER.error("Failed fetching HaveIBeenPwned Data for %s"
|
_LOGGER.error("Failed fetching data for %s"
|
||||||
"(HTTP Status_code = %d)", self._email,
|
"(HTTP Status_code = %d)", self._email,
|
||||||
req.status_code)
|
req.status_code)
|
||||||
|
|
|
@ -4,19 +4,20 @@ Support for monitoring NZBGet NZB client.
|
||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/sensor.nzbget/
|
https://home-assistant.io/components/sensor.nzbget/
|
||||||
"""
|
"""
|
||||||
import logging
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from aiohttp.hdrs import CONTENT_TYPE
|
||||||
import requests
|
import requests
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_HOST, CONF_PASSWORD, CONF_USERNAME, CONF_NAME, CONF_PORT,
|
CONF_SSL, CONF_HOST, CONF_NAME, CONF_PORT, CONF_PASSWORD, CONF_USERNAME,
|
||||||
CONF_SSL, CONTENT_TYPE_JSON, CONF_MONITORED_VARIABLES)
|
CONTENT_TYPE_JSON, CONF_MONITORED_VARIABLES)
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -145,7 +146,7 @@ class NZBGetAPI(object):
|
||||||
"""Initialize NZBGet API and set headers needed later."""
|
"""Initialize NZBGet API and set headers needed later."""
|
||||||
self.api_url = api_url
|
self.api_url = api_url
|
||||||
self.status = None
|
self.status = None
|
||||||
self.headers = {'content-type': CONTENT_TYPE_JSON}
|
self.headers = {CONTENT_TYPE: CONTENT_TYPE_JSON}
|
||||||
|
|
||||||
if username is not None and password is not None:
|
if username is not None and password is not None:
|
||||||
self.auth = (username, password)
|
self.auth = (username, password)
|
||||||
|
@ -155,7 +156,7 @@ class NZBGetAPI(object):
|
||||||
|
|
||||||
def post(self, method, params=None):
|
def post(self, method, params=None):
|
||||||
"""Send a POST request and return the response as a dict."""
|
"""Send a POST request and return the response as a dict."""
|
||||||
payload = {"method": method}
|
payload = {'method': method}
|
||||||
|
|
||||||
if params:
|
if params:
|
||||||
payload['params'] = params
|
payload['params'] = params
|
||||||
|
|
|
@ -4,18 +4,18 @@ Support for monitoring pyLoad.
|
||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/sensor.pyload/
|
https://home-assistant.io/components/sensor.pyload/
|
||||||
"""
|
"""
|
||||||
import logging
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from aiohttp.hdrs import CONTENT_TYPE
|
||||||
import requests
|
import requests
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_HOST, CONF_PASSWORD, CONF_USERNAME, CONF_NAME, CONF_PORT,
|
CONF_SSL, CONF_HOST, CONF_NAME, CONF_PORT, CONF_PASSWORD, CONF_USERNAME,
|
||||||
CONF_SSL, HTTP_HEADER_CONTENT_TYPE, CONTENT_TYPE_JSON,
|
CONTENT_TYPE_JSON, CONF_MONITORED_VARIABLES)
|
||||||
CONF_MONITORED_VARIABLES)
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ class PyLoadAPI(object):
|
||||||
"""Initialize pyLoad API and set headers needed later."""
|
"""Initialize pyLoad API and set headers needed later."""
|
||||||
self.api_url = api_url
|
self.api_url = api_url
|
||||||
self.status = None
|
self.status = None
|
||||||
self.headers = {HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_JSON}
|
self.headers = {CONTENT_TYPE: CONTENT_TYPE_JSON}
|
||||||
|
|
||||||
if username is not None and password is not None:
|
if username is not None and password is not None:
|
||||||
self.payload = {'username': username, 'password': password}
|
self.payload = {'username': username, 'password': password}
|
||||||
|
|
|
@ -8,15 +8,16 @@ import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
from aiohttp.hdrs import ACCEPT, AUTHORIZATION
|
||||||
import async_timeout
|
import async_timeout
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||||
from homeassistant.components.thethingsnetwork import (
|
from homeassistant.components.thethingsnetwork import (
|
||||||
DATA_TTN, TTN_APP_ID, TTN_ACCESS_KEY, TTN_DATA_STORAGE_URL)
|
DATA_TTN, TTN_APP_ID, TTN_ACCESS_KEY, TTN_DATA_STORAGE_URL)
|
||||||
from homeassistant.const import CONTENT_TYPE_JSON
|
from homeassistant.const import CONTENT_TYPE_JSON
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -122,8 +123,8 @@ class TtnDataStorage(object):
|
||||||
self._url = TTN_DATA_STORAGE_URL.format(
|
self._url = TTN_DATA_STORAGE_URL.format(
|
||||||
app_id=app_id, endpoint='api/v2/query', device_id=device_id)
|
app_id=app_id, endpoint='api/v2/query', device_id=device_id)
|
||||||
self._headers = {
|
self._headers = {
|
||||||
'Accept': CONTENT_TYPE_JSON,
|
ACCEPT: CONTENT_TYPE_JSON,
|
||||||
'Authorization': 'key {}'.format(access_key),
|
AUTHORIZATION: 'key {}'.format(access_key),
|
||||||
}
|
}
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
|
|
@ -5,24 +5,25 @@ For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/sensor.zamg/
|
https://home-assistant.io/components/sensor.zamg/
|
||||||
"""
|
"""
|
||||||
import csv
|
import csv
|
||||||
|
from datetime import datetime, timedelta
|
||||||
import gzip
|
import gzip
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from datetime import datetime, timedelta
|
|
||||||
|
|
||||||
|
from aiohttp.hdrs import USER_AGENT
|
||||||
import pytz
|
import pytz
|
||||||
import requests
|
import requests
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.components.weather import (
|
from homeassistant.components.weather import (
|
||||||
ATTR_WEATHER_HUMIDITY, ATTR_WEATHER_ATTRIBUTION, ATTR_WEATHER_PRESSURE,
|
ATTR_WEATHER_HUMIDITY, ATTR_WEATHER_PRESSURE, ATTR_WEATHER_WIND_SPEED,
|
||||||
ATTR_WEATHER_TEMPERATURE, ATTR_WEATHER_WIND_BEARING,
|
ATTR_WEATHER_ATTRIBUTION, ATTR_WEATHER_TEMPERATURE,
|
||||||
ATTR_WEATHER_WIND_SPEED)
|
ATTR_WEATHER_WIND_BEARING)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_MONITORED_CONDITIONS, CONF_NAME, __version__,
|
CONF_NAME, CONF_LATITUDE, CONF_LONGITUDE, CONF_MONITORED_CONDITIONS,
|
||||||
CONF_LATITUDE, CONF_LONGITUDE)
|
__version__)
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
|
|
||||||
|
@ -30,13 +31,12 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
ATTR_STATION = 'station'
|
ATTR_STATION = 'station'
|
||||||
ATTR_UPDATED = 'updated'
|
ATTR_UPDATED = 'updated'
|
||||||
ATTRIBUTION = 'Data provided by ZAMG'
|
ATTRIBUTION = "Data provided by ZAMG"
|
||||||
|
|
||||||
CONF_STATION_ID = 'station_id'
|
CONF_STATION_ID = 'station_id'
|
||||||
|
|
||||||
DEFAULT_NAME = 'zamg'
|
DEFAULT_NAME = 'zamg'
|
||||||
|
|
||||||
# Data source updates once per hour, so we do nothing if it's been less time
|
|
||||||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=10)
|
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=10)
|
||||||
|
|
||||||
SENSOR_TYPES = {
|
SENSOR_TYPES = {
|
||||||
|
@ -138,7 +138,7 @@ class ZamgData(object):
|
||||||
|
|
||||||
API_URL = 'http://www.zamg.ac.at/ogd/'
|
API_URL = 'http://www.zamg.ac.at/ogd/'
|
||||||
API_HEADERS = {
|
API_HEADERS = {
|
||||||
'User-Agent': '{} {}'.format('home-assistant.zamg/', __version__),
|
USER_AGENT: '{} {}'.format('home-assistant.zamg/', __version__),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, station_id):
|
def __init__(self, station_id):
|
||||||
|
@ -162,8 +162,8 @@ class ZamgData(object):
|
||||||
cls.API_URL, headers=cls.API_HEADERS, timeout=15)
|
cls.API_URL, headers=cls.API_HEADERS, timeout=15)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
response.encoding = 'UTF8'
|
response.encoding = 'UTF8'
|
||||||
return csv.DictReader(response.text.splitlines(),
|
return csv.DictReader(
|
||||||
delimiter=';', quotechar='"')
|
response.text.splitlines(), delimiter=';', quotechar='"')
|
||||||
except requests.exceptions.HTTPError:
|
except requests.exceptions.HTTPError:
|
||||||
_LOGGER.error("While fetching data")
|
_LOGGER.error("While fetching data")
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,12 @@ https://home-assistant.io/components/splunk/
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from aiohttp.hdrs import AUTHORIZATION
|
||||||
import requests
|
import requests
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_NAME, CONF_HOST, CONF_PORT, CONF_SSL, CONF_TOKEN, EVENT_STATE_CHANGED)
|
CONF_SSL, CONF_HOST, CONF_NAME, CONF_PORT, CONF_TOKEN, EVENT_STATE_CHANGED)
|
||||||
from homeassistant.helpers import state as state_helper
|
from homeassistant.helpers import state as state_helper
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.remote import JSONEncoder
|
from homeassistant.remote import JSONEncoder
|
||||||
|
@ -52,7 +53,7 @@ def setup(hass, config):
|
||||||
|
|
||||||
event_collector = '{}{}:{}/services/collector/event'.format(
|
event_collector = '{}{}:{}/services/collector/event'.format(
|
||||||
uri_scheme, host, port)
|
uri_scheme, host, port)
|
||||||
headers = {'Authorization': 'Splunk {}'.format(token)}
|
headers = {AUTHORIZATION: 'Splunk {}'.format(token)}
|
||||||
|
|
||||||
def splunk_event_listener(event):
|
def splunk_event_listener(event):
|
||||||
"""Listen for new messages on the bus and sends them to Splunk."""
|
"""Listen for new messages on the bus and sends them to Splunk."""
|
||||||
|
|
|
@ -10,6 +10,7 @@ import logging
|
||||||
|
|
||||||
import async_timeout
|
import async_timeout
|
||||||
from aiohttp.client_exceptions import ClientError
|
from aiohttp.client_exceptions import ClientError
|
||||||
|
from aiohttp.hdrs import CONNECTION, KEEP_ALIVE
|
||||||
|
|
||||||
from homeassistant.components.telegram_bot import (
|
from homeassistant.components.telegram_bot import (
|
||||||
CONF_ALLOWED_CHAT_IDS, BaseTelegramBotEntity,
|
CONF_ALLOWED_CHAT_IDS, BaseTelegramBotEntity,
|
||||||
|
@ -41,20 +42,14 @@ def async_setup_platform(hass, config):
|
||||||
"""Stop the bot."""
|
"""Stop the bot."""
|
||||||
pol.stop_polling()
|
pol.stop_polling()
|
||||||
|
|
||||||
hass.bus.async_listen_once(
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, _start_bot)
|
||||||
EVENT_HOMEASSISTANT_START,
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _stop_bot)
|
||||||
_start_bot
|
|
||||||
)
|
|
||||||
hass.bus.async_listen_once(
|
|
||||||
EVENT_HOMEASSISTANT_STOP,
|
|
||||||
_stop_bot
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class TelegramPoll(BaseTelegramBotEntity):
|
class TelegramPoll(BaseTelegramBotEntity):
|
||||||
"""asyncio telegram incoming message handler."""
|
"""Asyncio telegram incoming message handler."""
|
||||||
|
|
||||||
def __init__(self, bot, hass, allowed_chat_ids):
|
def __init__(self, bot, hass, allowed_chat_ids):
|
||||||
"""Initialize the polling instance."""
|
"""Initialize the polling instance."""
|
||||||
|
@ -62,9 +57,9 @@ class TelegramPoll(BaseTelegramBotEntity):
|
||||||
self.update_id = 0
|
self.update_id = 0
|
||||||
self.websession = async_get_clientsession(hass)
|
self.websession = async_get_clientsession(hass)
|
||||||
self.update_url = '{0}/getUpdates'.format(bot.base_url)
|
self.update_url = '{0}/getUpdates'.format(bot.base_url)
|
||||||
self.polling_task = None # The actuall polling task.
|
self.polling_task = None # The actual polling task.
|
||||||
self.timeout = 15 # async post timeout
|
self.timeout = 15 # async post timeout
|
||||||
# polling timeout should always be less than async post timeout.
|
# Polling timeout should always be less than async post timeout.
|
||||||
self.post_data = {'timeout': self.timeout - 5}
|
self.post_data = {'timeout': self.timeout - 5}
|
||||||
|
|
||||||
def start_polling(self):
|
def start_polling(self):
|
||||||
|
@ -87,7 +82,7 @@ class TelegramPoll(BaseTelegramBotEntity):
|
||||||
with async_timeout.timeout(self.timeout, loop=self.hass.loop):
|
with async_timeout.timeout(self.timeout, loop=self.hass.loop):
|
||||||
resp = yield from self.websession.post(
|
resp = yield from self.websession.post(
|
||||||
self.update_url, data=self.post_data,
|
self.update_url, data=self.post_data,
|
||||||
headers={'connection': 'keep-alive'}
|
headers={CONNECTION: KEEP_ALIVE}
|
||||||
)
|
)
|
||||||
if resp.status == 200:
|
if resp.status == 200:
|
||||||
_json = yield from resp.json()
|
_json = yield from resp.json()
|
||||||
|
|
|
@ -9,14 +9,15 @@ import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
from aiohttp.hdrs import REFERER, USER_AGENT
|
||||||
import async_timeout
|
import async_timeout
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
import yarl
|
import yarl
|
||||||
|
|
||||||
from homeassistant.components.tts import Provider, PLATFORM_SCHEMA, CONF_LANG
|
from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
|
||||||
REQUIREMENTS = ["gTTS-token==1.1.1"]
|
REQUIREMENTS = ['gTTS-token==1.1.1']
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -52,10 +53,10 @@ class GoogleProvider(Provider):
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self._lang = lang
|
self._lang = lang
|
||||||
self.headers = {
|
self.headers = {
|
||||||
'Referer': "http://translate.google.com/",
|
REFERER: "http://translate.google.com/",
|
||||||
'User-Agent': ("Mozilla/5.0 (Windows NT 10.0; WOW64) "
|
USER_AGENT: ("Mozilla/5.0 (Windows NT 10.0; WOW64) "
|
||||||
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||||
"Chrome/47.0.2526.106 Safari/537.36")
|
"Chrome/47.0.2526.106 Safari/537.36"),
|
||||||
}
|
}
|
||||||
self.name = 'Google'
|
self.name = 'Google'
|
||||||
|
|
||||||
|
|
|
@ -398,24 +398,7 @@ HTTP_BASIC_AUTHENTICATION = 'basic'
|
||||||
HTTP_DIGEST_AUTHENTICATION = 'digest'
|
HTTP_DIGEST_AUTHENTICATION = 'digest'
|
||||||
|
|
||||||
HTTP_HEADER_HA_AUTH = 'X-HA-access'
|
HTTP_HEADER_HA_AUTH = 'X-HA-access'
|
||||||
HTTP_HEADER_ACCEPT_ENCODING = 'Accept-Encoding'
|
|
||||||
HTTP_HEADER_AUTH = 'Authorization'
|
|
||||||
HTTP_HEADER_USER_AGENT = 'User-Agent'
|
|
||||||
HTTP_HEADER_CONTENT_TYPE = 'Content-type'
|
|
||||||
HTTP_HEADER_CONTENT_ENCODING = 'Content-Encoding'
|
|
||||||
HTTP_HEADER_VARY = 'Vary'
|
|
||||||
HTTP_HEADER_CONTENT_LENGTH = 'Content-Length'
|
|
||||||
HTTP_HEADER_CACHE_CONTROL = 'Cache-Control'
|
|
||||||
HTTP_HEADER_EXPIRES = 'Expires'
|
|
||||||
HTTP_HEADER_ORIGIN = 'Origin'
|
|
||||||
HTTP_HEADER_X_REQUESTED_WITH = 'X-Requested-With'
|
HTTP_HEADER_X_REQUESTED_WITH = 'X-Requested-With'
|
||||||
HTTP_HEADER_ACCEPT = 'Accept'
|
|
||||||
HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN = 'Access-Control-Allow-Origin'
|
|
||||||
HTTP_HEADER_ACCESS_CONTROL_ALLOW_HEADERS = 'Access-Control-Allow-Headers'
|
|
||||||
|
|
||||||
ALLOWED_CORS_HEADERS = [HTTP_HEADER_ORIGIN, HTTP_HEADER_ACCEPT,
|
|
||||||
HTTP_HEADER_X_REQUESTED_WITH, HTTP_HEADER_CONTENT_TYPE,
|
|
||||||
HTTP_HEADER_HA_AUTH]
|
|
||||||
|
|
||||||
CONTENT_TYPE_JSON = 'application/json'
|
CONTENT_TYPE_JSON = 'application/json'
|
||||||
CONTENT_TYPE_MULTIPART = 'multipart/x-mixed-replace; boundary={}'
|
CONTENT_TYPE_MULTIPART = 'multipart/x-mixed-replace; boundary={}'
|
||||||
|
|
|
@ -15,22 +15,18 @@ import urllib.parse
|
||||||
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from aiohttp.hdrs import METH_GET, METH_POST, METH_DELETE, CONTENT_TYPE
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from homeassistant import core as ha
|
from homeassistant import core as ha
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
HTTP_HEADER_HA_AUTH, SERVER_PORT, URL_API,
|
URL_API, SERVER_PORT, URL_API_CONFIG, URL_API_EVENTS, URL_API_STATES,
|
||||||
URL_API_EVENTS, URL_API_EVENTS_EVENT, URL_API_SERVICES, URL_API_CONFIG,
|
URL_API_SERVICES, CONTENT_TYPE_JSON, HTTP_HEADER_HA_AUTH,
|
||||||
URL_API_SERVICES_SERVICE, URL_API_STATES, URL_API_STATES_ENTITY,
|
URL_API_EVENTS_EVENT, URL_API_STATES_ENTITY, URL_API_SERVICES_SERVICE)
|
||||||
HTTP_HEADER_CONTENT_TYPE, CONTENT_TYPE_JSON)
|
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
METHOD_GET = 'get'
|
|
||||||
METHOD_POST = 'post'
|
|
||||||
METHOD_DELETE = 'delete'
|
|
||||||
|
|
||||||
|
|
||||||
class APIStatus(enum.Enum):
|
class APIStatus(enum.Enum):
|
||||||
"""Representation of an API status."""
|
"""Representation of an API status."""
|
||||||
|
@ -67,9 +63,7 @@ class API(object):
|
||||||
self.base_url += ':{}'.format(port)
|
self.base_url += ':{}'.format(port)
|
||||||
|
|
||||||
self.status = None
|
self.status = None
|
||||||
self._headers = {
|
self._headers = {CONTENT_TYPE: CONTENT_TYPE_JSON}
|
||||||
HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_JSON,
|
|
||||||
}
|
|
||||||
|
|
||||||
if api_password is not None:
|
if api_password is not None:
|
||||||
self._headers[HTTP_HEADER_HA_AUTH] = api_password
|
self._headers[HTTP_HEADER_HA_AUTH] = api_password
|
||||||
|
@ -89,7 +83,7 @@ class API(object):
|
||||||
url = urllib.parse.urljoin(self.base_url, path)
|
url = urllib.parse.urljoin(self.base_url, path)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if method == METHOD_GET:
|
if method == METH_GET:
|
||||||
return requests.get(
|
return requests.get(
|
||||||
url, params=data, timeout=timeout, headers=self._headers)
|
url, params=data, timeout=timeout, headers=self._headers)
|
||||||
|
|
||||||
|
@ -144,7 +138,7 @@ class JSONEncoder(json.JSONEncoder):
|
||||||
def validate_api(api):
|
def validate_api(api):
|
||||||
"""Make a call to validate API."""
|
"""Make a call to validate API."""
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_GET, URL_API)
|
req = api(METH_GET, URL_API)
|
||||||
|
|
||||||
if req.status_code == 200:
|
if req.status_code == 200:
|
||||||
return APIStatus.OK
|
return APIStatus.OK
|
||||||
|
@ -161,7 +155,7 @@ def validate_api(api):
|
||||||
def get_event_listeners(api):
|
def get_event_listeners(api):
|
||||||
"""List of events that is being listened for."""
|
"""List of events that is being listened for."""
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_GET, URL_API_EVENTS)
|
req = api(METH_GET, URL_API_EVENTS)
|
||||||
|
|
||||||
return req.json() if req.status_code == 200 else {}
|
return req.json() if req.status_code == 200 else {}
|
||||||
|
|
||||||
|
@ -175,7 +169,7 @@ def get_event_listeners(api):
|
||||||
def fire_event(api, event_type, data=None):
|
def fire_event(api, event_type, data=None):
|
||||||
"""Fire an event at remote API."""
|
"""Fire an event at remote API."""
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_POST, URL_API_EVENTS_EVENT.format(event_type), data)
|
req = api(METH_POST, URL_API_EVENTS_EVENT.format(event_type), data)
|
||||||
|
|
||||||
if req.status_code != 200:
|
if req.status_code != 200:
|
||||||
_LOGGER.error("Error firing event: %d - %s",
|
_LOGGER.error("Error firing event: %d - %s",
|
||||||
|
@ -188,7 +182,7 @@ def fire_event(api, event_type, data=None):
|
||||||
def get_state(api, entity_id):
|
def get_state(api, entity_id):
|
||||||
"""Query given API for state of entity_id."""
|
"""Query given API for state of entity_id."""
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_GET, URL_API_STATES_ENTITY.format(entity_id))
|
req = api(METH_GET, URL_API_STATES_ENTITY.format(entity_id))
|
||||||
|
|
||||||
# req.status_code == 422 if entity does not exist
|
# req.status_code == 422 if entity does not exist
|
||||||
|
|
||||||
|
@ -205,7 +199,7 @@ def get_state(api, entity_id):
|
||||||
def get_states(api):
|
def get_states(api):
|
||||||
"""Query given API for all states."""
|
"""Query given API for all states."""
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_GET,
|
req = api(METH_GET,
|
||||||
URL_API_STATES)
|
URL_API_STATES)
|
||||||
|
|
||||||
return [ha.State.from_dict(item) for
|
return [ha.State.from_dict(item) for
|
||||||
|
@ -224,7 +218,7 @@ def remove_state(api, entity_id):
|
||||||
Return True if entity is gone (removed/never existed).
|
Return True if entity is gone (removed/never existed).
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_DELETE, URL_API_STATES_ENTITY.format(entity_id))
|
req = api(METH_DELETE, URL_API_STATES_ENTITY.format(entity_id))
|
||||||
|
|
||||||
if req.status_code in (200, 404):
|
if req.status_code in (200, 404):
|
||||||
return True
|
return True
|
||||||
|
@ -250,9 +244,7 @@ def set_state(api, entity_id, new_state, attributes=None, force_update=False):
|
||||||
'force_update': force_update}
|
'force_update': force_update}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_POST,
|
req = api(METH_POST, URL_API_STATES_ENTITY.format(entity_id), data)
|
||||||
URL_API_STATES_ENTITY.format(entity_id),
|
|
||||||
data)
|
|
||||||
|
|
||||||
if req.status_code not in (200, 201):
|
if req.status_code not in (200, 201):
|
||||||
_LOGGER.error("Error changing state: %d - %s",
|
_LOGGER.error("Error changing state: %d - %s",
|
||||||
|
@ -280,7 +272,7 @@ def get_services(api):
|
||||||
Each dict has a string "domain" and a list of strings "services".
|
Each dict has a string "domain" and a list of strings "services".
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_GET, URL_API_SERVICES)
|
req = api(METH_GET, URL_API_SERVICES)
|
||||||
|
|
||||||
return req.json() if req.status_code == 200 else {}
|
return req.json() if req.status_code == 200 else {}
|
||||||
|
|
||||||
|
@ -294,7 +286,7 @@ def get_services(api):
|
||||||
def call_service(api, domain, service, service_data=None, timeout=5):
|
def call_service(api, domain, service, service_data=None, timeout=5):
|
||||||
"""Call a service at the remote API."""
|
"""Call a service at the remote API."""
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_POST,
|
req = api(METH_POST,
|
||||||
URL_API_SERVICES_SERVICE.format(domain, service),
|
URL_API_SERVICES_SERVICE.format(domain, service),
|
||||||
service_data, timeout=timeout)
|
service_data, timeout=timeout)
|
||||||
|
|
||||||
|
@ -309,7 +301,7 @@ def call_service(api, domain, service, service_data=None, timeout=5):
|
||||||
def get_config(api):
|
def get_config(api):
|
||||||
"""Return configuration."""
|
"""Return configuration."""
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_GET, URL_API_CONFIG)
|
req = api(METH_GET, URL_API_CONFIG)
|
||||||
|
|
||||||
if req.status_code != 200:
|
if req.status_code != 200:
|
||||||
return {}
|
return {}
|
||||||
|
|
|
@ -1,33 +1,32 @@
|
||||||
"""The tests for the emulated Hue component."""
|
"""The tests for the emulated Hue component."""
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant import setup, const, core
|
from aiohttp.hdrs import CONTENT_TYPE
|
||||||
|
import pytest
|
||||||
|
from tests.common import get_test_instance_port
|
||||||
|
|
||||||
|
from homeassistant import core, const, setup
|
||||||
import homeassistant.components as core_components
|
import homeassistant.components as core_components
|
||||||
from homeassistant.components import (
|
from homeassistant.components import (
|
||||||
emulated_hue, http, light, script, media_player, fan
|
fan, http, light, script, emulated_hue, media_player)
|
||||||
)
|
|
||||||
from homeassistant.const import STATE_ON, STATE_OFF
|
|
||||||
from homeassistant.components.emulated_hue.hue_api import (
|
|
||||||
HUE_API_STATE_ON, HUE_API_STATE_BRI, HueUsernameView,
|
|
||||||
HueAllLightsStateView, HueOneLightStateView, HueOneLightChangeView)
|
|
||||||
from homeassistant.components.emulated_hue import Config
|
from homeassistant.components.emulated_hue import Config
|
||||||
|
from homeassistant.components.emulated_hue.hue_api import (
|
||||||
from tests.common import get_test_instance_port
|
HUE_API_STATE_ON, HUE_API_STATE_BRI, HueUsernameView, HueOneLightStateView,
|
||||||
|
HueAllLightsStateView, HueOneLightChangeView)
|
||||||
|
from homeassistant.const import STATE_ON, STATE_OFF
|
||||||
|
|
||||||
HTTP_SERVER_PORT = get_test_instance_port()
|
HTTP_SERVER_PORT = get_test_instance_port()
|
||||||
BRIDGE_SERVER_PORT = get_test_instance_port()
|
BRIDGE_SERVER_PORT = get_test_instance_port()
|
||||||
|
|
||||||
BRIDGE_URL_BASE = 'http://127.0.0.1:{}'.format(BRIDGE_SERVER_PORT) + '{}'
|
BRIDGE_URL_BASE = 'http://127.0.0.1:{}'.format(BRIDGE_SERVER_PORT) + '{}'
|
||||||
JSON_HEADERS = {const.HTTP_HEADER_CONTENT_TYPE: const.CONTENT_TYPE_JSON}
|
JSON_HEADERS = {CONTENT_TYPE: const.CONTENT_TYPE_JSON}
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def hass_hue(loop, hass):
|
def hass_hue(loop, hass):
|
||||||
"""Setup a hass instance for these tests."""
|
"""Setup a Home Assistant instance for these tests."""
|
||||||
# We need to do this to get access to homeassistant/turn_(on,off)
|
# We need to do this to get access to homeassistant/turn_(on,off)
|
||||||
loop.run_until_complete(
|
loop.run_until_complete(
|
||||||
core_components.async_setup(hass, {core.DOMAIN: {}}))
|
core_components.async_setup(hass, {core.DOMAIN: {}}))
|
||||||
|
|
|
@ -4,6 +4,7 @@ import json
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
import requests
|
import requests
|
||||||
|
from aiohttp.hdrs import CONTENT_TYPE
|
||||||
|
|
||||||
from homeassistant import setup, const, core
|
from homeassistant import setup, const, core
|
||||||
import homeassistant.components as core_components
|
import homeassistant.components as core_components
|
||||||
|
@ -16,7 +17,7 @@ HTTP_SERVER_PORT = get_test_instance_port()
|
||||||
BRIDGE_SERVER_PORT = get_test_instance_port()
|
BRIDGE_SERVER_PORT = get_test_instance_port()
|
||||||
|
|
||||||
BRIDGE_URL_BASE = 'http://127.0.0.1:{}'.format(BRIDGE_SERVER_PORT) + '{}'
|
BRIDGE_URL_BASE = 'http://127.0.0.1:{}'.format(BRIDGE_SERVER_PORT) + '{}'
|
||||||
JSON_HEADERS = {const.HTTP_HEADER_CONTENT_TYPE: const.CONTENT_TYPE_JSON}
|
JSON_HEADERS = {CONTENT_TYPE: const.CONTENT_TYPE_JSON}
|
||||||
|
|
||||||
|
|
||||||
def setup_hass_instance(emulated_hue_config):
|
def setup_hass_instance(emulated_hue_config):
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
"""The tests for the Google Actions component."""
|
"""The tests for the Google Assistant component."""
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
import json
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import pytest
|
import json
|
||||||
|
|
||||||
from homeassistant import setup, const, core
|
from aiohttp.hdrs import CONTENT_TYPE, AUTHORIZATION
|
||||||
from homeassistant.components import (
|
import pytest
|
||||||
http, async_setup, light, cover, media_player, fan, switch, climate
|
|
||||||
)
|
|
||||||
from homeassistant.components import google_assistant as ga
|
|
||||||
from tests.common import get_test_instance_port
|
from tests.common import get_test_instance_port
|
||||||
|
|
||||||
from . import DEMO_DEVICES
|
from homeassistant import core, const, setup
|
||||||
|
from homeassistant.components import (
|
||||||
|
fan, http, cover, light, switch, climate, async_setup, media_player)
|
||||||
|
from homeassistant.components import google_assistant as ga
|
||||||
|
|
||||||
|
from . import DEMO_DEVICES
|
||||||
|
|
||||||
API_PASSWORD = "test1234"
|
API_PASSWORD = "test1234"
|
||||||
SERVER_PORT = get_test_instance_port()
|
SERVER_PORT = get_test_instance_port()
|
||||||
|
@ -20,7 +20,7 @@ BASE_API_URL = "http://127.0.0.1:{}".format(SERVER_PORT)
|
||||||
|
|
||||||
HA_HEADERS = {
|
HA_HEADERS = {
|
||||||
const.HTTP_HEADER_HA_AUTH: API_PASSWORD,
|
const.HTTP_HEADER_HA_AUTH: API_PASSWORD,
|
||||||
const.HTTP_HEADER_CONTENT_TYPE: const.CONTENT_TYPE_JSON,
|
CONTENT_TYPE: const.CONTENT_TYPE_JSON,
|
||||||
}
|
}
|
||||||
|
|
||||||
AUTHCFG = {
|
AUTHCFG = {
|
||||||
|
@ -28,12 +28,12 @@ AUTHCFG = {
|
||||||
'client_id': 'helloworld',
|
'client_id': 'helloworld',
|
||||||
'access_token': 'superdoublesecret'
|
'access_token': 'superdoublesecret'
|
||||||
}
|
}
|
||||||
AUTH_HEADER = {'Authorization': 'Bearer {}'.format(AUTHCFG['access_token'])}
|
AUTH_HEADER = {AUTHORIZATION: 'Bearer {}'.format(AUTHCFG['access_token'])}
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def assistant_client(loop, hass_fixture, test_client):
|
def assistant_client(loop, hass_fixture, test_client):
|
||||||
"""Create web client for emulated hue api."""
|
"""Create web client for the Google Assistant API."""
|
||||||
hass = hass_fixture
|
hass = hass_fixture
|
||||||
web_app = hass.http.app
|
web_app = hass.http.app
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ def assistant_client(loop, hass_fixture, test_client):
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def hass_fixture(loop, hass):
|
def hass_fixture(loop, hass):
|
||||||
"""Set up a hass instance for these tests."""
|
"""Set up a HOme Assistant instance for these tests."""
|
||||||
# We need to do this to get access to homeassistant/turn_(on,off)
|
# We need to do this to get access to homeassistant/turn_(on,off)
|
||||||
loop.run_until_complete(async_setup(hass, {core.DOMAIN: {}}))
|
loop.run_until_complete(async_setup(hass, {core.DOMAIN: {}}))
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
"""The tests for the Home Assistant HTTP component."""
|
"""The tests for the Home Assistant HTTP component."""
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
|
from aiohttp.hdrs import (
|
||||||
|
ORIGIN, ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_ALLOW_HEADERS,
|
||||||
|
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS,
|
||||||
|
CONTENT_TYPE)
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from homeassistant import setup, const
|
|
||||||
import homeassistant.components.http as http
|
|
||||||
|
|
||||||
from tests.common import get_test_instance_port, get_test_home_assistant
|
from tests.common import get_test_instance_port, get_test_home_assistant
|
||||||
|
|
||||||
|
from homeassistant import const, setup
|
||||||
|
import homeassistant.components.http as http
|
||||||
|
|
||||||
API_PASSWORD = 'test1234'
|
API_PASSWORD = 'test1234'
|
||||||
SERVER_PORT = get_test_instance_port()
|
SERVER_PORT = get_test_instance_port()
|
||||||
HTTP_BASE = '127.0.0.1:{}'.format(SERVER_PORT)
|
HTTP_BASE = '127.0.0.1:{}'.format(SERVER_PORT)
|
||||||
HTTP_BASE_URL = 'http://{}'.format(HTTP_BASE)
|
HTTP_BASE_URL = 'http://{}'.format(HTTP_BASE)
|
||||||
HA_HEADERS = {
|
HA_HEADERS = {
|
||||||
const.HTTP_HEADER_HA_AUTH: API_PASSWORD,
|
const.HTTP_HEADER_HA_AUTH: API_PASSWORD,
|
||||||
const.HTTP_HEADER_CONTENT_TYPE: const.CONTENT_TYPE_JSON,
|
CONTENT_TYPE: const.CONTENT_TYPE_JSON,
|
||||||
}
|
}
|
||||||
CORS_ORIGINS = [HTTP_BASE_URL, HTTP_BASE]
|
CORS_ORIGINS = [HTTP_BASE_URL, HTTP_BASE]
|
||||||
|
|
||||||
|
@ -64,9 +68,9 @@ class TestCors:
|
||||||
"""Test cross origin resource sharing with password in url."""
|
"""Test cross origin resource sharing with password in url."""
|
||||||
req = requests.get(_url(const.URL_API),
|
req = requests.get(_url(const.URL_API),
|
||||||
params={'api_password': API_PASSWORD},
|
params={'api_password': API_PASSWORD},
|
||||||
headers={const.HTTP_HEADER_ORIGIN: HTTP_BASE_URL})
|
headers={ORIGIN: HTTP_BASE_URL})
|
||||||
|
|
||||||
allow_origin = const.HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN
|
allow_origin = ACCESS_CONTROL_ALLOW_ORIGIN
|
||||||
|
|
||||||
assert req.status_code == 200
|
assert req.status_code == 200
|
||||||
assert req.headers.get(allow_origin) == HTTP_BASE_URL
|
assert req.headers.get(allow_origin) == HTTP_BASE_URL
|
||||||
|
@ -75,11 +79,11 @@ class TestCors:
|
||||||
"""Test cross origin resource sharing with password in header."""
|
"""Test cross origin resource sharing with password in header."""
|
||||||
headers = {
|
headers = {
|
||||||
const.HTTP_HEADER_HA_AUTH: API_PASSWORD,
|
const.HTTP_HEADER_HA_AUTH: API_PASSWORD,
|
||||||
const.HTTP_HEADER_ORIGIN: HTTP_BASE_URL
|
ORIGIN: HTTP_BASE_URL
|
||||||
}
|
}
|
||||||
req = requests.get(_url(const.URL_API), headers=headers)
|
req = requests.get(_url(const.URL_API), headers=headers)
|
||||||
|
|
||||||
allow_origin = const.HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN
|
allow_origin = ACCESS_CONTROL_ALLOW_ORIGIN
|
||||||
|
|
||||||
assert req.status_code == 200
|
assert req.status_code == 200
|
||||||
assert req.headers.get(allow_origin) == HTTP_BASE_URL
|
assert req.headers.get(allow_origin) == HTTP_BASE_URL
|
||||||
|
@ -91,8 +95,8 @@ class TestCors:
|
||||||
}
|
}
|
||||||
req = requests.get(_url(const.URL_API), headers=headers)
|
req = requests.get(_url(const.URL_API), headers=headers)
|
||||||
|
|
||||||
allow_origin = const.HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN
|
allow_origin = ACCESS_CONTROL_ALLOW_ORIGIN
|
||||||
allow_headers = const.HTTP_HEADER_ACCESS_CONTROL_ALLOW_HEADERS
|
allow_headers = ACCESS_CONTROL_ALLOW_HEADERS
|
||||||
|
|
||||||
assert req.status_code == 200
|
assert req.status_code == 200
|
||||||
assert allow_origin not in req.headers
|
assert allow_origin not in req.headers
|
||||||
|
@ -101,14 +105,14 @@ class TestCors:
|
||||||
def test_cors_preflight_allowed(self):
|
def test_cors_preflight_allowed(self):
|
||||||
"""Test cross origin resource sharing preflight (OPTIONS) request."""
|
"""Test cross origin resource sharing preflight (OPTIONS) request."""
|
||||||
headers = {
|
headers = {
|
||||||
const.HTTP_HEADER_ORIGIN: HTTP_BASE_URL,
|
ORIGIN: HTTP_BASE_URL,
|
||||||
'Access-Control-Request-Method': 'GET',
|
ACCESS_CONTROL_REQUEST_METHOD: 'GET',
|
||||||
'Access-Control-Request-Headers': 'x-ha-access'
|
ACCESS_CONTROL_REQUEST_HEADERS: 'x-ha-access'
|
||||||
}
|
}
|
||||||
req = requests.options(_url(const.URL_API), headers=headers)
|
req = requests.options(_url(const.URL_API), headers=headers)
|
||||||
|
|
||||||
allow_origin = const.HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN
|
allow_origin = ACCESS_CONTROL_ALLOW_ORIGIN
|
||||||
allow_headers = const.HTTP_HEADER_ACCESS_CONTROL_ALLOW_HEADERS
|
allow_headers = ACCESS_CONTROL_ALLOW_HEADERS
|
||||||
|
|
||||||
assert req.status_code == 200
|
assert req.status_code == 200
|
||||||
assert req.headers.get(allow_origin) == HTTP_BASE_URL
|
assert req.headers.get(allow_origin) == HTTP_BASE_URL
|
||||||
|
@ -158,7 +162,7 @@ def test_registering_view_while_running(hass, test_client):
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_api_base_url_with_domain(hass):
|
def test_api_base_url_with_domain(hass):
|
||||||
"""Test setting api url."""
|
"""Test setting API URL."""
|
||||||
result = yield from setup.async_setup_component(hass, 'http', {
|
result = yield from setup.async_setup_component(hass, 'http', {
|
||||||
'http': {
|
'http': {
|
||||||
'base_url': 'example.com'
|
'base_url': 'example.com'
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from unittest.mock import patch, MagicMock, mock_open
|
from unittest.mock import patch, MagicMock, mock_open
|
||||||
|
from aiohttp.hdrs import AUTHORIZATION
|
||||||
|
|
||||||
from homeassistant.components.notify import html5
|
from homeassistant.components.notify import html5
|
||||||
|
|
||||||
|
@ -278,8 +279,8 @@ class TestHtml5Notify(object):
|
||||||
assert json.loads(handle.write.call_args[0][0]) == config
|
assert json.loads(handle.write.call_args[0][0]) == config
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_unregister_device_view_handle_unknown_subscription(self, loop,
|
def test_unregister_device_view_handle_unknown_subscription(
|
||||||
test_client):
|
self, loop, test_client):
|
||||||
"""Test that the HTML unregister view handles unknown subscriptions."""
|
"""Test that the HTML unregister view handles unknown subscriptions."""
|
||||||
hass = MagicMock()
|
hass = MagicMock()
|
||||||
|
|
||||||
|
@ -322,8 +323,8 @@ class TestHtml5Notify(object):
|
||||||
assert handle.write.call_count == 0
|
assert handle.write.call_count == 0
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_unregistering_device_view_handles_json_safe_error(self, loop,
|
def test_unregistering_device_view_handles_json_safe_error(
|
||||||
test_client):
|
self, loop, test_client):
|
||||||
"""Test that the HTML unregister view handles JSON write errors."""
|
"""Test that the HTML unregister view handles JSON write errors."""
|
||||||
hass = MagicMock()
|
hass = MagicMock()
|
||||||
|
|
||||||
|
@ -423,8 +424,8 @@ class TestHtml5Notify(object):
|
||||||
assert len(hass.mock_calls) == 3
|
assert len(hass.mock_calls) == 3
|
||||||
|
|
||||||
with patch('pywebpush.WebPusher') as mock_wp:
|
with patch('pywebpush.WebPusher') as mock_wp:
|
||||||
service.send_message('Hello', target=['device'],
|
service.send_message(
|
||||||
data={'icon': 'beer.png'})
|
'Hello', target=['device'], data={'icon': 'beer.png'})
|
||||||
|
|
||||||
assert len(mock_wp.mock_calls) == 3
|
assert len(mock_wp.mock_calls) == 3
|
||||||
|
|
||||||
|
@ -453,7 +454,7 @@ class TestHtml5Notify(object):
|
||||||
|
|
||||||
resp = yield from client.post(PUBLISH_URL, data=json.dumps({
|
resp = yield from client.post(PUBLISH_URL, data=json.dumps({
|
||||||
'type': 'push',
|
'type': 'push',
|
||||||
}), headers={'Authorization': bearer_token})
|
}), headers={AUTHORIZATION: bearer_token})
|
||||||
|
|
||||||
assert resp.status == 200
|
assert resp.status == 200
|
||||||
body = yield from resp.json()
|
body = yield from resp.json()
|
||||||
|
|
|
@ -4,6 +4,7 @@ import json
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
from aiohttp.hdrs import CONTENT_TYPE
|
||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant import setup, const
|
from homeassistant import setup, const
|
||||||
|
@ -18,7 +19,7 @@ INTENTS_API_URL = "{}{}".format(BASE_API_URL, dialogflow.INTENTS_API_ENDPOINT)
|
||||||
|
|
||||||
HA_HEADERS = {
|
HA_HEADERS = {
|
||||||
const.HTTP_HEADER_HA_AUTH: API_PASSWORD,
|
const.HTTP_HEADER_HA_AUTH: API_PASSWORD,
|
||||||
const.HTTP_HEADER_CONTENT_TYPE: const.CONTENT_TYPE_JSON,
|
CONTENT_TYPE: const.CONTENT_TYPE_JSON,
|
||||||
}
|
}
|
||||||
|
|
||||||
SESSION_ID = "a9b84cec-46b6-484e-8f31-f65dba03ae6d"
|
SESSION_ID = "a9b84cec-46b6-484e-8f31-f65dba03ae6d"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue