Coder Social home page Coder Social logo

mathieu-mp / aio-intex-spa Goto Github PK

View Code? Open in Web Editor NEW
17.0 17.0 2.0 70 KB

Python client for Intex Spa wifi interface

Home Page: https://pypi.org/project/aio-intex-spa/

License: MIT License

Python 99.23% Shell 0.77%
asyncio intex intexspa purespa spa wifi

aio-intex-spa's People

Contributors

dependabot[bot] avatar ludeeus avatar mathieu-mp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

aio-intex-spa's Issues

Cannot connect to spa - errno 104

Version of the custom_component

cd27a20

Describe the bug

After power outtage, i cannot send commands/update state of spa. All entities are available, but when i try to change something, it says "errno 104".
I tried to capture packets using tshark, but there are no packet generated

Debug log


This error originated from a custom integration.

Logger: homeassistant
Source: custom_components/intex_spa/__init__.py:71
Integration: Intex Spa (documentation, issues)
First occurred: 11:45:59 AM (213 occurrences)
Last logged: 1:31:59 PM

Error doing job: Task exception was never retrieved
Traceback (most recent call last):
  File "/config/custom_components/intex_spa/__init__.py", line 67, in _async_update_data
    return await self.api.async_update_status()
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa.py", line 144, in async_update_status
    return await self._async_handle_intent("status")
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa.py", line 123, in _async_handle_intent
    await self.network.async_force_reconnect()
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa_network_layer.py", line 82, in async_force_reconnect
    await self._async_disconnect()
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa_network_layer.py", line 74, in _async_disconnect
    await self.writer.wait_closed()
  File "/usr/local/lib/python3.10/asyncio/streams.py", line 344, in wait_closed
    await self._protocol._get_close_waiter(self)
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa.py", line 101, in _async_handle_intent
    received_bytes = await self.network.async_receive()
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa_network_layer.py", line 100, in async_receive
    response_as_bytes = await self.reader.readline()
  File "/usr/local/lib/python3.10/asyncio/streams.py", line 525, in readline
    line = await self.readuntil(sep)
  File "/usr/local/lib/python3.10/asyncio/streams.py", line 617, in readuntil
    await self._wait_for_data('readuntil')
  File "/usr/local/lib/python3.10/asyncio/streams.py", line 502, in _wait_for_data
    await self._waiter
  File "/usr/local/lib/python3.10/asyncio/selector_events.py", line 854, in _read_ready__data_received
    data = self._sock.recv(self.max_size)
ConnectionResetError: [Errno 104] Connection reset by peer

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 151, in _handle_refresh_interval
    await self._async_refresh(log_failures=True, scheduled=True)
  File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 258, in _async_refresh
    raise err
  File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 205, in _async_refresh
    self.data = await self._async_update_data()
  File "/config/custom_components/intex_spa/__init__.py", line 71, in _async_update_data
    raise NotImplementedError from exception
NotImplementedError


shortened this, cannot fit to 65k characters


Logger: homeassistant.components.websocket_api.http.connection
Source: custom_components/intex_spa/__init__.py:67
Integration: Home Assistant WebSocket API (documentation, issues)
First occurred: 1:18:29 PM (9 occurrences)
Last logged: 1:28:09 PM

