abmantis / whirlpool-sixth-sense Goto Github PK
View Code? Open in Web Editor NEWWhirlpool unofficial API for 6th Sense appliances
License: MIT License
Whirlpool unofficial API for 6th Sense appliances
License: MIT License
I am trying to deploy this with HA and have looked through the initial supported Whirlpool Appliances but am unable to. How do I deploy this? I can't pull this through HACS so I think I'm missing something to connect the dots.
Can the readme be updated with better documentation or can someone help me and I can pull to update the readme after I have it deployed.
Thanks for building this library. I have a feature request. I could code it and submit a PR with a little guidance.
My request is for a Washer "wet laundry" sensor in Home Assistant. This is when the washer cycle is complete, but the washer door has not been opened. This can tell or remind me that someone needs to move the wet clothes into the dryer.
I assume I'd create a function WasherDryer.is_laundry_wet()
that returns a boolean.
Should I add a self._last_status and set it in get_machine_state
, and then
def is_laundry_wet():
return (self._last_status == MachineState.Complete and self._door_status == Closed)
Or do you have a better suggestion ?
Hey there!
As I started writing an integration for the oven (#6), I noticed that -- at least for me, it appears that logins are dependent on region, not on brand. Here in the US, we get both Maytag and Whirlpool brand appliances (my oven is Whirlpool) but it appears auth is not brand (only?) dependent, but region dependent. Trying auth to https://api.whrcloud.eu
gives a 400 error even with otherwise correct credentials for a Whirlpool-brand (oven) appliance for a US based account. Using the Maytag brand (-b maytag
) does auth the US based account, but the brand/url base are then wrong. I don't have any other devices to test whether the brand matters or just the region, but the region definitely does.
I think we at least need to introduce a region parameter. WDYT?
Current eventsocket code does not restart after 3 connection attempts and does not log the condition - results in a dead eventsocket task.
For a long lived eventsocket, it should continue to retry forever. At a minimum it should reset the retry count after a successful connection, and log when it stops trying.
#13 has retry counter removed.
Running the client in the US - all aiohttp calls fail the cert verification.
I have temporarily added verify_ssl = False to all .get and .post just to verify other functions are working - so far events are now connected an working. Not exactly sure how else to get around this issue.
After some testing, no event based messages were appearing in the cli.py test (as well as HomeAssistant). Looks like eventsocket.py didn't get the additions for backend selection and only connects to: websocketservice.wcloud-emea.eu-gb.containers.appdomain.cloud
There probably is a US version?
It would be great if Maytag washers and dryers located in the United States could be accessed using this. I can access the machines using the Maytag phone app, but not via whirlpool-sixth-sense (I get an "Invalid authentication" error when using the credentials that work for the phone app). I'm not a programmer and can't help programming, but can help as a user for testing.
When the eventsocket is disconnected then reconnects, there is no mechanism to update the data for missed events.
Whenever the eventsocket reconnects, it should call fetch_data (or something like it) to get a fresh _data_dict and trigger a callback to HA to cover any missed events while the socket was "away".
There is something wrong with the MockResponse and it's async context handler..
❯ pytest
==================================================================================== test session starts ====================================================================================
platform linux -- Python 3.10.6, pytest-7.1.3, pluggy-1.0.0
rootdir: /tmp/whirlpool-sixth-sense
plugins: asyncio-0.19.0, mock-3.8.2
asyncio: mode=strict
collected 6 items
tests/test_aircon.py FF [ 33%]
tests/test_auth.py .. [ 66%]
tests/test_oven.py FF [100%]
========================================================================================= FAILURES ==========================================================================================
______________________________________________________________________________________ test_attributes ______________________________________________________________________________________
caplog = <_pytest.logging.LogCaptureFixture object at 0x7efc915db0d0>, aio_httpclient = <MagicMock name='ClientSession()' id='139623235687744'>
async def test_attributes(caplog, aio_httpclient):
caplog.set_level(logging.DEBUG)
auth = MagicMock()
aio_httpclient.get.return_value = MockResponse(json.dumps(DATA1), 200)
aircon = Aircon(BackendSelectorMock(), auth, SAID, None)
> await aircon.connect()
tests/test_aircon.py:100:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
whirlpool/appliance.py:142: in connect
await self.start_event_listener()
whirlpool/appliance.py:161: in start_event_listener
await self._getWebsocketUrl(),
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <whirlpool.aircon.Aircon object at 0x7efc90d7da20>
async def _getWebsocketUrl(self):
async with aiohttp.ClientSession(headers=self._create_headers()) as session:
DEFAULT_WS_URL = "wss://ws.emeaprod.aws.whrcloud.com/appliance/websocket"
> async with session.get(
f"{self._backend_selector.base_url}/api/v1/client_auth/webSocketUrl"
) as r:
E AttributeError: __aenter__
whirlpool/appliance.py:67: AttributeError
_______________________________________________________________________________________ test_setters ________________________________________________________________________________________
caplog = <_pytest.logging.LogCaptureFixture object at 0x7efc90c8fb20>, aio_httpclient = <MagicMock name='ClientSession()' id='139623225939504'>
async def test_setters(caplog, aio_httpclient):
caplog.set_level(logging.DEBUG)
auth = MagicMock()
aio_httpclient.get.return_value = MockResponse(json.dumps(DATA1), 200)
aio_httpclient.post.return_value = MockResponse("", 200)
cmd_data = {
"header": {"said": SAID, "command": "setAttributes"},
}
aircon = Aircon(BackendSelectorMock(), auth, SAID, None)
> await aircon.connect()
tests/test_aircon.py:150:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
whirlpool/appliance.py:142: in connect
await self.start_event_listener()
whirlpool/appliance.py:161: in start_event_listener
await self._getWebsocketUrl(),
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <whirlpool.aircon.Aircon object at 0x7efc90c04580>
async def _getWebsocketUrl(self):
async with aiohttp.ClientSession(headers=self._create_headers()) as session:
DEFAULT_WS_URL = "wss://ws.emeaprod.aws.whrcloud.com/appliance/websocket"
> async with session.get(
f"{self._backend_selector.base_url}/api/v1/client_auth/webSocketUrl"
) as r:
E AttributeError: __aenter__
whirlpool/appliance.py:67: AttributeError
______________________________________________________________________________________ test_attributes ______________________________________________________________________________________
caplog = <_pytest.logging.LogCaptureFixture object at 0x7efc90ca97e0>, aio_httpclient = <MagicMock name='ClientSession()' id='139623225719248'>
async def test_attributes(caplog, aio_httpclient):
caplog.set_level(logging.DEBUG)
auth = MagicMock()
aio_httpclient.get.return_value = MockResponse(json.dumps(DATA1), 200)
oven = Oven(BackendSelectorMock(), auth, SAID, None)
> await oven.connect()
tests/test_oven.py:771:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
whirlpool/appliance.py:142: in connect
await self.start_event_listener()
whirlpool/appliance.py:161: in start_event_listener
await self._getWebsocketUrl(),
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <whirlpool.oven.Oven object at 0x7efc90c7a8f0>
async def _getWebsocketUrl(self):
async with aiohttp.ClientSession(headers=self._create_headers()) as session:
DEFAULT_WS_URL = "wss://ws.emeaprod.aws.whrcloud.com/appliance/websocket"
> async with session.get(
f"{self._backend_selector.base_url}/api/v1/client_auth/webSocketUrl"
) as r:
E AttributeError: __aenter__
whirlpool/appliance.py:67: AttributeError
_______________________________________________________________________________________ test_setters ________________________________________________________________________________________
caplog = <_pytest.logging.LogCaptureFixture object at 0x7efc90d07c70>, aio_httpclient = <MagicMock name='ClientSession()' id='139623226441104'>
async def test_setters(caplog, aio_httpclient):
caplog.set_level(logging.DEBUG)
auth = MagicMock()
aio_httpclient.get.return_value = MockResponse(json.dumps(DATA2), 200)
aio_httpclient.post.return_value = MockResponse("", 200)
cmd_data = {
"header": {"said": SAID, "command": "setAttributes"},
}
oven = Oven(BackendSelectorMock(), auth, SAID, None)
> await oven.connect()
tests/test_oven.py:836:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
whirlpool/appliance.py:142: in connect
await self.start_event_listener()
whirlpool/appliance.py:161: in start_event_listener
await self._getWebsocketUrl(),
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <whirlpool.oven.Oven object at 0x7efc90c94670>
async def _getWebsocketUrl(self):
async with aiohttp.ClientSession(headers=self._create_headers()) as session:
DEFAULT_WS_URL = "wss://ws.emeaprod.aws.whrcloud.com/appliance/websocket"
> async with session.get(
f"{self._backend_selector.base_url}/api/v1/client_auth/webSocketUrl"
) as r:
E AttributeError: __aenter__
whirlpool/appliance.py:67: AttributeError
===================================================================================== warnings summary ======================================================================================
tests/test_aircon.py::test_attributes
tests/test_aircon.py::test_setters
tests/test_oven.py::test_attributes
tests/test_oven.py::test_setters
/tmp/whirlpool-sixth-sense/whirlpool/appliance.py:67: RuntimeWarning: coroutine 'AsyncMockMixin._execute_mock_call' was never awaited
async with session.get(
Enable tracemalloc to get traceback where the object was allocated.
See https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings for more info.
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================================================================== short test summary info ==================================================================================
FAILED tests/test_aircon.py::test_attributes - AttributeError: __aenter__
FAILED tests/test_aircon.py::test_setters - AttributeError: __aenter__
FAILED tests/test_oven.py::test_attributes - AttributeError: __aenter__
FAILED tests/test_oven.py::test_setters - AttributeError: __aenter__
========================================================================== 4 failed, 2 passed, 4 warnings in 0.25s ==========================================================================
First of all congrats for this project @abmantis. I need your help please. The official whirlpool 6 sense integration doesn't work properly, how do I install this library in Homeassistant?
when a release is built for Pypi.
After some "time" the auth key expires. Once the eventsocket becomes disconnected and can never connect again as the auth key is statically passed to the event socket.
#13 includes one possible solution.
Thanks for creating this project! I was in the middle of creating a similar one when I saw yours. I have a Whirlpool smart oven that uses the same auth flow/APIs. Would you accept a pull request that adds smart oven support?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.