BnMcG / PyRobovac

Python library for controlling the Eufy RoboVac 11c
Apache License 2.0
27 stars 2 forks source link

Eufy Robovac 11c Status [Errno 9] Bad file descriptor #7

Open gsdevme opened 3 years ago

gsdevme commented 3 years ago

Hi,

I've been trying to debug why the Home Assistant component isn't working for the status call. The following code snippet works and the robot starts to do its cleaning

from robovac import Robovac, get_local_code

my_robovac = Robovac('xxx', 'xxx')

my_robovac.start_auto_clean()

However when I attempt to call my_robovac.get_status() the python execution always crashes and a bad file descriptor error seems to happen. I'm not that familiar with python but it would suggest the library is having some issue when trying to read data from the socket, it can send the data without any issue though.

from robovac import Robovac, get_local_code

my_robovac = Robovac('xxx', 'xxx')

my_robovac.get_status()
ERROR:root:[Errno 32] Broken pipe
Traceback (most recent call last):
  File "/home/gavin/.local/lib/python3.8/site-packages/robovac/robovac.py", line 325, in _send_packet
    self.s.send(encrypted_packet_data)
BrokenPipeError: [Errno 32] Broken pipe
Traceback (most recent call last):
  File "/home/gavin/.local/lib/python3.8/site-packages/robovac/robovac.py", line 325, in _send_packet
    self.s.send(encrypted_packet_data)
BrokenPipeError: [Errno 32] Broken pipe

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./test.py", line 6, in <module>
    my_robovac.get_status()
  File "/home/gavin/.local/lib/python3.8/site-packages/robovac/robovac.py", line 166, in get_status
    message = self._build_get_device_status_user_data_message()
  File "/home/gavin/.local/lib/python3.8/site-packages/robovac/robovac.py", line 293, in _build_get_device_status_user_data_message
    magic_number = self._get_magic_number()
  File "/home/gavin/.local/lib/python3.8/site-packages/robovac/robovac.py", line 309, in _get_magic_number
    pong = self._send_packet(ping, True)
  File "/home/gavin/.local/lib/python3.8/site-packages/robovac/robovac.py", line 329, in _send_packet
    self.connect()
  File "/home/gavin/.local/lib/python3.8/site-packages/robovac/robovac.py", line 155, in connect
    self.s.connect((self.ip, self.port))
OSError: [Errno 9] Bad file descriptor

I wondered if anyone had similar issues around this.

surskitt commented 2 years ago

I had a look at this today, looks like the problem lies in the _send_packet method. Looks like the socket that gets created along with the Robovac object gets closed and then it attempts to reuse it.

I made the changes below and things seem to work:

def _send_packet(self,
                     packet: LocalServerInfo_pb2.LocalServerMessage,
                     receive: True) -> Union[None, LocalServerInfo_pb2.LocalServerMessage]:
        """
        Send a packet to the RoboVac. This method handles all the required encryption.

        Will attempt to reconnect to the RoboVac if sending a packet fails.
        :param receive: If true, the packet sent in reply by the RoboVac will be parsed and returned.
        """
        raw_packet_data = packet.SerializeToString()
        encrypted_packet_data = _encrypt(raw_packet_data)

        try:
            self.s.send(encrypted_packet_data)
        except Exception as e:
            logging.exception(e)
            self.disconnect()
            self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # my change, creating a new socket here
            self.connect()
            self.s.send(encrypted_packet_data )

        if not receive:
            return None

        response_from_robovac = self.s.recv(1024)
        decrypted_response = _decrypt(response_from_robovac)
        return Robovac._parse_local_server_message_from_decrypted_response(decrypted_response)

This obviously isn't too clean though since the socket gets created at object creation and then created again in this function. Also might not be the best place to be creating the socket. I'll have a look at what might be a cleaner way of implementing it and make a PR (or just keep my own fork).

The fix is simple, I just need to make sure what the best way of including it is.