Improve PS4 media art fetching and config flow (#22167)

* improved config flow

* Added errors, docs url

* Added errors, docs url

* Added manual config mode

* Add tests for manual/auto host input

* fix inline docs

* fix inline docs

* Changed region list

* Added deprecated region message

* removed DEFAULT_REGION

* Added close method

* Fixes

* Update const.py

* Update const.py

* Update const.py

* Update test_config_flow.py

* Added invalid pin errors

* Update strings.json

* Update strings.json

* bump pyps4 to 0.5.0

* Bump pyps4 0.5.0

* Bump pyps4 to 0.5.0

* test fixes

* pylint

* Change error reference

* remove pin messages

* remove pin messages

* Update en.json

* remove pin tests

* fix tests

* update vol

* Vol fix

* Update config_flow.py

* Add migration for v1 entry

* lint

* fixes

* typo

* fix

* Update config_flow.py

* Fix vol

* Executor job for io method.

* Update __init__.py

* blank line

* Update __init__.py

* Update tests/components/ps4/test_config_flow.py

Co-Authored-By: ktnrg45 <38207570+ktnrg45@users.noreply.github.com>
This commit is contained in:
ktnrg45 2019-03-25 05:25:15 -07:00 committed by Charles Garwood
parent 96133f5e6b
commit 17a96c6d9b
9 changed files with 227 additions and 71 deletions

View file

@ -4,18 +4,27 @@
"credential_error": "Error fetching credentials.", "credential_error": "Error fetching credentials.",
"devices_configured": "All devices found are already configured.", "devices_configured": "All devices found are already configured.",
"no_devices_found": "No PlayStation 4 devices found on the network.", "no_devices_found": "No PlayStation 4 devices found on the network.",
"port_987_bind_error": "Could not bind to port 987.", "port_987_bind_error": "Could not bind to port 987. Refer to the [documentation](https://www.home-assistant.io/components/ps4/) for additional info.",
"port_997_bind_error": "Could not bind to port 997." "port_997_bind_error": "Could not bind to port 997. Refer to the [documentation](https://www.home-assistant.io/components/ps4/) for additional info."
}, },
"error": { "error": {
"login_failed": "Failed to pair to PlayStation 4. Verify PIN is correct.", "login_failed": "Failed to pair to PlayStation 4. Verify PIN is correct.",
"not_ready": "PlayStation 4 is not on or connected to network." "not_ready": "PlayStation 4 is not on or connected to network.",
"no_ipaddress": "Enter the IP Address of the PlayStation 4 you would like to configure."
}, },
"step": { "step": {
"creds": { "creds": {
"description": "Credentials needed. Press 'Submit' and then in the PS4 2nd Screen App, refresh devices and select the 'Home-Assistant' device to continue.", "description": "Credentials needed. Press 'Submit' and then in the PS4 2nd Screen App, refresh devices and select the 'Home-Assistant' device to continue.",
"title": "PlayStation 4" "title": "PlayStation 4"
}, },
"mode": {
"data": {
"mode": "Config Mode",
"ip_address": "IP Address (Leave empty if using Auto Discovery)."
},
"description": "Select mode for configuration. The IP Address field can be left blank if selecting Auto Discovery, as devices will be automatically discovered.",
"title": "PlayStation 4"
},
"link": { "link": {
"data": { "data": {
"code": "PIN", "code": "PIN",
@ -23,7 +32,7 @@
"name": "Name", "name": "Name",
"region": "Region" "region": "Region"
}, },
"description": "Enter your PlayStation 4 information. For 'PIN', navigate to 'Settings' on your PlayStation 4 console. Then navigate to 'Mobile App Connection Settings' and select 'Add Device'. Enter the PIN that is displayed.", "description": "Enter your PlayStation 4 information. For 'PIN', navigate to 'Settings' on your PlayStation 4 console. Then navigate to 'Mobile App Connection Settings' and select 'Add Device'. Enter the PIN that is displayed. Refer to the [documentation](https://www.home-assistant.io/components/ps4/) for additional info.",
"title": "PlayStation 4" "title": "PlayStation 4"
} }
}, },

