-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add additional whirlpool_android client credentials, add support for …
…shared devices (#49) This replaces the whirlpool brand client_id and client_secret with updated values that work, as the old ones were no longer valid. This also adds the methods `_get_owned_appliances`, `_get_shared_appliances`, and `_add_appliance` to `AppliancesManager`. The `fetch_appliances` method will call `_get_owned_appliances` first and then `_get_shared_appliances`, ensuring that both types are added to the appliance list. There are some other minor changes to fix failing tests, including the addition of a noop async `wait_for_close` method to the `aiohttp` mock and pinning `aiohttp` to below 3.9.0. The `wait_for_close` is actually not required any longer with `aiohttp` pinned to a lower version, but I figure it's better to leave it there to save the next person the headache. --------- Co-authored-by: Jessica Smith <(none)> Co-authored-by: Abílio Costa <[email protected]>
- Loading branch information
1 parent
18d3512
commit 180323d
Showing
13 changed files
with
494 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -131,3 +131,4 @@ dmypy.json | |
|
||
# Pyre type checker | ||
.pyre/ | ||
.vscode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" | |
[project] | ||
name = "whirlpool_sixth_sense" | ||
version = "0.18.5" | ||
authors = [{name = "Abílio Costa", email = "[email protected]"}] | ||
authors = [{ name = "Abílio Costa", email = "[email protected]" }] | ||
description = "Unofficial API for Whirlpool's 6th Sense appliances" | ||
classifiers = [ | ||
"Programming Language :: Python :: 3", | ||
|
@@ -15,8 +15,9 @@ classifiers = [ | |
requires-python = ">=3.6" | ||
dependencies = [ | ||
"aioconsole>=0.3.1", | ||
"aiohttp>=3.7.2", | ||
"aiohttp>=3.9.1", | ||
"websockets>=8.1", | ||
"async-timeout>=4.0.3", | ||
] | ||
|
||
[project.readme] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
aioconsole>=0.3.1 | ||
aiohttp>=3.7.2 | ||
aiohttp>=3.9.1 | ||
websockets>=8.1 | ||
async-timeout>=4.0.3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
{ | ||
"KEY1": [ | ||
{ | ||
"APPLIANCE_ID": 1033050, | ||
"APPLIANCE_MASTER_ID": 3721, | ||
"MODEL_SKU_ID": null, | ||
"APPLIANCE_NAME": "Washer", | ||
"SAID": "WPR4DL103WMLXV", | ||
"NEST_AWAY": 0, | ||
"CYCLE_HANDOFF": 0, | ||
"NEST_THERMOSTAT_ID": 0, | ||
"THERMOSTAT_INFLUENCE_THRESHOLD": null, | ||
"THERMOSTAT_DESIRED_OFFSET": null, | ||
"THERMOSTAT_OFFSET_NEEDED": null, | ||
"DELETE_FLAG": 0, | ||
"DISPLAY_POSITION": null, | ||
"SERIAL": "CC26012420", | ||
"LOCATION_ID": 15862310, | ||
"MACHINE_ID": null, | ||
"MACHINE_POSITION": 0, | ||
"ISVOICEDEFAULT": 1, | ||
"DEVICE_ID": "550e8400-e29b-41d4-a716-446655441234", | ||
"APPLIANCE_TYPE_ID": null, | ||
"STATUS": "CLAIMED", | ||
"IS_ENROLLED": null, | ||
"MACHINE_STATUS": null, | ||
"APPLIANCE_MODE": 2, | ||
"CREATED_AT": 1690565765000, | ||
"UPDATED_AT": 1690565765000, | ||
"DATA_MODEL_KEY": "DDM_LAUNDRY_VMAX20_WHIRLPOOL_WASHER8_V2", | ||
"CATEGORY_NAME": "FabricCare", | ||
"MODEL_NO": "WTW8127LW1", | ||
"REPLENISHMENT_DEVICE_MODEL": null | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
{ | ||
"sharedAppliances": [ | ||
{ | ||
"shareId": 12345, | ||
"sharedFirstName": "TestUser", | ||
"appliances": [ | ||
{ | ||
"APPLIANCE_ID": 1033047, | ||
"APPLIANCE_MASTER_ID": 3720, | ||
"MODEL_SKU_ID": null, | ||
"APPLIANCE_NAME": "Washer", | ||
"SAID": "WPR4DYW3WMLLL", | ||
"NEST_AWAY": 0, | ||
"CYCLE_HANDOFF": 0, | ||
"NEST_THERMOSTAT_ID": 0, | ||
"THERMOSTAT_INFLUENCE_THRESHOLD": null, | ||
"THERMOSTAT_DESIRED_OFFSET": null, | ||
"THERMOSTAT_OFFSET_NEEDED": null, | ||
"DELETE_FLAG": 0, | ||
"DISPLAY_POSITION": null, | ||
"SERIAL": "CC26012451", | ||
"LOCATION_ID": 15862309, | ||
"MACHINE_ID": null, | ||
"MACHINE_POSITION": 0, | ||
"ISVOICEDEFAULT": 1, | ||
"DEVICE_ID": "550e8400-e29b-41d4-a716-446655440000", | ||
"APPLIANCE_TYPE_ID": null, | ||
"STATUS": "CLAIMED", | ||
"IS_ENROLLED": null, | ||
"MACHINE_STATUS": null, | ||
"APPLIANCE_MODE": 2, | ||
"CREATED_AT": 1690565765000, | ||
"UPDATED_AT": 1690565765000, | ||
"DATA_MODEL_KEY": "DDM_LAUNDRY_VMAX20_WHIRLPOOL_WASHER8_V2", | ||
"CATEGORY_NAME": "FabricCare", | ||
"MODEL_NO": "WTW8127LW1", | ||
"REPLENISHMENT_DEVICE_MODEL": null | ||
} | ||
], | ||
"tsAppliances": null | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,53 @@ | ||
class BackendSelectorMock: | ||
from enum import Enum | ||
from typing import Dict, List | ||
|
||
from whirlpool.backendselector import BackendSelector | ||
|
||
|
||
class DummyBrand(Enum): | ||
DUMMY_BRAND = "dummy_brand" | ||
|
||
|
||
class DummyRegion(Enum): | ||
DUMMY_REGION = "dummy_region" | ||
|
||
|
||
class BackendSelectorMock(BackendSelector): | ||
def __init__(self): | ||
super().__init__(DummyBrand.DUMMY_BRAND, DummyRegion.DUMMY_REGION) | ||
|
||
@property | ||
def brand(self): | ||
return "dummy_brand" | ||
return DummyBrand.DUMMY_BRAND | ||
|
||
@property | ||
def region(self): | ||
return "dummy_region" | ||
return DummyRegion.DUMMY_REGION | ||
|
||
@property | ||
def base_url(self): | ||
return "http://dummy_base_url.com" | ||
|
||
@property | ||
def client_id(self): | ||
return "dummy_client_id" | ||
def client_credentials(self) -> List[Dict[str, str]]: | ||
return [ | ||
{ | ||
"client_id": "dummy_client_id1", | ||
"client_secret": "dummy_client_secret1", | ||
}, | ||
] | ||
|
||
|
||
class BackendSelectorMockMultipleCreds(BackendSelectorMock): | ||
@property | ||
def client_secret(self): | ||
return "dummy_client_secret" | ||
def client_credentials(self) -> List[Dict[str, str]]: | ||
return [ | ||
{ | ||
"client_id": "dummy_client_id1", | ||
"client_secret": "dummy_client_secret1", | ||
}, | ||
{ | ||
"client_id": "dummy_client_id2", | ||
"client_secret": "dummy_client_secret2", | ||
}, | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import asyncio | ||
|
||
import pytest | ||
|
||
from whirlpool.appliancesmanager import AppliancesManager | ||
from whirlpool.auth import Auth | ||
|
||
from .aiohttp import AiohttpClientMocker | ||
from .mock_backendselector import BackendSelectorMock | ||
from .utils import ( | ||
ACCOUNT_ID, | ||
get_mock_coro, | ||
mock_appliancesmanager_get_account_id_get, | ||
mock_appliancesmanager_get_owned_appliances_get, | ||
mock_appliancesmanager_get_shared_appliances_get, | ||
) | ||
|
||
BACKEND_SELECTOR_MOCK = BackendSelectorMock() | ||
|
||
|
||
def assert_appliances_manager_call( | ||
http_client_mock: AiohttpClientMocker, | ||
call_index: int, | ||
path: str, | ||
required_headers: dict = None, | ||
): | ||
mock_calls = http_client_mock.mock_calls | ||
|
||
call = mock_calls[call_index] | ||
assert call[0] == "GET" | ||
assert call[1].path == path | ||
# call[2] is body, which will be None | ||
|
||
if required_headers is not None: | ||
for k, v in required_headers.items(): | ||
assert call[3][k] == v | ||
|
||
|
||
@pytest.mark.parametrize("account_id", [None, ACCOUNT_ID]) | ||
async def test_fetch_appliances_with_set_account_id( | ||
account_id: str, http_client_mock: AiohttpClientMocker | ||
): | ||
get_appliances_idx = 0 if account_id is not None else 1 | ||
|
||
mock_appliancesmanager_get_account_id_get(http_client_mock, BACKEND_SELECTOR_MOCK) | ||
mock_appliancesmanager_get_owned_appliances_get( | ||
http_client_mock, BACKEND_SELECTOR_MOCK, ACCOUNT_ID | ||
) | ||
mock_appliancesmanager_get_shared_appliances_get( | ||
http_client_mock, BACKEND_SELECTOR_MOCK | ||
) | ||
|
||
http_client_mock.create_session(asyncio.get_event_loop()) | ||
auth = Auth(BACKEND_SELECTOR_MOCK, "email", "secretpass", http_client_mock.session) | ||
|
||
if account_id is not None: | ||
auth._auth_dict["accountId"] = account_id | ||
|
||
am = AppliancesManager(BACKEND_SELECTOR_MOCK, auth, http_client_mock.session) | ||
|
||
await am.fetch_appliances() | ||
|
||
if account_id is None: | ||
# make sure that the first call in this case is to get the account id | ||
assert_appliances_manager_call(http_client_mock, 0, "/api/v1/getUserDetails") | ||
|
||
# this should always be called | ||
assert_appliances_manager_call( | ||
http_client_mock, | ||
get_appliances_idx, | ||
f"/api/v2/appliance/all/account/{ACCOUNT_ID}", | ||
) | ||
|
||
# this should always be called and requires the WP-CLIENT-BRAND header | ||
assert_appliances_manager_call( | ||
http_client_mock, | ||
get_appliances_idx + 1, | ||
"/api/v1/share-accounts/appliances", | ||
{"WP-CLIENT-BRAND": "DUMMY_BRAND"}, | ||
) | ||
|
||
# ensure that the washer_dryers list is populated | ||
assert len(am.washer_dryers) == 2 | ||
|
||
await http_client_mock.close_session() | ||
|
||
|
||
@pytest.mark.parametrize( | ||
["owned_response", "shared_response"], | ||
[(True, True), (True, False), (False, True), (False, False)], | ||
) | ||
async def test_fetch_appliances_returns_true_if_either_method_returns_true( | ||
owned_response: bool, | ||
shared_response: bool, | ||
http_client_mock: AiohttpClientMocker, | ||
): | ||
mock_appliancesmanager_get_account_id_get(http_client_mock, BACKEND_SELECTOR_MOCK) | ||
mock_appliancesmanager_get_owned_appliances_get( | ||
http_client_mock, BACKEND_SELECTOR_MOCK, "12345" | ||
) | ||
mock_appliancesmanager_get_shared_appliances_get( | ||
http_client_mock, BACKEND_SELECTOR_MOCK | ||
) | ||
|
||
http_client_mock.create_session(asyncio.get_event_loop()) | ||
auth = Auth(BACKEND_SELECTOR_MOCK, "email", "secretpass", http_client_mock.session) | ||
|
||
am = AppliancesManager(BACKEND_SELECTOR_MOCK, auth, http_client_mock.session) | ||
am._get_shared_appliances = get_mock_coro(shared_response) | ||
am._get_owned_appliances = get_mock_coro(owned_response) | ||
|
||
result = await am.fetch_appliances() | ||
|
||
assert result == bool(owned_response or shared_response) | ||
await http_client_mock.close_session() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.