Closed aseabridge closed 5 years ago
@kdschlosser No luck for me:
SSDP: 192.168.1.20
b'M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: "ssdp:discover"\r\nMX: 1\r\nST: upnp:rootdevice\r\nCONTENT-LENGTH: 0\r\n\r\n'
Process finished with exit code 0
I have a K-series, which is 2016 or 2017 model. If I remember correctly others on the homeassistant forums have tried this route and UPNP doesn't seem to work for newer models
This is a screen shot of a 2018 model UPNP breakdown. I am trying to find a way to poke a stick at it to get it to respond to the discovery packets on demand. They may have overlooked the one thing that i found with my router. it too does not respond to the broadcast packets. It does however respond to a direct discovery packet. I am pretty sure the TV works in the same manner. I simply need to get all the bits properly set in order to elicit a response from it.
The smart view app uses UPNP to discover the TV's. The reason it does not work on the newer models is because the app is looking for a UPNP class the 2017 and 2018 TV's do not have.
I do want to say thank you for posting what you did. Your response may have located my problem.
this
M-SEARCH * HTTP/1.1\r\n
HOST: 239.255.255.250:1900\r\n
MAN: "ssdp:discover"\r\n
MX: 1\r\n
ST: upnp:rootdevice\r\n
CONTENT-LENGTH: 0\r\n
\r\n
should read
M-SEARCH * HTTP/1.1\r\n
HOST: 192.168.1.20:1900\r\n
MAN: "ssdp:discover"\r\n
MX: 1\r\n
ST: upnp:rootdevice\r\n
CONTENT-LENGTH: 0\r\n
\r\n
another thing I have read is that the newer TV's will only respond to UPNP broadcasts using IPV6. so that will be my next endeavor if this update fails to work. but I have other information that contradicts that as well. This whole process would go a lot faster if I had VPN access to someone's network that the TV is attached to. I can make the updates as I am testing to see if i can get it to respond. Not owning one of these TV's makes it difficult to develop. I own an older one and everything works.
@kdschlosser thanks for your research!
One thought - did you try ST ssdp:all
instead of upnp:rootdevice
?
M-SEARCH * HTTP/1.1\r\n
HOST: 239.255.255.250:1900\r\n
MAN: "ssdp:discover"\r\n
MX: 1\r\n
ST: ssdp:all\r\n
CONTENT-LENGTH: 0\r\n
\r\n
@kdschlosser do you still need vpn access to a network with a Samsung tv on it?
@kdschlosser thanks for the work on this. Maybe more hassle then its worth, but there is a TV emulator from the Samsung developer portal and it alludes to having "networking" functionality.
In theory this could help you.....or be a huge PIA
https://developer.samsung.com/tv/develop/getting-started/using-sdk/tv-emulator
i am downloading it now. It does not state that there is UPNP emulation in that. but it doesn't state that their isn't either. so it's a crap shoot.
If iI do not get anywhere with it then the VPN might be the best way to go.
I am goinig to retool the library so that the UPNP is more of an addon then a requirement. this way if it fails then the basic remote functionality will still be available.
@kdschlosser I have Samsung QE55Q7FAM and a VPN server if you are interested.
@kdschlosser Also, not sure if this reference on UPNP is helpful or not:
https://developer.samsung.com/tv/develop/legacy-platform-library/art00030/index
In the header its tagged as "legacy" with reference up to models in 2014
This document defines a simple way for a mobile device to discover Samsung smart TV supporting Smart View service on the network. The mobile device (Client application developed by the application developer) acts as a Control Point. Devices are discovered via the SSDP M-SEARCH method, using a below specific search target (ST) header. MSearch ST: urn:samsung.com:service:MultiScreenService:1
OK this is just stupid. I am really starting to hate Samsung with a passion. all of their software is so fucked up. excuse the language but it is the only word that properly describes it.
Their requirements for the Emulator state https://developer.tizen.org/ko/development/tizen-studio/download/installing-tizen-studio/prerequisites?langredirect=1#emulator
and i quote
CPU | Recommended: Support for Intel VTx (Virtualization Technology)
now is it me of does that say Recommended??????
not Required...
Then on a completely different Samsung page.
https://developer.samsung.com/tv/develop/tools/prerequisites
To use the TV emulator:
Your CPU must support hardware-assisted virtualization:
For Windows® and macOS, check the Intel® product specifications to make sure that your CPU supports Intel® VT-x.
For Linux, check the KVM site to make sure that your CPU supports either Intel® VT-x or AMD-V™.
so which is it???
then in a support forum... I find that they state that having the Intel VT-x is required no matter the OS you are running. now why on Windows is the AMD-V not supported??
they have their heads so far up their asses.
in a support forum they say that the part that is causing the problem can be disabled. but and they simply state disable it in the .conf file. but they do not explain how to go about doing this. if i comment out the line. it crashes. so how do I disable it?? sure as hell beats me.
I do not believe anything they say or their documentation states. I know the newer TV's support UPNP. I have a person that has a 2018 model and it works. He is just not online regularly. so I am not able to work through the issue using him. We have queried the TV using UPNP and it responds. we can change the volume/mute all of that with UPNP.
I am wondering if there is a setting on the TV it's self to disable UPNP and he has it turned on. I know that the smartview application uses UPNP. this is the mechanism that it uses to control the TV.
@KentEkl I am interested in that VPN access. I do not know if you are on the Slack chat or not. here is a link so you can join it. this expires in 24 hours.
send me a message directly and we can hash out the details on the VPN end of things.
I have to leave for a few hours gotta go and pickup a laptop. I will be back in a few hours. I do not know where about in the world you are. GMT -7:00 is my timezone. I am willing to do this anytime you are available. One thing tho. You need to make sure I will be able to access the internet through the tunnel. it is going to not make my route to the internet non functional. and I will need to have access to resources. I do have most thing available already but in the event I am needing some information I need to have a way to access the information other then disconnecting from the VPN and reconnecting. Tho I can do that if necessary.
send me all of the specific information on the slack chat,
@kdschlosser I messed around with your code tonight and couldn't get it to discover anything(hard coding IP address, etc.). I downloaded the intel tool in your screenshot. It finds the my TV no problem and it looks like the UPNP controls are in fact there.
I tried to use the intel tool to send a SOAP request, but couldn't get anything....however I don't know what I'm doing and I'm sure I didn't have the syntax correct.
EDIT: Using the intel "Device Sniffer" I can post a SOAP request and get a proper response. This should let you see how and where the request is going to troubleshoot your new library. (Returns volume = 3, which was correct). The intel "Device validator" will also be helpful as it'll run through all the available commands it finds via UPNP. Be careful as that test will set you TV volume to 100 at some point (and your wife will be pissed)!
see I told you the UPNP works. I am trying to figure out the mechanism that pokes it with a stick to get a response. I need it to tell me the port and so I can grab the structures from it to build the python classes.
I have a really sneaky suspension that if i broadcast the discovery using IPV6 it will respond on IPV4. I have everything set up identical to what the sniffer does except for the IPV6. this is the single thing I have not done. the broadcast packets are identical. the number of them that I broadcast are identical. You can check all of this if you use that UPNP program with the sniffer open. you will see the broadcasts. all except for the IPV6 ones. And I am willing to bet if you do this the TV will not respond.
Using the actions can be a bit tricky. Here is a little tip. anything that has an "InstanceId" for a parameter you will set that parameter always to 0. this is because the TV can only do a single thing at a time but the UPNP specification is designed so that more then a single thing can be controlled through the same method. an example would be a single controller but multiple screens. and being able to say control the video streams on each of the screens independently. The TV does not have this ability. so that is why it uses a "special use case" InstanceId of 0.
If you would be willing to spend the time you can help me out by compiling the XML for each of the classes and any embedded devices. this can be done by right clicking on an object in the Device Spy and clicking on "view XML" if available. it will then load your browser showing the XML. right click on the browser window and "view page source" copy the XML to a text file. do not reformat it. just plop it into a file the way it is. name the file the same as the object you clicked on in the device spy. you will want to keep some kind of organizational structure. you can do this by making folders of the objects that are a parent of the item you got the xml from and place that item in the folder. if the item has xml and also contains other objects create a folder as the name of the object and also create a file with the same name and place the file inside of that folder.
an example would be
urn:schemas-upnp-org:device:MediaRenderer:1.xml
urn:samsung.com:device:RemoteControlReceiver:1.xml
urn:samsung.com:device:MainTVServer2:1 (folder)
urn:samsung.com:device:MainTVServer2:1,xml
urn:samsung.com:device:MainTVAgent:1.xml
This is an actual layout from my TV this way I can create the convenience methods that will fill in any arbitrary data that the user does not need to have any involvement in.
an example would be what i did for the older TV's when handling sources and channels. With the sources when the user obtains a list of them a list of "Source" instances are returned. they are instances of a clas that I made where a user would be able to do things like.
if not source,is_active:
source.activate()
or the following will change the name displayed on the TV for the source.
source.label = 'Playstation 4'
it makes it so that the user does not need to deal with any of the SOAP it is all handled behind the scenes. as well handle any incorrect data types that are being sent
so the program will know if the following statement is wrong.
tv.volume = '5'
and when the correct one is used
tv.volume = 5
I have the whole backend already made up. I simply need to nail down the mechanism that gets the TV to respond. and then I can code in the convince classes.
I will work on the IPV6 stuff today and see where I get.
@kdschlosser You were right!.....I didnt think it would work. Before you start on IP6 stuff, I got your code to start working a bit. It is discovering my TV (192.168.1.20). Here is what I did with key being setting TV_IP_ADDRESS = 239.255.255.250. I was poking around in your code, but I don't think I modified anything else
from __future__ import print_function
import os
import UPNP_Device
import logging
TV_IP_ADDRESS = '239.255.255.250'
FILEPATH = r'c:\dev\UPNP_DEVICE_OUTPUT.log'
FILEPATH = os.path.expandvars(FILEPATH)
with open(FILEPATH, 'w') as f:
for device in UPNP_Device.discover(10, logging.DEBUG, TV_IP_ADDRESS):
data = str(device)
print(data)
f.write(data)
REMOVED
````!
@kdschlosser Here are those xml files. I think I got it right....if not let me know
-Deleted File-
it uses that IP address if you do not pass an IP to the call to discover. or if you pass '0.0.0.0' to it.
I have made some changes to the UPNP_Device library.
https://github.com/kdschlosser/UPNP_Device/tree/lxml
if you download that one and run
python setup.py install
it will install all required modules as well as the library. then the same script you ran that produced that error. and see what happens.
@kdschlosser Thanks! Getting closer. Looks like some of the URLs you're processing aren't getting chopped up correctly.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\\AppData\Local\Programs\Python\Python35\lib\threading.py", line 923, in _bootstrap_inner
self.run()
File "C:\Users\\AppData\Local\Programs\Python\Python35\lib\threading.py", line 871, in run
self._target(*self._args, **self._kwargs)
File "C:\Users\\PycharmProjects\test_upnp_2\UPNP_Device\discover.py", line 425, in found_thread
found_classes.append(UPNPObject(addr, classes))
File "C:\Users\\PycharmProjects\test_upnp_2\UPNP_Device\instance_singleton.py", line 14, in __call__
super(InstanceSingleton, cls).__call__(ip, classes)
File "C:\Users\\PycharmProjects\test_upnp_2\UPNP_Device\upnp_class.py", line 66, in __init__
device = EmbeddedDevice(url, node=device, parent=self)
File "C:\Users\\PycharmProjects\test_upnp_2\UPNP_Device\embedded_device.py", line 48, in __init__
service = Service(self, url, scpdurl, service_type, control_url)
File "C:\Users\\PycharmProjects\test_upnp_2\UPNP_Device\service.py", line 38, in __init__
response = requests.get(url + location)
File "C:\Users\\PycharmProjects\test_upnp_2\venv\lib\site-packages\requests\api.py", line 75, in get
return request('get', url, params=params, **kwargs)
File "C:\Users\\PycharmProjects\test_upnp_2\venv\lib\site-packages\requests\api.py", line 60, in request
return session.request(method=method, url=url, **kwargs)
File "C:\Users\\PycharmProjects\test_upnp_2\venv\lib\site-packages\requests\sessions.py", line 519, in request
prep = self.prepare_request(req)
File "C:\Users\\PycharmProjects\test_upnp_2\venv\lib\site-packages\requests\sessions.py", line 462, in prepare_request
hooks=merge_hooks(request.hooks, self.hooks),
File "C:\Users\\PycharmProjects\test_upnp_2\venv\lib\site-packages\requests\models.py", line 313, in prepare
self.prepare_url(url, params)
File "C:\Users\\PycharmProjects\test_upnp_2\venv\lib\site-packages\requests\models.py", line 381, in prepare_url
raise InvalidURL(*e.args)
requests.exceptions.InvalidURL: Failed to parse: 192.168.1.25:49152http:
that it does. so we are a step further.
OK give it a shot now. I think i fixed the issue with the URL and there were a couple other bugfixes in there as well.
well you will be happy to know the UPNP library is fixed. I used the xml data you sent me to run a test against it and get all of the kinks out of it.
here is the output from it
UN55MU6300
IP Address: 0.0.0.0
==============================================
Services:
Service name: dial
Service class: urn:dial-multiscreen-org:service:dial:1
Access point: UPNPObject.dial
----------------------------------------------
Methods:
Method name: SendKeyCode
Access point: UPNPObject.dial.SendKeyCode
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_KeyCode
Py data type: unsigned 32bit int
UPNP data type: A_ARG_TYPE_KeyDescription
Py data type: str, unicode
Return Values: None
Service name: StreamSplicing
Service class: urn:schemas-rvualliance-org:service:StreamSplicing:1
Access point: UPNPObject.StreamSplicing
----------------------------------------------
Methods:
Method name: SetBreakAuxStreamPlaylist
Access point: UPNPObject.StreamSplicing.SetBreakAuxStreamPlaylist
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_Int
Py data type: unsigned 32bit int
Default: 0
UPNP data type: A_ARG_TYPE_String
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_String
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_String
Py data type: str, unicode
Return Values: None
Method name: SetBreakAuxStreamTrigger
Access point: UPNPObject.StreamSplicing.SetBreakAuxStreamTrigger
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_Int
Py data type: unsigned 32bit int
Default: 0
UPNP data type: A_ARG_TYPE_Int
Py data type: unsigned 32bit int
Default: 0
UPNP data type: A_ARG_TYPE_Int
Py data type: unsigned 32bit int
Default: 0
Return Values: None
Service name: ConnectionManager
Service class: urn:schemas-upnp-org:service:ConnectionManager:1
Access point: UPNPObject.ConnectionManager
----------------------------------------------
Methods:
Method name: ConnectionComplete
Access point: UPNPObject.ConnectionManager.ConnectionComplete
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_ConnectionID
Py data type: signed 32bit int
Default: 0
Return Values: None
Method name: PrepareForConnection
Access point: UPNPObject.ConnectionManager.PrepareForConnection
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_ProtocolInfo
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_ConnectionManager
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_ConnectionID
Py data type: signed 32bit int
Default: 0
UPNP data type: A_ARG_TYPE_Direction
Py data type: str, unicode
Allowed values:
Input
Output
Return Values:
UPNP data type: A_ARG_TYPE_ConnectionID
Py data type: signed 32bit int
Default: 0
UPNP data type: A_ARG_TYPE_AVTransportID
Py data type: signed 32bit int
Default: 0
UPNP data type: A_ARG_TYPE_RcsID
Py data type: signed 32bit int
Default: 0
Method name: GetProtocolInfo
Access point: UPNPObject.ConnectionManager.GetProtocolInfo
----------------------------------------------
Parameters: None
Return Values:
UPNP data type: SourceProtocolInfo
Py data type: str, unicode
UPNP data type: SinkProtocolInfo
Py data type: str, unicode
Method name: GetCurrentConnectionIDs
Access point: UPNPObject.ConnectionManager.GetCurrentConnectionIDs
----------------------------------------------
Parameters: None
Return Values:
UPNP data type: CurrentConnectionIDs
Py data type: str, unicode
Default: 0
Method name: GetCurrentConnectionInfo
Access point: UPNPObject.ConnectionManager.GetCurrentConnectionInfo
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_ConnectionID
Py data type: signed 32bit int
Default: 0
Return Values:
UPNP data type: A_ARG_TYPE_RcsID
Py data type: signed 32bit int
Default: 0
UPNP data type: A_ARG_TYPE_AVTransportID
Py data type: signed 32bit int
Default: 0
UPNP data type: A_ARG_TYPE_ProtocolInfo
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_ConnectionManager
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_ConnectionID
Py data type: signed 32bit int
Default: 0
UPNP data type: A_ARG_TYPE_Direction
Py data type: str, unicode
Possible returned values:
Input
Output
UPNP data type: A_ARG_TYPE_ConnectionStatus
Py data type: str, unicode
Possible returned values:
OK
ContentFormatMismatch
InsufficientBandwidth
UnreliableChannel
Unknown
Service name: ScreenSharingService
Service class: urn:samsung.com:service:ScreenSharingService:1
Access point: UPNPObject.ScreenSharingService
----------------------------------------------
Methods:
Method name: X_ConnectScreenSharingM2TV
Access point: UPNPObject.ScreenSharingService.X_ConnectScreenSharingM2TV
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_mWlanMacAddress
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_mP2pDeviceAddress
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_mBluetoothMacAddress
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_mWFDSourcePort
Py data type: str, unicode
Return Values:
UPNP data type: A_ARG_TYPE_tBSSID
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_tWlanFreq
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_tListenFreq
Py data type: str, unicode
Method name: X_ConnectScreenSharingTV2M
Access point: UPNPObject.ScreenSharingService.X_ConnectScreenSharingTV2M
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_mWlanMacAddress
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_mP2pDeviceAddress
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_mBluetoothMacAddress
Py data type: str, unicode
Return Values:
UPNP data type: A_ARG_TYPE_tBSSID
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_tWlanFreq
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_tListenFreq
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_tWFDSourcePort
Py data type: str, unicode
Service name: AVTransport
Service class: urn:schemas-upnp-org:service:AVTransport:1
Access point: UPNPObject.AVTransport
----------------------------------------------
Methods:
Method name: SetNextAVTransportURI
Access point: UPNPObject.AVTransport.SetNextAVTransportURI
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: NextAVTransportURI
Py data type: str, unicode
UPNP data type: NextAVTransportURIMetaData
Py data type: str, unicode
Return Values: None
Method name: Play
Access point: UPNPObject.AVTransport.Play
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: TransportPlaySpeed
Py data type: str, unicode
Default: 1
Return Values: None
Method name: Pause
Access point: UPNPObject.AVTransport.Pause
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values: None
Method name: GetPositionInfo
Access point: UPNPObject.AVTransport.GetPositionInfo
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values:
UPNP data type: CurrentTrack
Py data type: unsigned 32bit int
Default: 0
Minimum: 0
Maximum: 4294967295
Step: 1
UPNP data type: CurrentTrackDuration
Py data type: str, unicode
Default: 00:00:00
UPNP data type: CurrentTrackMetaData
Py data type: str, unicode
UPNP data type: CurrentTrackURI
Py data type: str, unicode
UPNP data type: RelativeTimePosition
Py data type: str, unicode
Default: 00:00:00
UPNP data type: AbsoluteTimePosition
Py data type: str, unicode
Default: 00:00:00
UPNP data type: RelativeCounterPosition
Py data type: signed 32bit int
Default: 2147483647
UPNP data type: AbsoluteCounterPosition
Py data type: signed 32bit int
Default: 2147483647
Method name: X_PlayerAppHint
Access point: UPNPObject.AVTransport.X_PlayerAppHint
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: X_ARG_TYPE_UpnpClass
Py data type: str, unicode
Allowed values:
object.item.imageItem
object.item.audioItem
object.item.videoItem
UPNP data type: X_ARG_TYPE_PlayerHint
Py data type: str, unicode
Allowed values:
load
unload
Return Values: None
Method name: GetMediaInfo
Access point: UPNPObject.AVTransport.GetMediaInfo
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values:
UPNP data type: NumberOfTracks
Py data type: unsigned 32bit int
Default: 0
Minimum: 0
Maximum: 4294967295
UPNP data type: CurrentMediaDuration
Py data type: str, unicode
Default: 00:00:00
UPNP data type: AVTransportURI
Py data type: str, unicode
UPNP data type: AVTransportURIMetaData
Py data type: str, unicode
UPNP data type: NextAVTransportURI
Py data type: str, unicode
UPNP data type: NextAVTransportURIMetaData
Py data type: str, unicode
UPNP data type: PlaybackStorageMedium
Py data type: str, unicode
Default: NONE
Possible returned values:
NONE
NETWORK
UPNP data type: RecordStorageMedium
Py data type: str, unicode
NOT_IMPLEMENTED
UPNP data type: RecordMediumWriteStatus
Py data type: str, unicode
NOT_IMPLEMENTED
Method name: X_DLNA_GetBytePositionInfo
Access point: UPNPObject.AVTransport.X_DLNA_GetBytePositionInfo
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values:
UPNP data type: X_DLNA_CurrentTrackSize
Py data type: str, unicode
UPNP data type: X_DLNA_RelativeBytePosition
Py data type: str, unicode
UPNP data type: X_DLNA_AbsoluteBytePosition
Py data type: str, unicode
Method name: GetTransportInfo
Access point: UPNPObject.AVTransport.GetTransportInfo
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values:
UPNP data type: TransportState
Py data type: str, unicode
Default: NO_MEDIA_PRESENT
Possible returned values:
STOPPED
PAUSED_PLAYBACK
PLAYING
TRANSITIONING
NO_MEDIA_PRESENT
UPNP data type: TransportStatus
Py data type: str, unicode
Default: OK
Possible returned values:
OK
ERROR_OCCURRED
UPNP data type: TransportPlaySpeed
Py data type: str, unicode
Default: 1
Method name: Stop
Access point: UPNPObject.AVTransport.Stop
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values: None
Method name: GetDeviceCapabilities
Access point: UPNPObject.AVTransport.GetDeviceCapabilities
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values:
UPNP data type: PossiblePlaybackStorageMedia
Py data type: str, unicode
Default: NETWORK
UPNP data type: PossibleRecordStorageMedia
Py data type: str, unicode
NOT_IMPLEMENTED
UPNP data type: PossibleRecordQualityModes
Py data type: str, unicode
NOT_IMPLEMENTED
Method name: Next
Access point: UPNPObject.AVTransport.Next
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values: None
Method name: GetTransportSettings
Access point: UPNPObject.AVTransport.GetTransportSettings
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values:
UPNP data type: CurrentPlayMode
Py data type: str, unicode
Default: NORMAL
Possible returned values:
NORMAL
UPNP data type: CurrentRecordQualityMode
Py data type: str, unicode
NOT_IMPLEMENTED
Method name: SetAVTransportURI
Access point: UPNPObject.AVTransport.SetAVTransportURI
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: AVTransportURI
Py data type: str, unicode
UPNP data type: AVTransportURIMetaData
Py data type: str, unicode
Return Values: None
Method name: SetPlayMode
Access point: UPNPObject.AVTransport.SetPlayMode
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: CurrentPlayMode
Py data type: str, unicode
Default: NORMAL
Allowed values:
NORMAL
Return Values: None
Method name: X_PrefetchURI
Access point: UPNPObject.AVTransport.X_PrefetchURI
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: A_ARG_TYPE_PrefetchURI
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_PrefetchURIMetaData
Py data type: str, unicode
Return Values: None
Method name: X_GetStoppedReason
Access point: UPNPObject.AVTransport.X_GetStoppedReason
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values:
UPNP data type: A_ARG_TYPE_StoppedReason
Py data type: str, unicode
UPNP data type: A_ARG_TYPE_StoppedReasonData
Py data type: str, unicode
Method name: Seek
Access point: UPNPObject.AVTransport.Seek
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: A_ARG_TYPE_SeekMode
Py data type: str, unicode
Default: REL_TIME
Allowed values:
TRACK_NR
REL_TIME
ABS_TIME
ABS_COUNT
REL_COUNT
X_DLNA_REL_BYTE
FRAME
UPNP data type: A_ARG_TYPE_SeekTarget
Py data type: str, unicode
Return Values: None
Method name: GetCurrentTransportActions
Access point: UPNPObject.AVTransport.GetCurrentTransportActions
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values:
UPNP data type: CurrentTransportActions
Py data type: str, unicode
Method name: Previous
Access point: UPNPObject.AVTransport.Previous
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values: None
Service name: MultiScreenService
Service class: urn:samsung.com:service:MultiScreenService:1
Access point: UPNPObject.MultiScreenService
----------------------------------------------
Methods:
Method name: SendKeyCode
Access point: UPNPObject.MultiScreenService.SendKeyCode
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_KeyCode
Py data type: unsigned 32bit int
UPNP data type: A_ARG_TYPE_KeyDescription
Py data type: str, unicode
Return Values: None
Service name: IPControlService
Service class: urn:samsung.com:service:IPControlService:1
Access point: UPNPObject.IPControlService
----------------------------------------------
Methods:
None
Service name: RenderingControl
Service class: urn:schemas-upnp-org:service:RenderingControl:1
Access point: UPNPObject.RenderingControl
----------------------------------------------
Methods:
Method name: GetMute
Access point: UPNPObject.RenderingControl.GetMute
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: A_ARG_TYPE_Channel
Py data type: str, unicode
Allowed values:
Master
Return Values:
UPNP data type: Mute
Py data type: bool
Possible returned values: True/False
Method name: SelectPreset
Access point: UPNPObject.RenderingControl.SelectPreset
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: A_ARG_TYPE_PresetName
Py data type: str, unicode
Allowed values:
FactoryDefaults
Return Values: None
Method name: GetVolume
Access point: UPNPObject.RenderingControl.GetVolume
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: A_ARG_TYPE_Channel
Py data type: str, unicode
Allowed values:
Master
Return Values:
UPNP data type: Volume
Py data type: unsigned 16bit int
Minimum: 0
Maximum: 100
Step: 1
Method name: SetMute
Access point: UPNPObject.RenderingControl.SetMute
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: A_ARG_TYPE_Channel
Py data type: str, unicode
Allowed values:
Master
UPNP data type: Mute
Py data type: bool
Allowed values: True/False
Return Values: None
Method name: X_SetTVSlideShow
Access point: UPNPObject.RenderingControl.X_SetTVSlideShow
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: A_ARG_TYPE_SlideShowState
Py data type: bool
Allowed values: True/False
UPNP data type: A_ARG_TYPE_ThemeId
Py data type: unsigned 32bit int
Return Values: None
Method name: X_Origin360View
Access point: UPNPObject.RenderingControl.X_Origin360View
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values: None
Method name: X_Move360View
Access point: UPNPObject.RenderingControl.X_Move360View
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: X_ARG_TYPE_ArcDegreeOffset
Py data type: float
Default: 0.0
UPNP data type: X_ARG_TYPE_ArcDegreeOffset
Py data type: float
Default: 0.0
Return Values: None
Method name: X_GetVideoSelection
Access point: UPNPObject.RenderingControl.X_GetVideoSelection
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values:
UPNP data type: X_VideoPID
Py data type: unsigned 16bit int
Default: 0
Minimum: 0
Maximum: 65535
Step: 1
UPNP data type: X_VideoEncoding
Py data type: str, unicode
Method name: X_SetZoom
Access point: UPNPObject.RenderingControl.X_SetZoom
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: A_ARG_TYPE_Coordinate
Py data type: unsigned 32bit int
UPNP data type: A_ARG_TYPE_Coordinate
Py data type: unsigned 32bit int
UPNP data type: A_ARG_TYPE_Coordinate
Py data type: unsigned 32bit int
UPNP data type: A_ARG_TYPE_Coordinate
Py data type: unsigned 32bit int
Return Values: None
Method name: X_UpdateAudioSelection
Access point: UPNPObject.RenderingControl.X_UpdateAudioSelection
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: X_AudioPID
Py data type: unsigned 16bit int
Default: 0
Minimum: 0
Maximum: 65535
Step: 1
UPNP data type: X_AudioEncoding
Py data type: str, unicode
Return Values: None
Method name: ListPresets
Access point: UPNPObject.RenderingControl.ListPresets
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values:
UPNP data type: PresetNameList
Py data type: str, unicode
Default: FactoryDefaults
Method name: X_GetTVSlideShow
Access point: UPNPObject.RenderingControl.X_GetTVSlideShow
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values:
UPNP data type: A_ARG_TYPE_SlideShowState
Py data type: bool
Possible returned values: True/False
UPNP data type: A_ARG_TYPE_ThemeId
Py data type: unsigned 32bit int
UPNP data type: A_ARG_TYPE_TotalThemeNumber
Py data type: unsigned 32bit int
Method name: X_GetAspectRatio
Access point: UPNPObject.RenderingControl.X_GetAspectRatio
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values:
UPNP data type: X_AspectRatio
Py data type: str, unicode
Default: Default
Possible returned values:
Default
FitScreen
Method name: X_GetServiceCapabilities
Access point: UPNPObject.RenderingControl.X_GetServiceCapabilities
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values:
UPNP data type: X_ServiceCapabilities
Py data type: str, unicode
Method name: X_GetAudioSelection
Access point: UPNPObject.RenderingControl.X_GetAudioSelection
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values:
UPNP data type: X_AudioPID
Py data type: unsigned 16bit int
Default: 0
Minimum: 0
Maximum: 65535
Step: 1
UPNP data type: X_AudioEncoding
Py data type: str, unicode
Method name: X_ControlCaption
Access point: UPNPObject.RenderingControl.X_ControlCaption
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: X_ARG_TYPE_CaptionOperation
Py data type: str, unicode
Allowed values:
Enable
Disable
UPNP data type: X_ARG_TYPE_CaptionName
Py data type: str, unicode
UPNP data type: X_ARG_TYPE_ResourceURI
Py data type: str, unicode
UPNP data type: X_ARG_TYPE_CaptionURI
Py data type: str, unicode
UPNP data type: X_ARG_TYPE_CaptionType
Py data type: str, unicode
UPNP data type: X_ARG_TYPE_Language
Py data type: str, unicode
UPNP data type: X_ARG_TYPE_Encoding
Py data type: str, unicode
Return Values: None
Method name: X_GetCaptionState
Access point: UPNPObject.RenderingControl.X_GetCaptionState
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
Return Values:
UPNP data type: X_Captions
Py data type: str, unicode
UPNP data type: X_EnabledCaptions
Py data type: str, unicode
Method name: X_UpdateVideoSelection
Access point: UPNPObject.RenderingControl.X_UpdateVideoSelection
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: X_VideoPID
Py data type: unsigned 16bit int
Default: 0
Minimum: 0
Maximum: 65535
Step: 1
UPNP data type: X_VideoEncoding
Py data type: str, unicode
Return Values: None
Method name: X_Zoom360View
Access point: UPNPObject.RenderingControl.X_Zoom360View
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: X_ARG_TYPE_ScaleFactorOffset
Py data type: float
Default: 1.0
Return Values: None
Method name: X_SetAspectRatio
Access point: UPNPObject.RenderingControl.X_SetAspectRatio
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: X_AspectRatio
Py data type: str, unicode
Default: Default
Allowed values:
Default
FitScreen
Return Values: None
Method name: SetVolume
Access point: UPNPObject.RenderingControl.SetVolume
----------------------------------------------
Parameters:
UPNP data type: A_ARG_TYPE_InstanceID
Py data type: unsigned 32bit int
UPNP data type: A_ARG_TYPE_Channel
Py data type: str, unicode
Allowed values:
Master
UPNP data type: Volume
Py data type: unsigned 16bit int
Minimum: 0
Maximum: 100
Step: 1
Return Values: None
Devices: None
it seems as tho they removed all of the TV and source specific features. or maybe they are simply not broadcasting the UPNP class information.
That being said how the UPNP_Device discover function works is it send the broadcats only to get the IP address of a device. then it performs a direct query. this is something that you cannot do with the Device Sniffer. so the TV may not broadcast the UPNP classes but if directly queried it might.
there is still access to volume and mute. and it appears as tho there is a mechanism to possibly locate apps to be able to launch them.
@kdschlosser Thanks for all the work on this. This generally works for me, but I only get a results with IP=239.255.255.250; 0.0.0.0 and my tvs IP don't return anything. Also the " creating UPNPObject instance" portion errors on a specific device on my network (directv) - see below. Any idea on this? or maybe just put that code in a try-catch block and ignore this exception, since we don't care that device anyway?
SSDP: 192.168.1.27 creating UPNPObject instance
Exception in thread Thread-1:
Traceback (most recent call last):
File "C:\Users\\PycharmProjects\test_upnp_2\venv\lib\site-packages\requests\models.py", line 379, in prepare_url
scheme, auth, host, port, path, query, fragment = parse_url(url)
File "C:\Users\\PycharmProjects\test_upnp_2\venv\lib\site-packages\urllib3\util\url.py", line 199, in parse_url
raise LocationParseError(url)
urllib3.exceptions.LocationParseError: Failed to parse: 192.168.1.25:49152http:
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\\AppData\Local\Programs\Python\Python35\lib\threading.py", line 923, in _bootstrap_inner
self.run()
File "C:\Users\\AppData\Local\Programs\Python\Python35\lib\threading.py", line 871, in run
self._target(*self._args, **self._kwargs)
File "C:\Users\\AppData\Local\Programs\Python\Python35\lib\UPNP_Device\discover.py", line 426, in found_thread
found_classes.append(UPNPObject(addr, classes))
File "C:\Users\\AppData\Local\Programs\Python\Python35\lib\UPNP_Device\upnp_class.py", line 24, in __call__
super(UPNPSingleton, cls).__call__(ip, classes)
File "C:\Users\\AppData\Local\Programs\Python\Python35\lib\UPNP_Device\upnp_class.py", line 91, in __init__
device = UPNPDevice(url, node=device, parent=self)
File "C:\Users\\AppData\Local\Programs\Python\Python35\lib\UPNP_Device\upnp_class.py", line 189, in __init__
service = Service(self, url, scpdurl, service_type, control_url)
File "C:\Users\\AppData\Local\Programs\Python\Python35\lib\UPNP_Device\service.py", line 38, in __init__
response = requests.get(url + location)
File "C:\Users\\PycharmProjects\test_upnp_2\venv\lib\site-packages\requests\api.py", line 75, in get
return request('get', url, params=params, **kwargs)
File "C:\Users\\PycharmProjects\test_upnp_2\venv\lib\site-packages\requests\api.py", line 60, in request
return session.request(method=method, url=url, **kwargs)
File "C:\Users\\PycharmProjects\test_upnp_2\venv\lib\site-packages\requests\sessions.py", line 519, in request
prep = self.prepare_request(req)
File "C:\Users\\PycharmProjects\test_upnp_2\venv\lib\site-packages\requests\sessions.py", line 462, in prepare_request
hooks=merge_hooks(request.hooks, self.hooks),
File "C:\Users\\PycharmProjects\test_upnp_2\venv\lib\site-packages\requests\models.py", line 313, in prepare
self.prepare_url(url, params)
File "C:\Users\\PycharmProjects\test_upnp_2\venv\lib\site-packages\requests\models.py", line 381, in prepare_url
raise InvalidURL(*e.args)
requests.exceptions.InvalidURL: Failed to parse: 192.168.1.25:49152http:
the UPNP_Device library is a separate library I made. it is not specific to only Samsung product. It's so others can use it if they wanted to control say a network attached TV tuner. It also means that if there is an error in it now. that there could be later in the future if Samsung changes something that doesn't agree. so we have to make sure this portion is all nice and cleaned up. to keep from having problems in the future.
Now. that being stated. the line that the error is pointing to that pertaint to the UPNP_Device library
File "C:\Users\bmurr\AppData\Local\Programs\Python\Python35\lib\UPNP_Device\service.py", line 38, in init
response = requests.get(url + location)
line 38. there is nothing on that line in the latest version of the program. the highlighted line in the image below is the line that is mentioned in the error. which according to that error should read
response = requests.get(url + location)
but doesn't
You need to download the latest version from the lxml branch.
you can get to the lxml branch with this link
https://github.com/kdschlosser/UPNP_Device/tree/lxml
then click on the clone or download button and save it as a zip. decompress it to some temp folder. start a console (linux) or a command prompt (windows) navigate to that temp folder. and run the setup.py as outlined below.
python setup.py install
you have to run the setup.py. this will install any requirements that are necessary. in this case it would be lxml.
ok so here goes another try.
I do want to mention that if you have installed it this way before go into your siite packages folder and delete the one that is in there. it is going to be a file with the extension of .egg. then edit
easy-install.pth
it is in the same location as the .egg file.
find the line pointing to that .egg file and delete it.
download the version from this link. https://github.com/kdschlosser/samsungctl/tree/updated_upnp_features
to install it run the following command
python setup.py install
and here is a basic script to see if it works.
from samsungctl import Remote
remote = Remote(log_level=Remote.LOG_DEBUG)
print(remote.upnp_tv)
or you can use this script if you want to specify the IP of the TV change the MY_TV_IP variable to the address of your TV
MY_TV_IP = '0.0.0.0'
from samsungctl import Remote
remote = Remote(ip_address=MY_TV_IP, log_level=Remote.LOG_DEBUG)
print(remote.upnp_tv)
I forgot to mention. if you so choose the old way
from samsungctl import Remote
config = {
"name": "samsungctl",
"description": "PC",
"id": "",
"method": "legacy",
"port": 55000,
"timeout": 0
}
remote = Remote(config , log_level=Remote.LOG_DEBUG)
while remote.is_discovering:
pass
print(remote.upnp_tv)
i set this up so that if you pass a config file it will use that config file to make the remote connection first. and then it launches a thread to do the discovery side of things. So the library will actually still function if the UPNP end of things is not functioning for some reason. so I added a mechanism to be able to tell if the UPNP discovery process is still running.
if you do not pass a config file and simply pass an IP or let it automatically discover there are bits of information that needs to be obtained from the UPNP side of things in order to get the remote portion working. so there is no thread that gets launched. it keeps in all within the thread that created the Remote instance.
I also removed the creation of any classes based on any UPNP data that is not a TV. no sense in wasting overhead doing that.
ok people I have made some real progress in the past few hours. I have been going back and forth with a person that is testing the program.. so close to having it working right with extended functionality.
Nice! You're a real hero. I should be able to use this with openhab i think?
if openhab supports python then yes.
@kdschlosser Awesome work! One of the problems we have in Homesassistant is just knowing if the TV is on or off. All the solutions have been kinda flakey given all the different tv models. Any chance this library can subscribe to events? I noticed the TV sends an SSDP:byebye when you turn it off and SSDP:alive periodically when its on. I'd think we could leverage that to understand the actual TV power state?
Thanks!
having the TV show a power state is easy to do. and from what i have seen the TV's do not have any events that can be subscribed to. but we can do up some simple polling to get the job done. it would monitor whether or not the TV is on (responding to SSDP packets) and checking the status of the various elements like volume and mute. and see if they match values that we store for the device. and if they do not match an event can be generated.
it appears as tho Samsung has removed all of the TV tuner controls from the newer TV's as well as the ability to control what the input is which really sucks. nothing I can do about that. they have probably migrated everything to the websocket connection. but there really is no way to know. I am going to send an email off to Samsung to request the source code for the smartview application. the app is open source. so maybe that may give some insight.
I would rather not bind to a socket to wait for an SSDP byebye from the TV. the reason being is that if something happens with the program whether it be samsungctl or the program that is running it, and the socket does not get closed properly we will end up with an orphaned socket. so i would like to steer clear of doing that.
Maybe some inspiration can be had from this project https://github.com/tavicu/homebridge-samsung-tizen
well well. that did give me the mechanism to be able to get the app ID's
It will take a while to acquire them but they can be had.
if someone is willing to run this script and set the TV_IP to the IP address of your TV. and paste the data to me that would be helpful. you will need to have the YouTube app installed for this to work.
import requests
TV_IP = '0.0.0.0'
response = requests.get('http://' + TV_IP + ':8001/api/v2/applications/111299001912')
print(response.content)
{"id":"111299001912","name":"YouTube","running":true,"version":"2.1.486","visible":false}
Is what I got from running the link in my browser
ok cool
@kdschlosser Did you happen to see this thread? i think this thread tracked down some of the app ids already:
yeah only some. I am going to go after as man as i can get a hold of
here is a script that is going to have to run for a long time. probably several days. You will be able to check and see if it got all the applications on your TV by looking in the folder and counting the number of files and then counting the number of applications on the TV
you will need to change the TV_IP to be your TV ip address.
This is set up to be run on Windows it will make a directory called samsung_application_ids on your desktop, in that directory it will put a file for every application it finds. this is a brute force number deal. I started with the application ID for NetFlix as it is the lowest number therefor has been around the longest time. there are 10 threads that run querying the TV if there is no application at that number then it will get an error and the thread moves to the next number.
it will spit out app numbers it is trying to the console. so you know it's working.
import requests
import threading
import os
import json
TV_IP = '0.0.0.0'
# {
# "id": "111299001912",
# "name": "YouTube",
# "running": True,
# "version": "2.1.486",
# "visible": False
# }
OUTPUT_PATH = os.path.expandvars(
os.path.join('%userprofile%', 'desktop', 'samsung_application_ids')
)
file_save_count = 0
if not os.path.exists(OUTPUT_PATH):
os.mkdir(OUTPUT_PATH)
count = 11101200000
url = 'http://{0}:8001/api/v2/applications/'.format(TV_IP)
def do():
global count
global file_save_count
while True:
t_count = count
count += 1000
end_count = t_count + 999
while t_count != end_count:
print(t_count)
try:
response = requests.get(url + str(t_count), timeout=5)
content = json.loads(response.content)
try:
with open(os.path.join(OUTPUT_PATH, content['name'] + '.txt'), 'w') as f:
f.write(json.dumps(content, indent=4))
except KeyError:
pass
except:
save_count = file_save_count
file_save_count += 1
with open(os.path.join(OUTPUT_PATH, str(save_count) + '.txt'), 'w') as f:
f.write(json.dumps(content, indent=4))
except:
pass
for _ in range(10):
t = threading.Thread(target=do)
t.daemon = True
t.start()
event = threading.Event()
event.wait()
The code above will not work. Apparently not all of the apps will show up there. It doesn't matter anyway. I managed to get my hands on large portions of the Samsung TV OS source code. And k do believe the websocket bits are in it. This is going to make engineering an API a whole lot easier.
Hi for some reason i get the following error while trying any scripts:
Traceback (most recent call last): File "test.py", line 3, in
remote = Remote(log_level=Remote.LOG_DEBUG) File "/usr/local/lib/python2.7/dist-packages/samsungctl-0.7.1+1-py2.7.egg/samsungctl/remote.py", line 22, in init self._discovering = threading.Event() File "/usr/local/lib/python2.7/dist-packages/samsungctl-0.7.1+1-py2.7.egg/samsungctl/remote.py", line 100, in setattr if self.upnp_tv is not None: File "/usr/local/lib/python2.7/dist-packages/samsungctl-0.7.1+1-py2.7.egg/samsungctl/remote.py", line 93, in getattr if self.upnp_tv is not None: File "/usr/local/lib/python2.7/dist-packages/samsungctl-0.7.1+1-py2.7.egg/samsungctl/remote.py", line 93, in getattr if self.upnp_tv is not None: <.... Snipped out about 100 times repetition of line 93 errors ...> File "/usr/local/lib/python2.7/dist-packages/samsungctl-0.7.1+1-py2.7.egg/samsungctl/remote.py", line 93, in getattr if self.upnp_tv is not None: RuntimeError: maximum recursion depth exceeded Any Idea ? This is on ubuntu lts Python 2.7.15rc1
@kdschlosser Btw why do you need IDs of all possible applications?
We are retrieving all apps installed on particular TV by WebSocket with the following commands:
// get installed apps
send({
method: 'ms.channel.emit',
params: {
event: 'ed.installedApp.get',
to: 'host',
data: { },
}
});
// get eden apps (with more extra info)
send({
method: 'ms.channel.emit',
params: {
event: 'ed.edenApp.get',
to: 'host',
data: { },
}
});
I know I saw that. But if i happen to locate the mechanism to be able to install the applications we are going to need to know the application ID's in order to do this. So either a query would have to be made to Samsung servers with login details in order to obtain a list of available applications. or we can store the application names and ID's in a flat file and simply pull from them. I think the latter would be a bit easier to do. I know there would be more on the maintenance end of things but unless someone has reverse engineered the process to get the available applications from the Samsung servers the flat file is the best option.
I think I have gotten the ssl websocket end of things working. I have one last change to make and then I have to get it tested again. It is discovering the TV and connecting via UPNP and also by use of the websocket. the prompt comes up on the TV for the remote authorization but the UPNP end of things is not fully populated. I have to increase the discover timeout to see if that solves the issue. and in order to have the token stored correctly I am using the device_id gotten from UPNP to marry the token to. this way it is unique. If a config is passed to the constructor the token will be stored in that config and it will be up to the user to save that config file to a location of their choosing. I do not want to break API at all and that is how it is currently set up unless using the command line interface. I have tinkered with the idea of creating a class which would store all of the config information. and that config instance could be passed to the constructor and that config class would have a class method of load where a path could be specified. it would also have an instance method of save with a parameter for an optional path. there are several things I have toyed around with as ideas of making this an easier to use library for the user. I do like the idea of the config file. But ultimately the best way is to have the discovery working and using the device id would be the best way as it is not dependent on an IP address. this way the TV could be left as a dynamically assigned IP.
Hi @kdschlosser ... can you give me hint regarding my issue above ... (i don't have a very good python knowledge) ... that would be awesome.
Thanks
@derelict that is a test version that had some issues. i am still tinkering about with it to get things corrected.
so here is an example of things working properly with my older generation TV, wish I had a newer one to develop with it would make things far easier.
so here is the example I have.
Here you can see the ability to select a source. this is a list that is retrieved form the TV
and in this screen shot you can see the list on the right that contains only a partial list of what can be done on the TV. and on the left the lines with the Leggo looking icons represent me sending a command to the TV. and the lines with the lightning bolt are "events" or notifications that things have changed on the TV. the events some from data retrieved from the TV. If you look at the response time you will notice that the events are taking place less then a second after I send the command to the TV.
the next few images are to show the rest of the commands
so I am making more progress with this. I think I might have the final things finished up. I have to release a test version of the program that populates this interface into the forums of another project I work on to find out. if successful I will release it here. I have someone that is testing things out for me on that forum.
I did change how the config data is stored. the old API still functions. But I have made it a bit easier for those that do not know JSON.
here are some code examples of how one will be able to use the discovery and old API alike.
from samsungctl import discover, Config, Remote
config = Config.load('path/to/save/file')
tv = discover(config)
config.save()
# or if you want to manually enter the config data
config = Config(
# not used here for backwards compatibility
name=None,
# not used here for backwards compatibility
description=None,
# IP of the TV. this gets used if there is no device ID present
host='192.168.1.1',
# this only gets used if the TV cannot be discovered using UPNP or if the
# old API of passing a dict gets sent directly to Remote which would
# bypass any UPNP discovery
# settings are 55000 or 8001
port=55000,
# legacy connections and gets automatically generated. But it is here
# because the old config file used it.
id=None,
# not used here for backwards compatibility
method='legacy',
# not used here for backwards compatibility
timeout=0,
# mainly for internal use. but if you know the token you are more then
# welcome to enter it. if the token is present the mechanics of discovering
# if a TV supports ssl or not is bypassed.
token=None,
# optional save path for the config file. a path can also be specified
# when calling config.save
path=None,
# if this param is manually entered it has to be the device id gotten from
# UPNP. this will override the use of the host (IP) when discovering via
# UPNP. if you use the method above for discovering a TV this is
# automatically populated
device_id=None
)
tv = discover(config)
config.save('/path/to/save/file')
# because the library is now going to be able to support multiple TV's through
# the use of the discovery mechanism.
for tv in discover():
print(tv.name)
# another nice thing is if the user decides to use the TV
for tv in discover():
answer = raw_input('use TV ' + tv.name + ':')
if answer.lower() == 'y':
path = raw_input('config save path:')
tv.config.save(path)
# if you specify a path that is only a directory the tv model number and the
# device id will get used
# because of the above code if all of the config files are saved to the same
# directory iterating over the directory to pass the file names becomes a snap
# so setting up the configs for the TV's becomes easy.
import os
path = 'path/to/config/files'
tvs = []
for file in os.listdir(path):
config = Config.load(os.path.join(path, file))
tv = discover(config)
tvs += [tv]
# now to keep things from getting all kinds of out of sorts
# II made a meta class that is an instance singleton. so there can only be a
# single instance of a TV. This will make it it easier to track if a device is
# powered on or off (this method works quite well actually).
tvs = list(discover())
new_tvs = list(discover())
for tv in new_tvs:
tv.power = True
if tv not in tvs or not tv.power:
print('tv powered on ' + tv.name)
for tv in tvs:
if tv not in new_tvs:
tv.power = False
print('tv powered off ' + tv.name)
# and of coarse for supporting a single TV this way still works as well.
config = {
"name": "samsungctl",
"description": "PC",
"id": "",
"method": "legacy",
"port": 55000,
"timeout": 0
}
remote = Remote(config)
# this is a little added bonus a slight twist. You will have to specify the
# filename to save to. the above bypasses the UPNP discovery so the device id
# cannot be obtained.
remote.config.save('/path/to/save/file')
I changed how the config data gets written. I removed the use of storing the config data as a json object in favor of storing it as a flat file. I feel this will make it easier for the programming newbies. and in all honesty there is no reason why the config data should be stored as a json object. there is not enough data to warrant it and none of the data is nested at all. the library of course is backwards compatible with the json files. but when a new one gets written it will be as a flat file. the flat file will look like the following
name = Some TV Name
description = Some TV Description
host = 192.168.1.1
port = 8002
id = Some ID
method = websocket
timeout = 0
token = Token from the websocket
device id = Device ID from UPNP
the arrangement of the lines does not matter. any blank lines does not matter. if you want to add commented lines just make sure they do not have an = in them. all fields are not necessary.
@kdschlosser Ok, Thanks. Do you have an idea of when you plan to commit your most recent changes ?
hopefully soon. I hurt my back yesterday. This is the first time I am sitting down at the computer since then. we will see how much I can get done.
now I know that this appears to give the state of the application
import requests
TV_IP = '0.0.0.0'
response = requests.get('http://' + TV_IP + ':8001/api/v2/applications/111299001912')
print(response.content)
I do not know if this is the "universal" mechanism across all new TV's
i did find some information in the API about using the mechanism below. who knows with Samsung as to which was will work and which way will not. so better to have both in there. one as a fallback to use.
please post any error or data returned. change the TV_IP to your TV's IP address.
import requests
TV_IP = '0.0.0.0'
response = requests.get('http://' + TV_IP + ':8001/api/v2/applications/111299001912/info')
print(response.content)
I stumbled upon @eclair4151 's implementation of the encrypted websocket for the 2014 and 2015 models. I am hoping @eclair4151 doesn't mind. I borrowed the code to add to samsungctl to give full support of all available TV models from 2007+ (I think the IP control started with 2007). I did however have to make a large amount of changes to make it compatible with samsungctl as the original code was only drafted for single use. I also did some reformatting to make it PEP8 compliant as well as made it compatible with python 2.7+
ok so here is a test version again
https://github.com/kdschlosser/samsungctl/tree/yet_another_test_version
I am making changes as I go. so bear with me on these things. Ideas come to me as I am working on it.
typical install script. please run it to get the newest requirements
python setup.py install
here is a simple test script to test with if you want to capture all of the console output you will need to set the scroll history to a pretty high number. I would say somewhere around 10K would be a good number. if you have more then a single TV then you will need more.
from __future__ import print_function
import samsungctl
for tv in samsungctl.discover():
print(tv.config)
print()
print(tv.upnp_tv)
OK i made a branch that is just a modification of the original library to use the ssl websocket. i removed use of the old style websocket. so this version will only do legacy and websocket ssl. use the original config file that you were using with samsungctl. or pass the exact same config parameters to it as you were before.
This version supports more then a single TV. and it also should overcome the issues with having to run the script as sudo.
https://github.com/kdschlosser/samsungctl/tree/ssl_websocket_only
now remember this works exactly like the original samsungctrl library
Thanks for putting in a fix for the timeout @kdschlosser. I can see you’ve added the LG logging to the file. To test this with my 2017/2018 tv I’m guessing I’ll need to checkout this timeout branch and call the method. What command should I issue? When I tried using this library earlier today diectlty it was giving me errors about bytes so I’m guessing I was missing some parameters.