Use constants for HTTP headers (#10313)

* Use constants for HTTP headers

* Fix ordering

* Move 'no-cache' to platform
This commit is contained in:
Fabian Affolter 2017-11-04 20:04:05 +01:00 committed by Paulus Schoutsen
parent e64803e701
commit de9d19d6f4
36 changed files with 408 additions and 444 deletions

View file

@ -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)

View file

@ -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:

View file

@ -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"}}"""

View file

@ -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:

View file

@ -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())

View file

@ -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)

View file

@ -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'

View file

@ -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)

View file

@ -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

View file

@ -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:

View file

@ -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,

View file

@ -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'

View file

@ -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()

View file

@ -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")

View file

@ -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):

View file

@ -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
} }
] ]

View file

@ -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,

View file

@ -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

View file

@ -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')

View file

@ -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__)

View file

@ -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)

View file

@ -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

View file

@ -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}

View file

@ -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

View file

@ -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")

View file

@ -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."""

View file

@ -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()

View file

@ -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'

View file

@ -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={}'

View file

@ -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 {}

View file

@ -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: {}}))

View file

@ -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):

View file

@ -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: {}}))

View file

@ -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'

View file

@ -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()

View file

@ -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"