fix notes

This commit is contained in:
escoand
2019-11-18 22:22:19 +01:00
parent a642c7b6d5
commit 076f44fe8b
5 changed files with 100 additions and 171 deletions

View File

@@ -1,7 +1,15 @@
"""The Samsung TV integration."""
from wakeonlan import BROADCAST_IP
import voluptuous as vol
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME, CONF_PORT, CONF_TIMEOUT
from homeassistant.const import (
CONF_BROADCAST_ADDRESS,
CONF_HOST,
CONF_MAC,
CONF_NAME,
CONF_PORT,
CONF_TIMEOUT,
)
import homeassistant.helpers.config_validation as cv
from .const import DEFAULT_NAME, DEFAULT_TIMEOUT, DOMAIN
@@ -20,6 +28,9 @@ CONFIG_SCHEMA = vol.Schema(
vol.Optional(
CONF_TIMEOUT, default=DEFAULT_TIMEOUT
): cv.positive_int,
vol.Optional(
CONF_BROADCAST_ADDRESS, default=BROADCAST_IP
): cv.string,
}
)
]

View File

@@ -7,6 +7,7 @@ import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import (
CONF_BROADCAST_ADDRESS,
CONF_HOST,
CONF_ID,
CONF_IP_ADDRESS,
@@ -87,6 +88,7 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
def __init__(self):
"""Initialize flow."""
self._broadcast = None
self._host = None
self._ip = None
self._mac = None
@@ -102,6 +104,7 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
return self.async_create_entry(
title=self._title,
data={
CONF_BROADCAST_ADDRESS: self._broadcast,
CONF_HOST: self._host,
CONF_ID: self._uuid,
CONF_IP_ADDRESS: self._ip,
@@ -124,6 +127,7 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
if _is_already_configured(self.hass, ip_address):
return self.async_abort(reason="already_configured")
self._broadcast = user_input.get(CONF_BROADCAST_ADDRESS)
self._host = user_input.get(CONF_HOST)
self._ip = self.context[CONF_IP_ADDRESS] = ip_address
self._mac = user_input.get(CONF_MAC)

View File

@@ -1,17 +1,12 @@
"""Support for interface with an Samsung TV."""
import asyncio
from datetime import timedelta
import socket
from samsungctl import exceptions as samsung_exceptions, Remote as SamsungRemote
import voluptuous as vol
import wakeonlan
from homeassistant.components.media_player import (
MediaPlayerDevice,
PLATFORM_SCHEMA,
DEVICE_CLASS_TV,
)
from homeassistant.components.media_player import MediaPlayerDevice, DEVICE_CLASS_TV
from homeassistant.components.media_player.const import (
MEDIA_TYPE_CHANNEL,
SUPPORT_NEXT_TRACK,
@@ -30,7 +25,6 @@ from homeassistant.const import (
CONF_HOST,
CONF_ID,
CONF_MAC,
CONF_NAME,
CONF_PORT,
CONF_TIMEOUT,
STATE_OFF,
@@ -39,19 +33,10 @@ from homeassistant.const import (
import homeassistant.helpers.config_validation as cv
from homeassistant.util import dt as dt_util
from .const import (
CONF_MANUFACTURER,
CONF_MODEL,
DEFAULT_NAME,
DEFAULT_TIMEOUT,
DOMAIN,
LOGGER,
METHODS,
)
from .const import CONF_MANUFACTURER, CONF_MODEL, DOMAIN, LOGGER, METHODS
DEFAULT_BROADCAST_ADDRESS = "255.255.255.255"
KEY_PRESS_TIMEOUT = 1.2
KNOWN_DEVICES_KEY = "samsungtv_known_devices"
SOURCES = {"TV": "KEY_TV", "HDMI": "KEY_HDMI"}
SUPPORT_SAMSUNGTV = (
@@ -66,67 +51,17 @@ SUPPORT_SAMSUNGTV = (
| SUPPORT_PLAY_MEDIA
)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PORT): cv.port,
vol.Optional(CONF_MAC): cv.string,
vol.Optional(
CONF_BROADCAST_ADDRESS, default=DEFAULT_BROADCAST_ADDRESS
): cv.string,
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
}
)
def setup_platform(hass, config, add_entities, discovery_info=None):
async def async_setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Samsung TV platform."""
known_devices = hass.data.get(KNOWN_DEVICES_KEY)
if known_devices is None:
known_devices = set()
hass.data[KNOWN_DEVICES_KEY] = known_devices
uuid = None
# Is this a manual configuration?
if config.get(CONF_HOST) is not None:
host = config.get(CONF_HOST)
port = config.get(CONF_PORT)
name = config.get(CONF_NAME)
mac = config.get(CONF_MAC)
broadcast = config.get(CONF_BROADCAST_ADDRESS)
timeout = config.get(CONF_TIMEOUT)
elif discovery_info is not None:
tv_name = discovery_info.get("name")
model = discovery_info.get("model_name")
host = discovery_info.get("host")
name = f"{tv_name} ({model})"
if name.startswith("[TV]"):
name = name[4:]
port = None
timeout = DEFAULT_TIMEOUT
mac = None
broadcast = DEFAULT_BROADCAST_ADDRESS
uuid = discovery_info.get("udn")
if uuid and uuid.startswith("uuid:"):
uuid = uuid[len("uuid:") :]
# Only add a device once, so discovered devices do not override manual
# config.
ip_addr = socket.gethostbyname(host)
if ip_addr not in known_devices:
known_devices.add(ip_addr)
add_entities([SamsungTVDevice(host, port, name, timeout, mac, broadcast, uuid)])
LOGGER.info("Samsung TV %s added as '%s'", host, name)
else:
LOGGER.info("Ignoring duplicate Samsung TV %s", host)
pass
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Samsung TV from a config entry."""
host = config_entry.data.get(CONF_HOST)
host = config_entry.data[CONF_HOST]
mac = config_entry.data.get(CONF_MAC)
broadcast = config_entry.data.get(CONF_BROADCAST_ADDRESS, DEFAULT_BROADCAST_ADDRESS)
broadcast = config_entry.data.get(CONF_BROADCAST_ADDRESS) or wakeonlan.BROADCAST_IP
manufacturer = config_entry.data.get(CONF_MANUFACTURER)
model = config_entry.data.get(CONF_MODEL)
name = config_entry.title

View File

@@ -47,11 +47,7 @@ def remote_fixture():
"""Patch the samsungctl Remote."""
with patch("homeassistant.components.samsungtv.config_flow.socket"), patch(
"homeassistant.components.samsungtv.config_flow.Remote"
), patch(
"homeassistant.components.samsungtv.media_player.SamsungRemote"
) as remote, patch(
"homeassistant.components.samsungtv.media_player.socket"
):
), patch("homeassistant.components.samsungtv.media_player.SamsungRemote") as remote:
yield remote

View File

@@ -8,6 +8,7 @@ from asynctest import mock
import pytest
from samsungctl import exceptions
from tests.common import async_fire_time_changed
import wakeonlan
from homeassistant.components.media_player import DEVICE_CLASS_TV
from homeassistant.components.media_player.const import (
@@ -22,6 +23,7 @@ from homeassistant.components.media_player.const import (
MEDIA_TYPE_CHANNEL,
MEDIA_TYPE_URL,
)
from homeassistant.components import samsungtv
from homeassistant.components.samsungtv.const import DOMAIN as SAMSUNGTV_DOMAIN
from homeassistant.components.samsungtv.media_player import (
CONF_TIMEOUT,
@@ -36,7 +38,6 @@ from homeassistant.const import (
CONF_HOST,
CONF_MAC,
CONF_NAME,
CONF_PLATFORM,
CONF_PORT,
SERVICE_MEDIA_NEXT_TRACK,
SERVICE_MEDIA_PAUSE,
@@ -51,27 +52,26 @@ from homeassistant.const import (
STATE_ON,
STATE_UNKNOWN,
)
from homeassistant.helpers.discovery import async_load_platform
from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util
ENTITY_ID = f"{DOMAIN}.fake"
MOCK_CONFIG = {
DOMAIN: {
CONF_PLATFORM: SAMSUNGTV_DOMAIN,
SAMSUNGTV_DOMAIN: [
{
CONF_HOST: "fake",
CONF_NAME: "fake",
CONF_PORT: 8001,
CONF_TIMEOUT: 10,
CONF_MAC: "38:f9:d3:82:b4:f1",
}
]
}
ENTITY_ID_BROADCAST = f"{DOMAIN}.fake_broadcast"
MOCK_CONFIG_BROADCAST = {
DOMAIN: {
CONF_PLATFORM: SAMSUNGTV_DOMAIN,
SAMSUNGTV_DOMAIN: [
{
CONF_HOST: "fake_broadcast",
CONF_NAME: "fake_broadcast",
CONF_PORT: 8001,
@@ -79,43 +79,49 @@ MOCK_CONFIG_BROADCAST = {
CONF_MAC: "38:f9:d3:82:b4:f1",
CONF_BROADCAST_ADDRESS: "192.168.5.255",
}
]
}
ENTITY_ID_NOMAC = f"{DOMAIN}.fake_nomac"
MOCK_CONFIG_NOMAC = {
DOMAIN: {
CONF_PLATFORM: SAMSUNGTV_DOMAIN,
SAMSUNGTV_DOMAIN: [
{
CONF_HOST: "fake_nomac",
CONF_NAME: "fake_nomac",
CONF_PORT: 55000,
CONF_TIMEOUT: 10,
}
]
}
ENTITY_ID_AUTO = f"{DOMAIN}.fake_auto"
MOCK_CONFIG_AUTO = {
DOMAIN: {
CONF_PLATFORM: SAMSUNGTV_DOMAIN,
CONF_HOST: "fake_auto",
CONF_NAME: "fake_auto",
}
SAMSUNGTV_DOMAIN: [{CONF_HOST: "fake_auto", CONF_NAME: "fake_auto"}]
}
ENTITY_ID_DISCOVERY = f"{DOMAIN}.fake_discovery_fake_model"
MOCK_CONFIG_DISCOVERY = {
SAMSUNGTV_DOMAIN: [
{
"name": "fake_discovery",
"model_name": "fake_model",
"host": "fake_host",
"udn": "fake_uuid",
}
]
}
ENTITY_ID_DISCOVERY_PREFIX = f"{DOMAIN}.fake_discovery_prefix_fake_model_prefix"
MOCK_CONFIG_DISCOVERY_PREFIX = {
DOMAIN: [
{
"name": "[TV]fake_discovery_prefix",
"model_name": "fake_model_prefix",
"host": "fake_host_prefix",
"udn": "uuid:fake_uuid_prefix",
}
]
}
AUTODETECT_WEBSOCKET = {
"name": "HomeAssistant",
@@ -124,7 +130,7 @@ AUTODETECT_WEBSOCKET = {
"method": "websocket",
"port": None,
"host": "fake_auto",
"timeout": 1,
"timeout": None,
}
AUTODETECT_LEGACY = {
"name": "HomeAssistant",
@@ -133,22 +139,20 @@ AUTODETECT_LEGACY = {
"method": "legacy",
"port": None,
"host": "fake_auto",
"timeout": 1,
"timeout": None,
}
@pytest.fixture(name="remote")
def remote_fixture():
"""Patch the samsungctl Remote."""
with patch(
with patch("homeassistant.components.samsungtv.config_flow.socket"), patch(
"homeassistant.components.samsungtv.config_flow.Remote"
), patch(
"homeassistant.components.samsungtv.media_player.SamsungRemote"
) as remote_class, patch(
"homeassistant.components.samsungtv.media_player.socket"
) as socket_class:
) as remote_class:
remote = mock.Mock()
remote_class.return_value = remote
socket = mock.Mock()
socket_class.return_value = socket
yield remote
@@ -158,6 +162,7 @@ def wakeonlan_fixture():
with patch(
"homeassistant.components.samsungtv.media_player.wakeonlan"
) as wakeonlan_module:
wakeonlan_module.BROADCAST_IP = wakeonlan.BROADCAST_IP
yield wakeonlan_module
@@ -169,7 +174,7 @@ def mock_now():
async def setup_samsungtv(hass, config):
"""Set up mock Samsung TV."""
await async_setup_component(hass, "media_player", config)
await samsungtv.async_setup(hass, config)
await hass.async_block_till_done()
@@ -181,9 +186,15 @@ async def test_setup_with_mac(hass, remote):
async def test_setup_duplicate(hass, remote, caplog):
"""Test duplicate setup of platform."""
DUPLICATE = {DOMAIN: [MOCK_CONFIG[DOMAIN], MOCK_CONFIG[DOMAIN]]}
DUPLICATE = {
SAMSUNGTV_DOMAIN: [
MOCK_CONFIG[SAMSUNGTV_DOMAIN][0],
MOCK_CONFIG[SAMSUNGTV_DOMAIN][0],
]
}
await setup_samsungtv(hass, DUPLICATE)
assert "Ignoring duplicate Samsung TV fake" in caplog.text
assert hass.states.get(ENTITY_ID)
assert len(hass.states.async_all()) == 1
async def test_setup_without_mac(hass, remote):
@@ -192,40 +203,6 @@ async def test_setup_without_mac(hass, remote):
assert hass.states.get(ENTITY_ID_NOMAC)
async def test_setup_discovery(hass, remote):
"""Test setup of platform with discovery."""
hass.async_create_task(
async_load_platform(
hass, DOMAIN, SAMSUNGTV_DOMAIN, MOCK_CONFIG_DISCOVERY, {DOMAIN: {}}
)
)
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID_DISCOVERY)
assert state
assert state.name == "fake_discovery (fake_model)"
entity_registry = await hass.helpers.entity_registry.async_get_registry()
entry = entity_registry.async_get(ENTITY_ID_DISCOVERY)
assert entry
assert entry.unique_id == "fake_uuid"
async def test_setup_discovery_prefix(hass, remote):
"""Test setup of platform with discovery."""
hass.async_create_task(
async_load_platform(
hass, DOMAIN, SAMSUNGTV_DOMAIN, MOCK_CONFIG_DISCOVERY_PREFIX, {DOMAIN: {}}
)
)
await hass.async_block_till_done()
state = hass.states.get(ENTITY_ID_DISCOVERY_PREFIX)
assert state
assert state.name == "fake_discovery_prefix (fake_model_prefix)"
entity_registry = await hass.helpers.entity_registry.async_get_registry()
entry = entity_registry.async_get(ENTITY_ID_DISCOVERY_PREFIX)
assert entry
assert entry.unique_id == "fake_uuid_prefix"
async def test_update_on(hass, remote, mock_now):
"""Testing update tv on."""
await setup_samsungtv(hass, MOCK_CONFIG)
@@ -268,9 +245,9 @@ async def test_send_key(hass, remote, wakeonlan):
async def test_send_key_autodetect_websocket(hass, remote):
"""Test for send key with autodetection of protocol."""
with patch(
"homeassistant.components.samsungtv.media_player.SamsungRemote"
) as remote, patch("homeassistant.components.samsungtv.media_player.socket"):
with patch("homeassistant.components.samsungtv.config_flow.socket"), patch(
"homeassistant.components.samsungtv.config_flow.Remote"
), patch("homeassistant.components.samsungtv.media_player.SamsungRemote") as remote:
await setup_samsungtv(hass, MOCK_CONFIG_AUTO)
assert await hass.services.async_call(
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID_AUTO}, True
@@ -284,10 +261,12 @@ async def test_send_key_autodetect_websocket(hass, remote):
async def test_send_key_autodetect_websocket_exception(hass, caplog):
"""Test for send key with autodetection of protocol."""
caplog.set_level(logging.DEBUG)
with patch(
with patch("homeassistant.components.samsungtv.config_flow.socket"), patch(
"homeassistant.components.samsungtv.config_flow.Remote"
), patch(
"homeassistant.components.samsungtv.media_player.SamsungRemote",
side_effect=[exceptions.AccessDenied("Boom"), mock.DEFAULT],
) as remote, patch("homeassistant.components.samsungtv.media_player.socket"):
) as remote:
await setup_samsungtv(hass, MOCK_CONFIG_AUTO)
assert await hass.services.async_call(
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID_AUTO}, True
@@ -306,10 +285,12 @@ async def test_send_key_autodetect_websocket_exception(hass, caplog):
async def test_send_key_autodetect_legacy(hass, remote):
"""Test for send key with autodetection of protocol."""
with patch(
with patch("homeassistant.components.samsungtv.config_flow.socket"), patch(
"homeassistant.components.samsungtv.config_flow.Remote"
), patch(
"homeassistant.components.samsungtv.media_player.SamsungRemote",
side_effect=[OSError("Boom"), mock.DEFAULT],
) as remote, patch("homeassistant.components.samsungtv.media_player.socket"):
) as remote:
await setup_samsungtv(hass, MOCK_CONFIG_AUTO)
assert await hass.services.async_call(
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID_AUTO}, True
@@ -325,10 +306,12 @@ async def test_send_key_autodetect_legacy(hass, remote):
async def test_send_key_autodetect_none(hass, remote):
"""Test for send key with autodetection of protocol."""
with patch(
with patch("homeassistant.components.samsungtv.config_flow.socket"), patch(
"homeassistant.components.samsungtv.config_flow.Remote"
), patch(
"homeassistant.components.samsungtv.media_player.SamsungRemote",
side_effect=OSError("Boom"),
) as remote, patch("homeassistant.components.samsungtv.media_player.socket"):
) as remote:
await setup_samsungtv(hass, MOCK_CONFIG_AUTO)
assert await hass.services.async_call(
DOMAIN, SERVICE_VOLUME_UP, {ATTR_ENTITY_ID: ENTITY_ID_AUTO}, True