Library no longer functions for websocket commands since firmware update. "Invalid opcode"
My Samsung M series TV updated to firmware 1250 last night. They must have changed the websocket protocol because now this library and every other 3rd party Samsung app no longer works.
Traceback (most recent call last):
File "C:/Users/tomer/PycharmProjects/samsung/main.py", line 15, in
Here is the crash although it most likely isn't very helpful. I will work on reverse engineering the official Samsung smart view app tomorrow which still works fine.
I got a NU8000 recently and even SmartView app doesn't work (It doesn't find the TV). Only Smartthings seems to find and be able to control the TV with wifi...
SamsungCTL gives the {u'event': u'ms.channel.unauthorized'} error.
Same issue, occurred after TV firmware update. root@raspberrypi:~/AlexaControlledSamsungTV# /usr/bin/python3 /root/AlexaControlledSamsungTV/alexasmartcli.py start -m starting server... server running. Pres CTRL + C to stop Failed to send message to TV: Invalid close opcode.
'tv_model' : 'UN82MU8000',
root@raspberrypi:~/AlexaControlledSamsungTV# uname -a Linux raspberrypi 4.9.80-v7+ #1098 SMP Fri Mar 9 19:11:42 GMT 2018 armv7l GNU/Linux
root@raspberrypi:~/AlexaControlledSamsungTV# samsungctl --version samsungctl 0.7.1
Ran into the same issue yesterday morning. Tried a number of ways and it just wouldn't go.
I'll add a "me too" to this one. *New user, so I can't confirm if it ever worked. New install, fresh out of the box TV, model UN32N5300. Dump from "api/v2/":
{"device":{"FrameTVSupport":"false","GamePadSupport":"true","ImeSyncedSupport":"true","OS":"Tizen","TokenAuthSupport":"true","VoiceSupport":"false","countryCode":"US","description":"Samsung DTV RCR","developerIP":"0.0.0.0","developerMode":"0","duid":"uuid:77298b13-353d-4849-85bf-2267744cf696","firmwareVersion":"Unknown","id":"uuid:77298b13-353d-4849-85bf-2267744cf696","ip":"192.168.0.150","model":"18_KANTS_FHD","modelName":"UN32N5300","name":"[TV] Samsung 5 Series (32)","networkType":"wired","resolution":"1920x1080","smartHubAgreement":"true","type":"Samsung SmartTV","udn":"uuid:77298b13-353d-4849-85bf-2267744cf696","wifiMac":"c0:48:e6:d7:c2:5d"},"id":"uuid:77298b13-353d-4849-85bf-2267744cf696","isSupport":"{\"DMP_DRM_PLAYREADY\":\"false\",\"DMP_DRM_WIDEVINE\":\"false\",\"DMP_available\":\"true\",\"EDEN_available\":\"true\",\"FrameTVSupport\":\"false\",\"ImeSyncedSupport\":\"true\",\"TokenAuthSupport\":\"true\",\"remote_available\":\"true\",\"remote_fourDirections\":\"true\",\"remote_touchPad\":\"true\",\"remote_voiceControl\":\"false\"}\n","name":"[TV] Samsung 5 Series (32)","remote":"1.0","type":"Samsung SmartTV","uri":"http://192.168.0.150:8001/api/v2/","version":"2.0.25"}
And the error:
[19:10:57] openhabian@openHABianPi:~$ python3 -m samsungctl --host 192.168.0.150 --port 8001 --method websocket Traceback (most recent call last): File "/usr/lib/python3.4/runpy.py", line 170, in _run_module_as_main "__main__", mod_spec) File "/usr/lib/python3.4/runpy.py", line 85, in _run_code exec(code, run_globals) File "/usr/local/lib/python3.4/dist-packages/samsungctl/__main__.py", line 133, in <module> main() File "/usr/local/lib/python3.4/dist-packages/samsungctl/__main__.py", line 110, in main with Remote(config) as remote: File "/usr/local/lib/python3.4/dist-packages/samsungctl/remote.py", line 11, in __init__ self.remote = RemoteWebsocket(config) File "/usr/local/lib/python3.4/dist-packages/samsungctl/remote_websocket.py", line 30, in __init__ self._read_response() File "/usr/local/lib/python3.4/dist-packages/samsungctl/remote_websocket.py", line 72, in _read_response raise exceptions.UnhandledResponse(response) samsungctl.exceptions.UnhandledResponse: {'event': 'ms.channel.unauthorized'}
Yep, same as above for me. Additionally, here is the output of /logs when running samsungctl --host <host> -i --method=websocket:
DEBUG | "IPC callRPC : result : true\n" | "timestamp: 1540948125873, file: IpcClient.cpp, func: transceive_cb_ : 190"
-- | -- | --
DEBUG | "IPC callRPC : result : true\n" | "timestamp: 1540948125863, file: IpcClient.cpp, func: transceive_cb_ : 190"
DEBUG | "## notifyRemoteNumbers ##" | "timestamp: 1540948125858, file: TizenDevice.cpp, func: notifyRemoteNumbers : 649"
DEBUG | "Client(id:) has disconnected" | "timestamp: 1540948125858, file: RemoteChannel.cpp, func: onSocketClose : 1027"
ERROR | "error(GetSocketInfo) : can't find socket info" | "timestamp: 1540948125858, file: RemoteChannel.cpp, func: GetSocketInfo : 370"
Got this half figured out. Swap the port to 8002 and encapsulate it in SSL. Then, commands work. However, they require manual approval on the TV for every connection, multiple commands can be issued in one connection, but the moment you establish a new connection, you need to manually approve again.
So basically Samsung added "token auth" which you can see in their UPnP advertisement. Whatever that is. So they reply with this when you establish the websocket after you approve it on the TV:
{ "data": { "clients": [ { "attributes": { "name": "fooBase64==" }, "connectTime": 1541354167097, "deviceName": "fooBase64==", "id": "xy123", "isHost": false } ], "id": "xy123", "token": "65811577" }, "event": "ms.channel.connect" }
So obviously the solution is to figure out how to use "token" such that the next time you connect, the TV knows you've already been approved. Since I don't have anything to reverse engineer at the moment that does it correctly, I'm not sure what the correct way to do that is. It could be as simple as adding ?token=xyxyxz to the initial apiv2 GET, but I haven't tried anything yet.
Tried adding ?token=xyxyxz to the GET /api/v2/channels/samsung.remote.control?name=foo and that did not work. For the moment, stumped. My random guess is they hash the connectTime with the token or something like that. Reverse engineering would be the best course if anyone has something that works.
Has anyone been able to run a wireshark capture on the smart view app? For some reason i am unable to get my wire shark instance to pick up any websocket messages between the app and TV. I tried turning my pc into a hotspot and running the websockets through it but the app is unable to find the tv when i do that
In my testing, SmartView2 does not support any 2018 model. I don't think it has been updated for this token auth. There are third party vendors that are supposedly compatible with 2018 models, but I don't have those products.
Hmm yea but SmartView2 works fine for my 2017 TV model with the updated firmware, so it must have some updated compatibility.
Doing a capture on that sounds highly useful. I have 2 models from 2018 and they are not detected by it while a 2016 model works fine the old way. The first questions would be if it uses port 8002 SSL like the 2018 models, and inside that, does it use the token auth. Figuring out the right response for subsequent connections once we have a token is all that is needed here.
To capture SmartView, I use an old '90s era Ethernet Hub connected to a 90s era WiFi router and plug my WireShark machine into that Hub which is all promiscuous. Or, use a managed Ethernet switch with port mirroring. Or, for extreme cases, a dedicated tap: https://www.amazon.com/midBit-Technologies-LLC-100-1000/dp/B0175EODCE
Side note the app MyTifi just had an iOS update to support the TV's again. I have talked to him before and he is very secretive about his code but at least this shows it cant be to hard to fix the issue if he did it in like 5 days
MyTifi does not work. It does exactly what I said above. Connects once, gets manual approval, commands work, and then it can't connect again without manual approval.
Ah ok. i should have probably actually tested it before saying anything haha
When connection is established to: wss://@IP:8002/api/v2/channels/samsung.remote.control?name=base64Name
and token is returned. You can use this token: wss://@IP:8002/api/v2/channels/samsung.remote.control?name=base64Name&token=THETOKEN on next reconnects.
Withouth this token - the TV is asking me to Allow/Deny the device that is trying to connect every time - no matter that previously i choosed Allow (the name, device and tv ips are the same).
When i add the token to the wss url as parameter - i dont get Allow/Deny action (even when TV ip is different).
For now , this is the only way i found useful for this token.
ill try this when i get home today. If that really is that easy then it would be great
I can confirm that works. It was just an error in my REST API syntax yesterday.
Thanks for getting this hammered out so quickly.
I have another brainstorm scenario that i want to understand. The coap:// protocol that tv exposes. If you make coap reqest to coap://239.255.255.250:5683/oic/res You get bunch of resources. When i try to call this resources i get 4.01 unauthorized. Maybe if someone of you guys is more familiar with coap architecture can give some clue on that?
i have made a pull request to support everything! https://github.com/Ape/samsungctl/pull/94
@Ape this pr supports new tv firmware, and uses a new config method "websocketssl" which also will need port 8002
Wish I knew how to incorporate this fix into HomeAssistant myself
it's pretty easy actually.
download the modified library from here https://github.com/eclair4151/samsungctl/tree/websocketssl
copy the file remote_websocket.py in that new package to your site-packages\samsungctl folder.
you will need to edit the config for samsungctl and change the port to 8002
but that should be all that is needed.
@galinkyuchukov
When connection is established to: wss://@ip:8002/api/v2/channels/samsung.remote.control?name=base64Name and token is returned.
Wonderful! Btw, how did you find that?
@vitalets
nmap -p1-65535 ip
I got as result bunch of open ports , including 8001 and 8002.
Since , before firmware update i used ws:// on 8001. I thought 8002 will be the secure version. First tested with all ports and ws .. and then with all on wss:// actualy. :)
After connection the token was just there.
I have another brainstorm scenario that i want to understand. The coap:// protocol that tv exposes. If you make coap reqest to coap://239.255.255.250:5683/oic/res You get bunch of resources. When i try to call this resources i get 4.01 unauthorized. Maybe if someone of you guys is more familiar with coap architecture can give some clue on that?
Interesting.. can you post an request/response sample ?
it's pretty easy actually.
download the modified library from here https://github.com/eclair4151/samsungctl/tree/websocketssl
copy the file remote_websocket.py in that new package to your site-packages\samsungctl folder.
you will need to edit the config for samsungctl and change the port to 8002
but that should be all that is needed.
I did this and the TV just continues to ask me to authorise it, no matter how many times i say yes. I imagine this has to do with what @galinkyuchukov said. You need to add &token=THETOKEN however I don't know what i'd edit to do this or how to get the token!
it's pretty easy actually. download the modified library from here https://github.com/eclair4151/samsungctl/tree/websocketssl copy the file remote_websocket.py in that new package to your site-packages\samsungctl folder. you will need to edit the config for samsungctl and change the port to 8002 but that should be all that is needed.
I did this and the TV just continues to ask me to authorise it, no matter how many times i say yes. I imagine this has to do with what @galinkyuchukov said. You need to add &token=THETOKEN however I don't know what i'd edit to do this or how to get the token!
@DemiMelfice Set the timeout parameter to something like 5. I had the same problem and this gave me enough time to hit "allow" on the tv remote and get his working.
ok cool we need to make a modification to cause the program to stall if this is a new connection to a TV. (meaning there is no token being passed the first time) that is fairly easy to make the modifications for. I also wanted to change how the whole timeout thing works anyway but I would need someone that has a newer TV to test something for me.
in the remote_websocket.py file change
def _read_response(self):
response = self.connection.recv()
response = json.loads(response)
if response["event"] != "ms.channel.connect":
self.close()
raise exceptions.UnhandledResponse(response)
logging.debug("Access granted.")
to read
def _read_response(self):
response = self.connection.recv()
response = json.loads(response)
print(json.dumps(response, indent=4))
if response["event"] != "ms.channel.connect":
self.close()
raise exceptions.UnhandledResponse(response)
logging.debug("Access granted.")
I am interested in knowing what the response is after a key is sent. or if there even is one. If there is one then we can use a threading.Event() object to handle the timeout. because right now the timeout is something that gets set to the maximum amount of time the TV is going to take to process a key request. which can be variable based on TV model and year and also by what button it is that has been pressed. by using that event object we can have the program stall with a default timeout value and the stall would be release if either a response comes in or the default takes place. which ever is first. this would be a far better mechanism and also allow for much faster key presses. and having that feature like that we would be able to force a longer timeout if a token was not provided in the config. and produce an exception if that timeout expires before a response has come in.
But first thing is first. I need to know what the data is that comes in after a key press. actually if someone would be willing to unpair/unauthorize and start the process from scratch and paste all of the output to me that would be really helpful. pair the remote and also a bunch of keypresses. It is going to print out the data to the console window.