[140663695691392] [Errno 104] Connection reset by peer
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/websocket_api/commands.py", line 193, in handle_call_service
    await hass.services.async_call(
  File "/usr/src/homeassistant/homeassistant/core.py", line 1713, in async_call
    task.result()
  File "/usr/src/homeassistant/homeassistant/core.py", line 1750, in _execute_service
    await cast(Callable[[ServiceCall], Awaitable[None]], handler.job.target)(
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 204, in handle_service
    await service.entity_service_call(
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 680, in entity_service_call
    future.result()  # pop exception if have
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 930, in async_request_call
    await coro
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 717, in _handle_entity_call
    await result
  File "/config/custom_components/intex_spa/switch.py", line 93, in async_turn_off
    status = await self.coordinator.api.async_set(self._switch_type, False)
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa.py", line 157, in async_set
    return await self._async_handle_intent(parameter, expected_state)
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa.py", line 77, in _async_handle_intent
    await self.async_update_status()
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa.py", line 144, in async_update_status
    return await self._async_handle_intent("status")
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa.py", line 123, in _async_handle_intent
    await self.network.async_force_reconnect()
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa_network_layer.py", line 82, in async_force_reconnect
    await self._async_disconnect()
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa_network_layer.py", line 74, in _async_disconnect
    await self.writer.wait_closed()
  File "/usr/local/lib/python3.10/asyncio/streams.py", line 344, in wait_closed
    await self._protocol._get_close_waiter(self)
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa.py", line 98, in _async_handle_intent
    await self.network.async_send(query.request_bytes)
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa_network_layer.py", line 91, in async_send
    await self._async_disconnect()
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa_network_layer.py", line 74, in _async_disconnect
    await self.writer.wait_closed()
  File "/usr/local/lib/python3.10/asyncio/streams.py", line 344, in wait_closed
    await self._protocol._get_close_waiter(self)
  File "/config/custom_components/intex_spa/__init__.py", line 67, in _async_update_data
    return await self.api.async_update_status()
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa.py", line 144, in async_update_status
    return await self._async_handle_intent("status")
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa.py", line 123, in _async_handle_intent
    await self.network.async_force_reconnect()
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa_network_layer.py", line 82, in async_force_reconnect
    await self._async_disconnect()
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa_network_layer.py", line 74, in _async_disconnect
    await self.writer.wait_closed()
  File "/usr/local/lib/python3.10/asyncio/streams.py", line 344, in wait_closed
    await self._protocol._get_close_waiter(self)

...

  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa_network_layer.py", line 82, in async_force_reconnect
    await self._async_disconnect()
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa_network_layer.py", line 74, in _async_disconnect
    await self.writer.wait_closed()
  File "/usr/local/lib/python3.10/asyncio/streams.py", line 344, in wait_closed
    await self._protocol._get_close_waiter(self)
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa.py", line 101, in _async_handle_intent
    received_bytes = await self.network.async_receive()
  File "/usr/local/lib/python3.10/site-packages/intex_spa/intex_spa_network_layer.py", line 100, in async_receive
    response_as_bytes = await self.reader.readline()
  File "/usr/local/lib/python3.10/asyncio/streams.py", line 525, in readline
    line = await self.readuntil(sep)
  File "/usr/local/lib/python3.10/asyncio/streams.py", line 617, in readuntil
    await self._wait_for_data('readuntil')
  File "/usr/local/lib/python3.10/asyncio/streams.py", line 502, in _wait_for_data
    await self._waiter
  File "/usr/local/lib/python3.10/asyncio/selector_events.py", line 854, in _read_ready__data_received
    data = self._sock.recv(self.max_size)
ConnectionResetError: [Errno 104] Connection reset by peer


Filtering turns on for 2h only

Hey!

as i discovered that original intex app scheduling was somewhat flaky (sometimes it worked other times not) i decided to implement scheduling of filter running thrugh my local HA.
However i have noticed that sending ON command for filtering turns it on for 2 hours and it shuts off automatically after that (the display shows 02h when sending filter on command).

in contrast turning filtering on the control panel offers 2,4 or 6h running time.

Is it possible that there is also different remote command for running time and could be implemented?

thx

Parse error from status

Hi, i wanted to see, if error is presented in code returned as status.
My SPA now says , that it has E81 (no power) on display, and

DEBUG:intex_spa.intex_spa_network_layer:Receiving bytes from the spa: b'{"sid":"16555066810742","data":"FFFF110F010001B5000000008080801B00008B","result":"ok","type":2}\n'
DEBUG:intex_spa.intex_spa_status:Spa status: '{'power': False, 'filter': False, 'heater': False, 'jets': False, 'bubbles': False, 'sanitizer': False, 'unit': '°C', 'current_temp': 181, 'preset_temp': 27}'

after restoring power

DEBUG:intex_spa.intex_spa_network_layer:Receiving bytes from the spa: b'{"sid":"16555066641209","data":"FFFF110F01000016000000008080801B00002C","result":"ok","type":2}\n'
DEBUG:intex_spa.intex_spa_status:Spa status: '{'power': False, 'filter': False, 'heater': False, 'jets': False, 'bubbles': False, 'sanitizer': False, 'unit': '°C', 'current_temp': 22, 'preset_temp': 27}'

so the error is mixed in the current_temp.
Don't have time to debug now, just reporting

JSONDecodeError to catch when response cannot be received

JSONDecodeError to catch when response cannot be received

2022-06-20 17:17:39 ERROR (MainThread) [custom_components.intex_spa] Error fetching intex_spa data: 
2022-06-20 17:18:09 ERROR (MainThread) [homeassistant.core] Error executing service: <ServiceCall switch.turn_on (c:01G611X014FMMB0MVRR50BTW7K): entity_id=['switch.spa_power']>
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/core.py", line 1722, in catch_exceptions
    await coro_or_task
  File "/usr/src/homeassistant/homeassistant/core.py", line 1741, in _execute_service
    await cast(Callable[[ServiceCall], Awaitable[None]], handler.job.target)(
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 204, in handle_service
    await service.entity_service_call(
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 680, in entity_service_call
    future.result()  # pop exception if have
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 964, in async_request_call
    await coro
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 717, in _handle_entity_call
    await result
  File "/config/custom_components/intex_spa/switch.py", line 86, in async_turn_on
    status = await self.coordinator.api.async_set(self._switch_type, True)
  File "/usr/local/lib/python3.9/site-packages/intex_spa/intex_spa.py", line 152, in async_set
    return await self._async_handle_intent(parameter, expected_state)
  File "/usr/local/lib/python3.9/site-packages/intex_spa/intex_spa.py", line 83, in _async_handle_intent
    await self.async_update_status()
  File "/usr/local/lib/python3.9/site-packages/intex_spa/intex_spa.py", line 139, in async_update_status
    return await self._async_handle_intent("status")
  File "/usr/local/lib/python3.9/site-packages/intex_spa/intex_spa.py", line 106, in _async_handle_intent
    query.render_response_status(received_bytes)
  File "/usr/local/lib/python3.9/site-packages/intex_spa/intex_spa_query.py", line 87, in render_response_status
    response = json.loads(received_bytes.decode())
  File "/usr/local/lib/python3.9/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
  File "/usr/local/lib/python3.9/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/local/lib/python3.9/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

I added firewall rule between my dev host and SPA. Then switched the power switch. First line of log pops up almost instatly, exception shows after 30s timeout. My firewall is set to REJECT packets, not to DROP

Originally posted by @Elkropac in mathieu-mp/homeassistant-intex-spa#10 (comment)

Handle simultaneous requests

Describe the bug
The package does not support multiple simultaneous requests.

Exception is raised

log from Home Assistant

2022-06-20 16:33:43 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection] [139669599812720] readuntil() called while another coroutine is already waiting for incoming data
Traceback (most recent call last):
  File "/usr/local/python/lib/python3.10/site-packages/homeassistant/components/websocket_api/commands.py", line 193, in handle_call_service
    await hass.services.async_call(
  File "/usr/local/python/lib/python3.10/site-packages/homeassistant/core.py", line 1706, in async_call
    task.result()
  File "/usr/local/python/lib/python3.10/site-packages/homeassistant/core.py", line 1743, in _execute_service
    await cast(Callable[[ServiceCall], Awaitable[None]], handler.job.target)(
  File "/usr/local/python/lib/python3.10/site-packages/homeassistant/helpers/entity_component.py", line 204, in handle_service
    await service.entity_service_call(
  File "/usr/local/python/lib/python3.10/site-packages/homeassistant/helpers/service.py", line 680, in entity_service_call
    future.result()  # pop exception if have
  File "/usr/local/python/lib/python3.10/site-packages/homeassistant/helpers/entity.py", line 900, in async_request_call
    await coro
  File "/usr/local/python/lib/python3.10/site-packages/homeassistant/helpers/service.py", line 717, in _handle_entity_call
    await result
  File "/config/custom_components/intex_spa/switch.py", line 87, in async_turn_off
    status = await self.coordinator.api.async_set(self._switch_type, False)
  File "/usr/local/python/lib/python3.10/site-packages/intex_spa/intex_spa.py", line 152, in async_set
    return await self._async_handle_intent(parameter, expected_state)
  File "/usr/local/python/lib/python3.10/site-packages/intex_spa/intex_spa.py", line 83, in _async_handle_intent
    await self.async_update_status()
  File "/usr/local/python/lib/python3.10/site-packages/intex_spa/intex_spa.py", line 139, in async_update_status
    return await self._async_handle_intent("status")
  File "/usr/local/python/lib/python3.10/site-packages/intex_spa/intex_spa.py", line 104, in _async_handle_intent
    received_bytes = await self.network.async_receive()
  File "/usr/local/python/lib/python3.10/site-packages/intex_spa/intex_spa_network_layer.py", line 82, in async_receive
    response_as_bytes = await self.reader.readline()
  File "/usr/local/python/lib/python3.10/asyncio/streams.py", line 525, in readline
    line = await self.readuntil(sep)
  File "/usr/local/python/lib/python3.10/asyncio/streams.py", line 617, in readuntil
    await self._wait_for_data('readuntil')
  File "/usr/local/python/lib/python3.10/asyncio/streams.py", line 488, in _wait_for_data
    raise RuntimeError(
RuntimeError: readuntil() called while another coroutine is already waiting for incoming data

Question: Domoticz plugin

Hello, (thanks google translation, I'm French without great English skills!), I would like to know if your plugin could be installed on a raspberry with Domoticz? If so, could you show me how to do it? Where to start ? knowing that I am not an expert like you but a simple guy who would like to enjoy his spa via his home automation!
Thank you for your understanding.
Sincerely

ps: I hope not to disturb the project, I did not know how to contact you.

Get battery info

Hi Mathieu,

Nice job.
Jeedom integration done and quite easy.

Will it be possible to get battery info at some point?

Cheers,
Patrice

Checksum algorithm fails on 0xFF legitimate checksum

I have a cron job running that records the temperature and heats overnight. I am consistently getting exceptions thrown for a period because the checksum doesn't match.

I took the output from the exception below and ran it through the checksum as int function and it came back as zero, as you can see the spa returned it as 0xFF. If this is a legitimate issue, I am surprised it hasn't come up before although I am using Farenheit. Calling the function and ignoring the assertion for the assertion for the checksum the status below is reported. The spa I have is an SC-WF10.
I tried replicating the issue today with the same settings but was unable to get the code to fail. I have posted the failing bytes returned and the passing bytes returned below:

FFFF110F0107006400000085808085670000FF
FFFF110F01070064000000008080806700008A
{'power': True, 'filter': True, 'heater': True, 'jets': False, 'bubbles': False, 'sanitizer': False, 'unit': '°F', 'current_temp': 100, 'preset_temp': 103, 'error_code': False}

The bolded section is different, between the preset temperature and current temperature. I tried finding the intex protocol but I'm guessing it is by trial and error.  I'm not sure what is correcting the issue - it may be a scheduled event from the app that causes the temperature to update and a different checksum?
The only thing I found about 0 and FF checksums was in relation to UDP pstavirs/ostinato#160
I'm not sure how to work around this as I understand that every command issues a status to begin with? I have tried executing an async_update_info to see if that changes anything. 

Initializing IntexSpa instance...
IntexSpa instance initialized
'status' intent: Handling new intent...
'status' intent: a spa query is needed
'status' intent: new spa query...
Not connected to the spa, trying to connect...
Opening TCP connection with the spa at SPA_DEVICE:8990 with asyncio...
TCP connection established with the spa
Sending bytes to the spa: b'{"data": "8888060FEE0F01DA", "sid": "16877683219221", "type": 1}'
Receiving bytes from the spa: b'{"sid":"16877683219221","data":"FFFF110F0107006400000086808086680000FC","result":"ok","type":2}\n'
'status' intent: new status is rendered
'status' intent: Handling new intent...
'status' intent: a spa query is needed
'status' intent: new spa query...
Sending bytes to the spa: b'{"data": "8888060FEE0F01DA", "sid": "16877683220962", "type": 1}'
Receiving bytes from the spa: b'{"sid":"16877683220962","data":"FFFF110F0107006400000086808086680000FC","result":"ok","type":2}\n'
'status' intent: new status is rendered
'preset_temp' intent: Handling new intent...
'preset_temp' intent: triggering a preliminary 'update' intent...
'status' intent: Handling new intent...
'status' intent: a spa query is needed
'status' intent: new spa query...
Sending bytes to the spa: b'{"data": "8888060FEE0F01DA", "sid": "16877683221893", "type": 1}'
Receiving bytes from the spa: b'{"sid":"16877683221893","data":"FFFF110F0107006400000086808086680000FC","result":"ok","type":2}\n'
'status' intent: new status is rendered
'preset_temp' intent: a spa query is needed
'preset_temp' intent: new spa query...
Sending bytes to the spa: b'{"data": "8888050F0C6767", "sid": "16877683222765", "type": 1}'
Receiving bytes from the spa: b'{"sid":"16877683222765","data":"FFFF110F0107006400000085808085670000FF","result":"ok","type":2}\n'
Malformed spa response during spa querying
'preset_temp' intent: new spa query...
Sending bytes to the spa: b'{"data": "8888050F0C6767", "sid": "16877683247193", "type": 1}'
Receiving bytes from the spa: b'{"sid":"16877683247193","data":"FFFF110F0107006400000085808085670000FF","result":"ok","type":2}\n'
Malformed spa response during spa querying
'preset_temp' intent: new spa query...
Sending bytes to the spa: b'{"data": "8888050F0C6767", "sid": "16877683270894", "type": 1}'
Receiving bytes from the spa: b'{"sid":"16877683270894","data":"FFFF110F0107006400000085808085670000FF","result":"ok","type":2}\n'
Malformed spa response during spa querying
Spa is unreachable

MQTT implementation

Awesome work @mathieu-mp !

Compared to intex app which if flaky and moody, this is straight forward and works without a hitch.
I was wondering if there is any chance to implement MQTT support as this would make this solution even more universal.

Request version info from device

This string is sent by android app during pairing

{"data":"","sid":"1654467840319","type":3}

SPA responds with

{"sid":"1654467840319","data":"{\"ip\":\"192.168.x.x\",\"uid\":\"0K040210272020102000008062\",\"dtype\":\"spa\"}","result":"ok","type":3}

We found out here
https://community.home-assistant.io/t/intex-pure-spa-wifi-control/323591/25
that last four numbers in uid are probably device model - my app says i have Bubble SPA V28062

Could you please implement this query in your lib?

Thanks

Stale temperature when heating (python module)

I tried looking to see if this is documented elsewhere, but didn't find it.
With no heating, it seems the current temperature is correctly reported with the status message, at least it changes over time.
However, when the heater is on it doesn't report updated temperatures. I noticed this on the intex app and had to force refresh it to give me an updated temperature. Today after leaving it heating overnight it still had the same temperature. The one way I found to force it to update was by changing the preset_temperature. It seemed that I had to set a different temperature for it to actually update the current temperature.

Am I missing something? I tried the three example get status scripts and info. before trying to change the temperature. Maybe it's my model (SC-WF10)? Anyway, it seems to be working now with making the call to change the preset temperature.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.