View file

@ -6,13 +6,15 @@ https://home-assistant.io/components/ps4/
""" """
import logging import logging
from .config_flow import ( # noqa pylint: disable=unused-import from homeassistant.const import CONF_REGION
PlayStation4FlowHandler) from homeassistant.util import location
from .config_flow import PlayStation4FlowHandler # noqa: pylint: disable=unused-import
from .const import DOMAIN # noqa: pylint: disable=unused-import from .const import DOMAIN # noqa: pylint: disable=unused-import
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['pyps4-homeassistant==0.4.8'] REQUIREMENTS = ['pyps4-homeassistant==0.5.0']
async def async_setup(hass, config): async def async_setup(hass, config):
@ -32,3 +34,40 @@ async def async_unload_entry(hass, entry):
await hass.config_entries.async_forward_entry_unload( await hass.config_entries.async_forward_entry_unload(
entry, 'media_player') entry, 'media_player')
return True return True
async def async_migrate_entry(hass, entry):
"""Migrate old entry."""
from pyps4_homeassistant.media_art import COUNTRIES
config_entries = hass.config_entries
data = entry.data
version = entry.version
reason = {1: "Region codes have changed"} # From 0.89
# Migrate Version 1 -> Version 2
if version == 1:
loc = await hass.async_add_executor_job(location.detect_location_info)
if loc:
country = loc.country_name
if country in COUNTRIES:
for device in data['devices']:
device[CONF_REGION] = country
entry.version = 2
config_entries.async_update_entry(entry, data=data)
_LOGGER.info(
"PlayStation 4 Config Updated: \
Region changed to: %s", country)
return True
msg = """{} for the PlayStation 4 Integration.
Please remove the PS4 Integration and re-configure
[here](/config/integrations).""".format(reason[version])
hass.components.persistent_notification.async_create(
title="PlayStation 4 Integration Configuration Requires Update",
message=msg,
notification_id='config_entry_migration'
)
return False

View file

