alexmohr / sonyapilib

Contains a python api to control sony devices.
MIT License
20 stars 11 forks source link

AttributeError: 'SonyDevice' object has no attribute 'uuid' #31

Closed dilruacs closed 5 years ago

dilruacs commented 5 years ago

I have a fresh copy of the v4 branch.

I tried to power on the device and got the following stacktrace:

2019-04-05 22:23:20 ERROR (Thread-21) [sonyapilib.device] HTTPError: HTTPConnectionPool(host='192.168.240.4', port=52323): Max retries exceeded with url: /dmr.xml (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f780830d780>: Failed to establish a new connection: [Errno 111] Connection refused',))
2019-04-05 22:23:20 ERROR (Thread-21) [sonyapilib.device] Failed to get DMR
2019-04-05 22:23:20 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection.140153510594880] 'SonyDevice' object has no attribute 'uuid'
Traceback (most recent call last):
  File "/srv/homeassistant/lib/python3.5/site-packages/homeassistant/components/websocket_api/commands.py", line 122, in handle_call_service
    connection.context(msg))
  File "/srv/homeassistant/lib/python3.5/site-packages/homeassistant/core.py", line 1138, in async_call
    self._execute_service(handler, service_call))
  File "/usr/lib/python3.5/asyncio/futures.py", line 380, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib/python3.5/asyncio/tasks.py", line 304, in _wakeup
    future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 293, in result
    raise self._exception
  File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
    result = coro.send(None)
  File "/srv/homeassistant/lib/python3.5/site-packages/homeassistant/core.py", line 1160, in _execute_service
    await handler.func(service_call)
  File "/srv/homeassistant/lib/python3.5/site-packages/homeassistant/helpers/entity_component.py", line 188, in handle_service
    self._platforms.values(), func, call, service_name
  File "/srv/homeassistant/lib/python3.5/site-packages/homeassistant/helpers/service.py", line 314, in entity_service_call
    future.result()  # pop exception if have
  File "/usr/lib/python3.5/asyncio/futures.py", line 293, in result
    raise self._exception
  File "/usr/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "/srv/homeassistant/lib/python3.5/site-packages/homeassistant/helpers/service.py", line 328, in _handle_service_platform_call
    await getattr(entity, func)(**data)
  File "/usr/lib/python3.5/asyncio/futures.py", line 380, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib/python3.5/asyncio/tasks.py", line 304, in _wakeup
    future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 293, in result
    raise self._exception
  File "/usr/lib/python3.5/concurrent/futures/thread.py", line 55, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/home/homeassistant/.homeassistant/custom_components/sony/media_player.py", line 251, in turn_on
    self.sonydevice.power(True, self._broadcast)
  File "/srv/homeassistant/lib/python3.5/site-packages/sonyapilib/device.py", line 597, in power
    self._send_command('Power')
  File "/srv/homeassistant/lib/python3.5/site-packages/sonyapilib/device.py", line 436, in _send_command
    self._init_device()
  File "/srv/homeassistant/lib/python3.5/site-packages/sonyapilib/device.py", line 112, in _init_device
    self._recreate_authentication()
  File "/srv/homeassistant/lib/python3.5/site-packages/sonyapilib/device.py", line 349, in _recreate_authentication
    self.headers['X-CERS-DEVICE-ID'] = self.get_device_id()
  File "/srv/homeassistant/lib/python3.5/site-packages/sonyapilib/device.py", line 506, in get_device_id
    return "TVSideView:{0}".format(self.uuid)
AttributeError: 'SonyDevice' object has no attribute 'uuid'

https://github.com/alexmohr/sonyapilib/blob/0d8548ed5fa22b574016e5d09822b35d613a8da3/sonyapilib/device.py#L99 At this point the attribute should be there, I do not understand why the error occurs.

alexmohr commented 5 years ago

This might happen if you used a configuration which existed before the uuid field has been added. It has been added in this commit: https://github.com/alexmohr/sonyapilib/commit/4f1e63ce6fad95888ea9b675d5011deafd562c6e ( 2 days ago ). This would explain the other issue you opened ( and I already closed again ) I would not implement something which updates the json config with new field because this only happens at development time.

schoenpat commented 5 years ago

@alexmohr Why not? So you limit yourself in the future to these variables. It could be done in a few lines.

alexmohr commented 5 years ago

Because it adds unnecessary complexity for something which is most likely not needed. One other field is necessary for the device is necessary which is one for the psk. One valid approach would be to only save necessary fields to the json:

All other data can be read from the device at startup but I rather persist other fields as well ( as it is today )

The current approach does not require to know which fields are relevant for the authentication and communication with the device and I want to keep it that way.

dilruacs commented 5 years ago

If the remote commands aren't persisted, there is no way to know what command needs to be send to power it on (if this is possible at all, I only have a WOL capable device)

On the other hand: if a future version of the api requires a new field to be persisted, it would be great if this is possible without deleting the json and "pairing" the device from scratch.

dilruacs commented 5 years ago

wouldn't it be better to just store the API version number instead of a "is_v4" flag?

alexmohr commented 5 years ago

I've updated the comment above to reflect your answer. Implementing this is currently not on my agenda because I believe it won't be necessary because after finishing the v4 and psk authentication new fields will probably only added for api version 5 ( if sony releases something like that ). Altough I might be wrong and we'll need more fields before that. Keep in mind that v4 is currently still a development branch and I cannot guarantee not to break things because I'm not testing every commit with a device. This will be done before this branch is merged on dev. But I am very happy about the feedback.

The main problem I have with this idea is that the serialized json still would not contain a newly added field and would be initalized with some kind of default value. This would work with things like the uuid but must values I can think of will a value which depends on something meaning if this is the case some kind of configuration file migration must be done anyways.

If any of you has a good idea on how to implement that and account for the problems described I'd be happy to accept a PR. This should also contain a test validating that the serialized data contains all valid fields. Take a look at the previous comment by me to see the necessary values.

wouldn't it be better to just store the API version number instead of a "is_v4" flag?

Yes it would be, I'm going replace the flag with the version number.

schoenpat commented 5 years ago

My idea was that you load the device instance from the json (jsonpickle). Then you create a new SonyDevice instance with host and nickname from the loaded instance. Compare the attributes of both instances and copy the missing ones from the new instance to the loaded one. So you cover all attributes that will be set in the init

alexmohr commented 5 years ago

If the attributes are compared without the need to list them explicitly this could be an elegant solution. But it would lead to running the init_device method at every startup, it should be made sure that this works if the device is not powered on. If a difference is detected the stored json must be updated because, in this uuid example, a new uuid would be generated each time leading to breaking the authentication. Beside that even if this uuid would have been persisted in the json the pairing had to done again because when I added this I also changed how the device id is generated meaning the sony device wouldn't be able to match it with the paired configuration due to the id missmatch and a new pairing is neccessary anyway. If you are able to implement this in "a few lines" go ahead but I cannot come up with a scenario where this is really neccessary ;)