Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing the api_streams sensor #22200

Merged
merged 11 commits into from Mar 22, 2019
1 change: 0 additions & 1 deletion .coveragerc
Expand Up @@ -415,7 +415,6 @@ omit =
homeassistant/components/lifx_cloud/scene.py
homeassistant/components/scsgate/*
homeassistant/components/sense/*
homeassistant/components/api_streams/sensor.py
homeassistant/components/aftership/sensor.py
homeassistant/components/airvisual/sensor.py
homeassistant/components/alpha_vantage/sensor.py
Expand Down
90 changes: 0 additions & 90 deletions homeassistant/components/api_streams/sensor.py

This file was deleted.

4 changes: 4 additions & 0 deletions homeassistant/components/websocket_api/const.py
Expand Up @@ -20,3 +20,7 @@
# Originally, this was just asyncio.CancelledError, but issue #9546 showed
# that futures.CancelledErrors can also occur in some situations.
CANCELLATION_ERRORS = (asyncio.CancelledError, futures.CancelledError)

# Event types
SIGNAL_WEBSOCKET_CONNECTED = 'websocket_connected'
SIGNAL_WEBSOCKET_DISCONNECTED = 'websocket_disconnected'
9 changes: 8 additions & 1 deletion homeassistant/components/websocket_api/http.py
Expand Up @@ -13,7 +13,9 @@
from homeassistant.components.http import HomeAssistantView
from homeassistant.helpers.json import JSONEncoder

from .const import MAX_PENDING_MSG, CANCELLATION_ERRORS, URL, ERR_UNKNOWN_ERROR
from .const import (
Swamp-Ig marked this conversation as resolved.
Show resolved Hide resolved
MAX_PENDING_MSG, CANCELLATION_ERRORS, URL, ERR_UNKNOWN_ERROR,
SIGNAL_WEBSOCKET_CONNECTED, SIGNAL_WEBSOCKET_DISCONNECTED)
from .auth import AuthPhase, auth_required_message
from .error import Disconnect
from .messages import error_message
Expand Down Expand Up @@ -142,6 +144,8 @@ def handle_hass_stop(event):

self._logger.debug("Received %s", msg)
connection = await auth.async_handle(msg)
self.hass.helpers.dispatcher.async_dispatcher_send(
SIGNAL_WEBSOCKET_CONNECTED)

# Command phase
while not wsock.closed:
Expand Down Expand Up @@ -192,4 +196,7 @@ def handle_hass_stop(event):
else:
self._logger.warning("Disconnected: %s", disconnect_warn)

self.hass.helpers.dispatcher.async_dispatcher_send(
SIGNAL_WEBSOCKET_DISCONNECTED)

return wsock
53 changes: 53 additions & 0 deletions homeassistant/components/websocket_api/sensor.py
@@ -0,0 +1,53 @@
"""Entity to track connections to websocket API."""

from homeassistant.core import callback
from homeassistant.helpers.entity import Entity

from .const import SIGNAL_WEBSOCKET_CONNECTED, SIGNAL_WEBSOCKET_DISCONNECTED


async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None):
"""Set up the API streams platform."""
entity = APICount()

# pylint: disable=protected-access
hass.helpers.dispatcher.async_dispatcher_connect(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be done from inside the entity in async_added_to_hass.

SIGNAL_WEBSOCKET_CONNECTED, entity._increment)
hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_WEBSOCKET_DISCONNECTED, entity._decrement)

async_add_entities([entity])


class APICount(Entity):
"""Entity to represent how many people are connected to the stream API."""

def __init__(self):
"""Initialize the API count."""
self.count = 0

@property
def name(self):
"""Return name of entity."""
return "Connected clients"

@property
def state(self):
"""Return current API count."""
return self.count

@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
return "clients"

@callback
def _increment(self):
self.count += 1
self.async_schedule_update_ha_state()

@callback
def _decrement(self):
self.count -= 1
self.async_schedule_update_ha_state()
75 changes: 0 additions & 75 deletions tests/components/api_streams/test_sensor.py

This file was deleted.

50 changes: 49 additions & 1 deletion tests/components/websocket_api/test_auth.py
@@ -1,7 +1,9 @@
"""Test auth of websocket API."""
import asyncio
Swamp-Ig marked this conversation as resolved.
Show resolved Hide resolved
from unittest.mock import patch

from homeassistant.components.websocket_api.const import URL
from homeassistant.components.websocket_api.const import (
URL, SIGNAL_WEBSOCKET_CONNECTED, SIGNAL_WEBSOCKET_DISCONNECTED)
from homeassistant.components.websocket_api.auth import (
TYPE_AUTH, TYPE_AUTH_INVALID, TYPE_AUTH_OK, TYPE_AUTH_REQUIRED)

Expand All @@ -24,6 +26,28 @@ async def test_auth_via_msg(no_auth_websocket_client, legacy_auth):
assert msg['type'] == TYPE_AUTH_OK


async def test_auth_events(hass, no_auth_websocket_client, legacy_auth):
"""Test authenticating."""
connected_evt = []
hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_WEBSOCKET_CONNECTED,
lambda: connected_evt.append(1))
disconnected_evt = []
hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_WEBSOCKET_DISCONNECTED,
lambda: disconnected_evt.append(1))

await test_auth_via_msg(no_auth_websocket_client, legacy_auth)

assert len(connected_evt) == 1
assert not disconnected_evt

await no_auth_websocket_client.close()
await hass.async_block_till_done()

assert len(disconnected_evt) == 1


async def test_auth_via_msg_incorrect_pass(no_auth_websocket_client):
"""Test authenticating."""
with patch('homeassistant.components.websocket_api.auth.'
Expand All @@ -41,6 +65,30 @@ async def test_auth_via_msg_incorrect_pass(no_auth_websocket_client):
assert msg['message'] == 'Invalid access token or password'


async def test_auth_events_incorrect_pass(hass, no_auth_websocket_client):
"""Test authenticating."""
connected_evt = []
hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_WEBSOCKET_CONNECTED,
lambda: connected_evt.append(1))
disconnected_evt = []
hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_WEBSOCKET_DISCONNECTED,
lambda: disconnected_evt.append(1))

await test_auth_via_msg_incorrect_pass(no_auth_websocket_client)

await asyncio.sleep(0.1)
Swamp-Ig marked this conversation as resolved.
Show resolved Hide resolved
assert not connected_evt
assert not disconnected_evt

await no_auth_websocket_client.close()
await hass.async_block_till_done()

assert not connected_evt
assert not disconnected_evt


async def test_pre_auth_only_auth_allowed(no_auth_websocket_client):
"""Verify that before authentication, only auth messages are allowed."""
await no_auth_websocket_client.send_json({
Expand Down
30 changes: 30 additions & 0 deletions tests/components/websocket_api/test_sensor.py
@@ -0,0 +1,30 @@
"""Test cases for the API stream sensor."""

from homeassistant.bootstrap import async_setup_component

from tests.common import assert_setup_component
from .test_auth import test_auth_via_msg


async def test_websocket_api(hass, no_auth_websocket_client, legacy_auth):
"""Test API streams."""
with assert_setup_component(1):
await async_setup_component(hass, 'sensor', {
'sensor': {
'platform': 'websocket_api',
}
})

state = hass.states.get('sensor.connected_clients')
assert state.state == '0'

await test_auth_via_msg(no_auth_websocket_client, legacy_auth)

state = hass.states.get('sensor.connected_clients')
assert state.state == '1'

await no_auth_websocket_client.close()
await hass.async_block_till_done()

state = hass.states.get('sensor.connected_clients')
assert state.state == '0'