@ -8,10 +8,14 @@ from homeassistant import config_entries
from homeassistant.const import ( from homeassistant.const import (
CONF_CODE, CONF_HOST, CONF_IP_ADDRESS, CONF_NAME, CONF_REGION, CONF_TOKEN) CONF_CODE, CONF_HOST, CONF_IP_ADDRESS, CONF_NAME, CONF_REGION, CONF_TOKEN)
from .const import DEFAULT_NAME, DEFAULT_REGION, DOMAIN, REGIONS from .const import DEFAULT_NAME, DOMAIN
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CONF_MODE = 'Config Mode'
CONF_AUTO = "Auto Discover"
CONF_MANUAL = "Manual Entry"
UDP_PORT = 987 UDP_PORT = 987
TCP_PORT = 997 TCP_PORT = 997
PORT_MSG = {UDP_PORT: 'port_987_bind_error', TCP_PORT: 'port_997_bind_error'} PORT_MSG = {UDP_PORT: 'port_987_bind_error', TCP_PORT: 'port_997_bind_error'}
@ -21,7 +25,7 @@ PORT_MSG = {UDP_PORT: 'port_987_bind_error', TCP_PORT: 'port_997_bind_error'}
class PlayStation4FlowHandler(config_entries.ConfigFlow): class PlayStation4FlowHandler(config_entries.ConfigFlow):
"""Handle a PlayStation 4 config flow.""" """Handle a PlayStation 4 config flow."""
VERSION = 1 VERSION = 2
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
def __init__(self): def __init__(self):
@ -34,6 +38,8 @@ class PlayStation4FlowHandler(config_entries.ConfigFlow):
self.host = None self.host = None
self.region = None self.region = None
self.pin = None self.pin = None
self.m_device = None
self.device_list = []
async def async_step_user(self, user_input=None): async def async_step_user(self, user_input=None):
"""Handle a user config flow.""" """Handle a user config flow."""
@ -46,7 +52,7 @@ class PlayStation4FlowHandler(config_entries.ConfigFlow):
return self.async_abort(reason=reason) return self.async_abort(reason=reason)
# Skip Creds Step if a device is configured. # Skip Creds Step if a device is configured.
if self.hass.config_entries.async_entries(DOMAIN): if self.hass.config_entries.async_entries(DOMAIN):
return await self.async_step_link() return await self.async_step_mode()
return await self.async_step_creds() return await self.async_step_creds()
async def async_step_creds(self, user_input=None): async def async_step_creds(self, user_input=None):
@ -56,26 +62,55 @@ class PlayStation4FlowHandler(config_entries.ConfigFlow):
self.helper.get_creds) self.helper.get_creds)
if self.creds is not None: if self.creds is not None:
return await self.async_step_link() return await self.async_step_mode()
return self.async_abort(reason='credential_error') return self.async_abort(reason='credential_error')
return self.async_show_form( return self.async_show_form(
step_id='creds') step_id='creds')
async def async_step_mode(self, user_input=None):
"""Prompt for mode."""
errors = {}
mode = [CONF_AUTO, CONF_MANUAL]
if user_input is not None:
if user_input[CONF_MODE] == CONF_MANUAL:
try:
device = user_input[CONF_IP_ADDRESS]
if device:
self.m_device = device
except KeyError:
errors[CONF_IP_ADDRESS] = 'no_ipaddress'
if not errors:
return await self.async_step_link()
mode_schema = OrderedDict()
mode_schema[vol.Required(
CONF_MODE, default=CONF_AUTO)] = vol.In(list(mode))
mode_schema[vol.Optional(CONF_IP_ADDRESS)] = str
return self.async_show_form(
step_id='mode',
data_schema=vol.Schema(mode_schema),
errors=errors,
)
async def async_step_link(self, user_input=None): async def async_step_link(self, user_input=None):
"""Prompt user input. Create or edit entry.""" """Prompt user input. Create or edit entry."""
from pyps4_homeassistant.media_art import COUNTRIES
regions = sorted(COUNTRIES.keys())
errors = {} errors = {}
if user_input is None:
# Search for device. # Search for device.
devices = await self.hass.async_add_executor_job( devices = await self.hass.async_add_executor_job(
self.helper.has_devices) self.helper.has_devices, self.m_device)
# Abort if can't find device. # Abort if can't find device.
if not devices: if not devices:
return self.async_abort(reason='no_devices_found') return self.async_abort(reason='no_devices_found')
device_list = [ self.device_list = [device['host-ip'] for device in devices]
device['host-ip'] for device in devices]
# If entry exists check that devices found aren't configured. # If entry exists check that devices found aren't configured.
if self.hass.config_entries.async_entries(DOMAIN): if self.hass.config_entries.async_entries(DOMAIN):
@ -86,11 +121,11 @@ class PlayStation4FlowHandler(config_entries.ConfigFlow):
# Retrieve device data from entry # Retrieve device data from entry
conf_devices = entry.data['devices'] conf_devices = entry.data['devices']
for c_device in conf_devices: for c_device in conf_devices:
if c_device['host'] in device_list: if c_device['host'] in self.device_list:
# Remove configured device from search list. # Remove configured device from search list.
device_list.remove(c_device['host']) self.device_list.remove(c_device['host'])
# If list is empty then all devices are configured. # If list is empty then all devices are configured.
if not device_list: if not self.device_list:
return self.async_abort(reason='devices_configured') return self.async_abort(reason='devices_configured')
# Add existing creds for linking. Should be only 1. # Add existing creds for linking. Should be only 1.
if not creds: if not creds:
@ -102,7 +137,7 @@ class PlayStation4FlowHandler(config_entries.ConfigFlow):
if user_input is not None: if user_input is not None:
self.region = user_input[CONF_REGION] self.region = user_input[CONF_REGION]
self.name = user_input[CONF_NAME] self.name = user_input[CONF_NAME]
self.pin = user_input[CONF_CODE] self.pin = str(user_input[CONF_CODE])
self.host = user_input[CONF_IP_ADDRESS] self.host = user_input[CONF_IP_ADDRESS]
is_ready, is_login = await self.hass.async_add_executor_job( is_ready, is_login = await self.hass.async_add_executor_job(
@ -130,10 +165,11 @@ class PlayStation4FlowHandler(config_entries.ConfigFlow):
# Show User Input form. # Show User Input form.
link_schema = OrderedDict() link_schema = OrderedDict()
link_schema[vol.Required(CONF_IP_ADDRESS)] = vol.In(list(device_list)) link_schema[vol.Required(CONF_IP_ADDRESS)] = vol.In(
link_schema[vol.Required( list(self.device_list))
CONF_REGION, default=DEFAULT_REGION)] = vol.In(list(REGIONS)) link_schema[vol.Required(CONF_REGION)] = vol.In(list(regions))
link_schema[vol.Required(CONF_CODE)] = str link_schema[vol.Required(CONF_CODE)] = vol.All(
vol.Strip, vol.Length(min=8, max=8), vol.Coerce(int))
link_schema[vol.Required(CONF_NAME, default=DEFAULT_NAME)] = str link_schema[vol.Required(CONF_NAME, default=DEFAULT_NAME)] = str
return self.async_show_form( return self.async_show_form(

View file

@ -1,5 +1,7 @@
"""Constants for PlayStation 4.""" """Constants for PlayStation 4."""
DEFAULT_NAME = "PlayStation 4" DEFAULT_NAME = "PlayStation 4"
DEFAULT_REGION = "R1" DEFAULT_REGION = "United States"
DOMAIN = 'ps4' DOMAIN = 'ps4'
REGIONS = ('R1', 'R2', 'R3', 'R4', 'R5')
# Deprecated used for logger/backwards compatibility from 0.89
REGIONS = ['R1', 'R2', 'R3', 'R4', 'R5']

View file

@ -20,7 +20,7 @@ from homeassistant.const import (
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.util.json import load_json, save_json from homeassistant.util.json import load_json, save_json
from .const import DOMAIN as PS4_DOMAIN from .const import DOMAIN as PS4_DOMAIN, REGIONS as deprecated_regions
DEPENDENCIES = ['ps4'] DEPENDENCIES = ['ps4']
@ -142,6 +142,12 @@ class PS4Device(MediaPlayerDevice):
self._games = self.load_games() self._games = self.load_games()
if self._games is not None: if self._games is not None:
self._source_list = list(sorted(self._games.values())) self._source_list = list(sorted(self._games.values()))
# Non-Breaking although data returned may be inaccurate.
if self._region in deprecated_regions:
_LOGGER.info("""Region: %s has been deprecated.
Please remove PS4 integration
and Re-configure again to utilize
current regions""", self._region)
except socket.timeout: except socket.timeout:
status = None status = None
if status is not None: if status is not None:
@ -275,6 +281,8 @@ class PS4Device(MediaPlayerDevice):
async def async_will_remove_from_hass(self): async def async_will_remove_from_hass(self):
"""Remove Entity from Hass.""" """Remove Entity from Hass."""
# Close TCP Socket
await self.hass.async_add_executor_job(self._ps4.close)
self.hass.data[PS4_DATA].devices.remove(self) self.hass.data[PS4_DATA].devices.remove(self)
@property @property
@ -320,6 +328,7 @@ class PS4Device(MediaPlayerDevice):
@property @property
def media_content_type(self): def media_content_type(self):
"""Content type of current playing media.""" """Content type of current playing media."""
# No MEDIA_TYPE_GAME attr as of 0.90.
return MEDIA_TYPE_MUSIC return MEDIA_TYPE_MUSIC
@property @property

View file

@ -6,9 +6,17 @@
"title": "PlayStation 4", "title": "PlayStation 4",
"description": "Credentials needed. Press 'Submit' and then in the PS4 2nd Screen App, refresh devices and select the 'Home-Assistant' device to continue." "description": "Credentials needed. Press 'Submit' and then in the PS4 2nd Screen App, refresh devices and select the 'Home-Assistant' device to continue."
}, },
"mode": {
"title": "PlayStation 4",
"description": "Select mode for configuration. The IP Address field can be left blank if selecting Auto Discovery, as devices will be automatically discovered.",
"data": {
"mode": "Config Mode",
"ip_address": "IP Address (Leave empty if using Auto Discovery)."
},
},
"link": { "link": {
"title": "PlayStation 4", "title": "PlayStation 4",
"description": "Enter your PlayStation 4 information. For 'PIN', navigate to 'Settings' on your PlayStation 4 console. Then navigate to 'Mobile App Connection Settings' and select 'Add Device'. Enter the PIN that is displayed.", "description": "Enter your PlayStation 4 information. For 'PIN', navigate to 'Settings' on your PlayStation 4 console. Then navigate to 'Mobile App Connection Settings' and select 'Add Device'. Enter the PIN that is displayed. Refer to the [documentation](https://www.home-assistant.io/components/ps4/) for additional info.",
"data": { "data": {
"region": "Region", "region": "Region",
"name": "Name", "name": "Name",
@ -19,14 +27,15 @@
}, },
"error": { "error": {
"not_ready": "PlayStation 4 is not on or connected to network.", "not_ready": "PlayStation 4 is not on or connected to network.",
"login_failed": "Failed to pair to PlayStation 4. Verify PIN is correct." "login_failed": "Failed to pair to PlayStation 4. Verify PIN is correct.",
"no_ipaddress": "Enter the IP Address of the PlayStation 4 you would like to configure."
}, },
"abort": { "abort": {
"credential_error": "Error fetching credentials.", "credential_error": "Error fetching credentials.",
"no_devices_found": "No PlayStation 4 devices found on the network.", "no_devices_found": "No PlayStation 4 devices found on the network.",
"devices_configured": "All devices found are already configured.", "devices_configured": "All devices found are already configured.",
"port_987_bind_error": "Could not bind to port 987.", "port_987_bind_error": "Could not bind to port 987. Refer to the [documentation](https://www.home-assistant.io/components/ps4/) for additional info.",
"port_997_bind_error": "Could not bind to port 997." "port_997_bind_error": "Could not bind to port 997. Refer to the [documentation](https://www.home-assistant.io/components/ps4/) for additional info."
} }
} }
} }

View file

@ -1232,7 +1232,7 @@ pypoint==1.1.1
pypollencom==2.2.3 pypollencom==2.2.3
# homeassistant.components.ps4 # homeassistant.components.ps4
pyps4-homeassistant==0.4.8 pyps4-homeassistant==0.5.0
# homeassistant.components.qwikswitch # homeassistant.components.qwikswitch
pyqwikswitch==0.8 pyqwikswitch==0.8

View file

@ -230,7 +230,7 @@ pyopenuv==1.0.9
pyotp==2.2.6 pyotp==2.2.6
# homeassistant.components.ps4 # homeassistant.components.ps4
pyps4-homeassistant==0.4.8 pyps4-homeassistant==0.5.0
# homeassistant.components.qwikswitch # homeassistant.components.qwikswitch
pyqwikswitch==0.8 pyqwikswitch==0.8

View file

@ -44,6 +44,9 @@ MOCK_DATA = {
MOCK_UDP_PORT = int(987) MOCK_UDP_PORT = int(987)
MOCK_TCP_PORT = int(997) MOCK_TCP_PORT = int(997)
MOCK_AUTO = {"Config Mode": 'Auto Discover'}
MOCK_MANUAL = {"Config Mode": 'Manual Entry', CONF_IP_ADDRESS: MOCK_HOST}
async def test_full_flow_implementation(hass): async def test_full_flow_implementation(hass):
"""Test registering an implementation and flow works.""" """Test registering an implementation and flow works."""
@ -58,13 +61,18 @@ async def test_full_flow_implementation(hass):
assert result['type'] == data_entry_flow.RESULT_TYPE_FORM assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
assert result['step_id'] == 'creds' assert result['step_id'] == 'creds'
# Step Creds results with form in Step Link. # Step Creds results with form in Step Mode.
with patch('pyps4_homeassistant.Helper.get_creds', with patch('pyps4_homeassistant.Helper.get_creds',
return_value=MOCK_CREDS), \ return_value=MOCK_CREDS):
patch('pyps4_homeassistant.Helper.has_devices',
return_value=[{'host-ip': MOCK_HOST}]):
result = await flow.async_step_creds({}) result = await flow.async_step_creds({})
assert result['type'] == data_entry_flow.RESULT_TYPE_FORM assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
assert result['step_id'] == 'mode'
# Step Mode with User Input which is not manual, results in Step Link.
with patch('pyps4_homeassistant.Helper.has_devices',
return_value=[{'host-ip': MOCK_HOST}]):
result = await flow.async_step_mode(MOCK_AUTO)
assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
assert result['step_id'] == 'link' assert result['step_id'] == 'link'
# User Input results in created entry. # User Input results in created entry.
@ -78,6 +86,8 @@ async def test_full_flow_implementation(hass):
assert result['data']['devices'] == [MOCK_DEVICE] assert result['data']['devices'] == [MOCK_DEVICE]
assert result['title'] == MOCK_TITLE assert result['title'] == MOCK_TITLE
await hass.async_block_till_done()
# Add entry using result data. # Add entry using result data.
mock_data = { mock_data = {
CONF_TOKEN: result['data'][CONF_TOKEN], CONF_TOKEN: result['data'][CONF_TOKEN],
@ -104,13 +114,18 @@ async def test_multiple_flow_implementation(hass):
assert result['type'] == data_entry_flow.RESULT_TYPE_FORM assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
assert result['step_id'] == 'creds' assert result['step_id'] == 'creds'
# Step Creds results with form in Step Link. # Step Creds results with form in Step Mode.
with patch('pyps4_homeassistant.Helper.get_creds', with patch('pyps4_homeassistant.Helper.get_creds',
return_value=MOCK_CREDS), \ return_value=MOCK_CREDS):
patch('pyps4_homeassistant.Helper.has_devices', result = await flow.async_step_creds({})
assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
assert result['step_id'] == 'mode'
# Step Mode with User Input which is not manual, results in Step Link.
with patch('pyps4_homeassistant.Helper.has_devices',
return_value=[{'host-ip': MOCK_HOST}, return_value=[{'host-ip': MOCK_HOST},
{'host-ip': MOCK_HOST_ADDITIONAL}]): {'host-ip': MOCK_HOST_ADDITIONAL}]):
result = await flow.async_step_creds({}) result = await flow.async_step_mode(MOCK_AUTO)
assert result['type'] == data_entry_flow.RESULT_TYPE_FORM assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
assert result['step_id'] == 'link' assert result['step_id'] == 'link'
@ -142,7 +157,7 @@ async def test_multiple_flow_implementation(hass):
# Test additional flow. # Test additional flow.
# User Step Started, results in Step Link: # User Step Started, results in Step Mode:
with patch('pyps4_homeassistant.Helper.port_bind', with patch('pyps4_homeassistant.Helper.port_bind',
return_value=None), \ return_value=None), \
patch('pyps4_homeassistant.Helper.has_devices', patch('pyps4_homeassistant.Helper.has_devices',
@ -150,6 +165,14 @@ async def test_multiple_flow_implementation(hass):
{'host-ip': MOCK_HOST_ADDITIONAL}]): {'host-ip': MOCK_HOST_ADDITIONAL}]):
result = await flow.async_step_user() result = await flow.async_step_user()
assert result['type'] == data_entry_flow.RESULT_TYPE_FORM assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
assert result['step_id'] == 'mode'
# Step Mode with User Input which is not manual, results in Step Link.
with patch('pyps4_homeassistant.Helper.has_devices',
return_value=[{'host-ip': MOCK_HOST},
{'host-ip': MOCK_HOST_ADDITIONAL}]):
result = await flow.async_step_mode(MOCK_AUTO)
assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
assert result['step_id'] == 'link' assert result['step_id'] == 'link'
# Step Link # Step Link
@ -158,12 +181,14 @@ async def test_multiple_flow_implementation(hass):
{'host-ip': MOCK_HOST_ADDITIONAL}]), \ {'host-ip': MOCK_HOST_ADDITIONAL}]), \
patch('pyps4_homeassistant.Helper.link', patch('pyps4_homeassistant.Helper.link',
return_value=(True, True)): return_value=(True, True)):
result = await flow.async_step_link(user_input=MOCK_CONFIG_ADDITIONAL) result = await flow.async_step_link(MOCK_CONFIG_ADDITIONAL)
assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result['data'][CONF_TOKEN] == MOCK_CREDS assert result['data'][CONF_TOKEN] == MOCK_CREDS
assert len(result['data']['devices']) == 1 assert len(result['data']['devices']) == 1
assert result['title'] == MOCK_TITLE assert result['title'] == MOCK_TITLE
await hass.async_block_till_done()
mock_data = { mock_data = {
CONF_TOKEN: result['data'][CONF_TOKEN], CONF_TOKEN: result['data'][CONF_TOKEN],
'devices': result['data']['devices']} 'devices': result['data']['devices']}
@ -230,7 +255,7 @@ async def test_additional_device(hass):
{'host-ip': MOCK_HOST_ADDITIONAL}]), \ {'host-ip': MOCK_HOST_ADDITIONAL}]), \
patch('pyps4_homeassistant.Helper.link', patch('pyps4_homeassistant.Helper.link',
return_value=(True, True)): return_value=(True, True)):
result = await flow.async_step_link(user_input=MOCK_CONFIG_ADDITIONAL) result = await flow.async_step_link(MOCK_CONFIG_ADDITIONAL)
assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result['data'][CONF_TOKEN] == MOCK_CREDS assert result['data'][CONF_TOKEN] == MOCK_CREDS
assert len(result['data']['devices']) == 1 assert len(result['data']['devices']) == 1
@ -249,12 +274,26 @@ async def test_no_devices_found_abort(hass):
flow = ps4.PlayStation4FlowHandler() flow = ps4.PlayStation4FlowHandler()
flow.hass = hass flow.hass = hass
with patch('pyps4_homeassistant.Helper.has_devices', return_value=None): with patch('pyps4_homeassistant.Helper.has_devices', return_value=[]):
result = await flow.async_step_link(MOCK_CONFIG) result = await flow.async_step_link()
assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT
assert result['reason'] == 'no_devices_found' assert result['reason'] == 'no_devices_found'
async def test_manual_mode(hass):
"""Test host specified in manual mode is passed to Step Link."""
flow = ps4.PlayStation4FlowHandler()
flow.hass = hass
# Step Mode with User Input: manual, results in Step Link.
with patch('pyps4_homeassistant.Helper.has_devices',
return_value=[{'host-ip': flow.m_device}]):
result = await flow.async_step_mode(MOCK_MANUAL)
assert flow.m_device == MOCK_HOST
assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
assert result['step_id'] == 'link'
async def test_credential_abort(hass): async def test_credential_abort(hass):
"""Test that failure to get credentials aborts flow.""" """Test that failure to get credentials aborts flow."""
flow = ps4.PlayStation4FlowHandler() flow = ps4.PlayStation4FlowHandler()
@ -266,8 +305,8 @@ async def test_credential_abort(hass):
assert result['reason'] == 'credential_error' assert result['reason'] == 'credential_error'
async def test_invalid_pin_error(hass): async def test_wrong_pin_error(hass):
"""Test that invalid pin throws an error.""" """Test that incorrect pin throws an error."""
flow = ps4.PlayStation4FlowHandler() flow = ps4.PlayStation4FlowHandler()
flow.hass = hass flow.hass = hass
@ -294,3 +333,16 @@ async def test_device_connection_error(hass):
assert result['type'] == data_entry_flow.RESULT_TYPE_FORM assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
assert result['step_id'] == 'link' assert result['step_id'] == 'link'
assert result['errors'] == {'base': 'not_ready'} assert result['errors'] == {'base': 'not_ready'}
async def test_manual_mode_no_ip_error(hass):
"""Test no IP specified in manual mode throws an error."""
flow = ps4.PlayStation4FlowHandler()
flow.hass = hass
mock_input = {"Config Mode": 'Manual Entry'}
result = await flow.async_step_mode(mock_input)
assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
assert result['step_id'] == 'mode'
assert result['errors'] == {CONF_IP_ADDRESS: 'no_ipaddress'}