Hi Scott,
On Thu, 2018-05-17 at 21:09 -0400, Scott Talbert wrote:
> If there's anyone still out there,
I'm here! I've got a Harmony Ultimate One and use some of the library
bits on the odd occasion I need to fiddle with my remote setup.
> I would appreciate if you could test
> the latest git master and let me know if you run into any problems. If
> you want to use Python 3, you'll need the latest git master of
> concordance, too. I'm trying to get Phil to do a release of concordance
> too.
I have some local mods (some selectively picked from the harmony_touch
branch) which I've rebased from e62e77c8c9d0 onto c32a5ce8572e and
adjusted to fit and they seem to be ok with a my local app (which uses
uses mhmanager) that lets me pull the config into a file. I have
another app which lets me program (somewhat) arbitrary IR codes without
learning that I've not tried since the rebase this morning.
I've pasted the raw diff from c32a5ce8572e below, I suspect there is a
bunch of redundant (to this branch at least) stuff from the
harmony_touch branch which I could drop.
I've been sitting on these changes for far too long and should submit
them. Would it be useful if I pulled at least the wsdl and xsd changes
into a PR ASAP or would you prefer to release what you have now and
take a look at this stuff later on?
The "apps" themselves are very skanky and specific to my mythtv setup
and some other tooling I wrote to help myself keep the key bindings
straight in my head.
CHeers,
Ian.
diff --git a/congruity/harmony.wsdl b/congruity/harmony.wsdl
index 2cf79aa..ff8a38f 100644
--- a/congruity/harmony.wsdl
+++ b/congruity/harmony.wsdl
@@ -126,10 +126,7 @@
<sequence>
<element name="DisplayName" type="xsd:string"/>
<element name="DisplayPriority" type="xsd:string"/>
- <element name="IsProSKUEnabled" type="xsd:boolean"/>
<element name="Name" type="xsd:string"/>
- <element name="ProSKUDisplayName" type="xsd:string" nillable="true"/>
- <element name="ProductFamily" type="xsd:string"/>
<element name="ProductId" type="xsd:string"/>
<element name="ProductIdentifier" type="xsd:string"/>
<element name="SkinId" type="xsd:string"/>
@@ -161,9 +158,11 @@
</sequence>
</complexType>
+ <!-- Contains a KeyValueOfDeviceIdArrayOfCommandXXXX where XXXX varies, not
+ sure how to deal with other than using an any.-->
<complexType name="GetCommandsResult">
<sequence>
- <element name="KeyValueOfDeviceIdArrayOfCommand8_PSBnnKs"
type="ns9:KeyValueOfDeviceIdArrayOfCommand"/>
+ <any/>
</sequence>
</complexType>
@@ -220,6 +219,20 @@
</sequence>
</complexType>
+ <complexType name="GetJson2UrisResult">
+ <sequence>
+ <element name="ServiceDescription" type="ns2:ServiceDescription"
minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ </complexType>
+
+ <complexType name="GetAllServicesResult">
+ <sequence>
+ <element name="Json2Services" type="ns2:ServiceDescriptions"/>
+ <element name="JsonServices" type="ns2:ServiceDescriptions"/>
+ <element name="SoapServices" type="ns2:ServiceDescriptions"/>
+ </sequence>
+ </complexType>
+
</schema>
</types>
@@ -541,11 +554,35 @@
<message name="SaveButtonMapsResponse">
</message>
+<message name="GetJson2Uris">
+ <part name="clientTypeId" type="xsd:string"/>
+ <part name="clientId" type="xsd:string"/>
+</message>
+<message name="GetJson2UrisResponse">
+ <part name="GetJson2UrisResult" type="GetJson2UrisResult"/>
+</message>
+
+<message name="GetAllServices">
+ <part name="clientTypeId" type="xsd:string"/>
+ <part name="clientId" type="xsd:string"/>
+</message>
+<message name="GetAllServicesResponse">
+ <part name="GetAllServicesResult" type="GetAllServicesResult"/>
+</message>
+
<portType name="DiscoveryPortType">
<operation name="GetSoapServices">
<input message="ns:GetSoapServices"/>
<output message="ns:GetSoapServicesResponse"/>
</operation>
+ <operation name="GetJson2Uris">
+ <input message="ns:GetJson2Uris"/>
+ <output message="ns:GetJson2UrisResponse"/>
+ </operation>
+ <operation name="GetAllServices">
+ <input message="ns:GetAllServices"/>
+ <output message="ns:GetAllServicesResponse"/>
+ </operation>
</portType>
<portType name="AuthenticationServicePortType">
<operation name="Login">
@@ -749,6 +786,24 @@
<SOAP:body use="literal" namespace="http://tempuri.org/"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>;
</output>
</operation>
+ <operation name="GetJson2Uris">
+ <SOAP:operation style="rpc"
soapAction="http://tempuri.org/IDiscovery/GetJson2Uris"/>;
+ <input>
+ <SOAP:body use="literal" namespace="http://tempuri.org/"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>;
+ </input>
+ <output>
+ <SOAP:body use="literal" namespace="http://tempuri.org/"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>;
+ </output>
+ </operation>
+ <operation name="GetAllServices">
+ <SOAP:operation style="rpc"
soapAction="http://tempuri.org/IDiscovery/GetAllServices"/>;
+ <input>
+ <SOAP:body use="literal" namespace="http://tempuri.org/"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>;
+ </input>
+ <output>
+ <SOAP:body use="literal" namespace="http://tempuri.org/"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>;
+ </output>
+ </operation>
</binding>
<binding name="AuthenticationServiceBinding"
type="ns:AuthenticationServicePortType">
<SOAP:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>;
diff --git a/congruity/mhgui.py b/congruity/mhgui.py
index 0f22af3..def104f 100644
--- a/congruity/mhgui.py
+++ b/congruity/mhgui.py
@@ -57,7 +57,9 @@ WATCH_TV_BUTTON_SKIN_IDS = [78, 79, 80, 81, 104]
try:
import argparse
except:
- use_local_wsdl = False
+ use_local_wsdl = True
+ if '--use-global-wsdl' in sys.argv:
+ use_local_wsdl = False
if '--use-local-wsdl' in sys.argv:
use_local_wsdl = True
suds_debug = False
diff --git a/congruity/mhmanager.py b/congruity/mhmanager.py
index ec0e2de..341e8cd 100644
--- a/congruity/mhmanager.py
+++ b/congruity/mhmanager.py
@@ -27,6 +27,7 @@ import sys
import random
import datetime
import json
+import collections
from six.moves.html_parser import HTMLParser
from suds.cache import ObjectCache
from suds.client import Client
@@ -119,6 +120,13 @@ ROLE_STRINGS = {
"VolumeActivityRole" : "control volume",
}
+# Change urllib.quote to use lowercase characters instead of uppercase. This
+# seems to be what the official software does and may be required.
+#urllib._safe_map = {}
+#for i, c in zip(xrange(256), str(bytearray(xrange(256)))):
+# urllib._safe_map[c] = c if (i < 128 and c in urllib.always_safe) else
'%{:02x}'.format(i)
+#urllib._safe_quoters = {}
+
class MHPlugin(MessagePlugin):
def fix_elements(self, prefix, elements):
for element in elements:
@@ -308,7 +316,7 @@ class MHManager():
self.client.service['UserButtonMappingManager'] \
.UpdateDeviceModeButtonMaps(buttonMaps)
- def GetUserButtonMap(self, deviceId):
+ def GetAllUserButtonMap(self, deviceId, activityId = None):
DeviceId = self.client.factory.create('{' + DATA_NS + '}DeviceId')
DeviceId.IsPersisted = deviceId.IsPersisted
DeviceId.Value = deviceId.Value
@@ -317,9 +325,21 @@ class MHManager():
accountId = self.GetAccountIdForDevice(deviceId)
remote = self.GetRemoteForAccountId(accountId)
surfaceId = remote.Surfaces.Surface[0].Id.Value
+ if activityId is not None:
+ ActivityId = self.client.factory.create('{' + DATA_NS +
'}ActivityId')
+ ActivityId.IsPersisted = activityId.IsPersisted
+ ActivityId.Value = activityId.Value
+ activityIds = self.client.factory.create('{' + DATA_NS +
'}abstractIds')
+ activityIds.AbstractId.append(ActivityId)
+ else:
+ activityIds = ""
return self.client.service['UserButtonMappingManager'] \
- .GetButtonMaps(deviceIds, "", remote.SkinId, accountId,
- surfaceId).AbstractButtonMap[0]
+ .GetButtonMaps(deviceIds, activityIds, remote.SkinId, accountId,
+ surfaceId).AbstractButtonMap
+ def GetUserButtonMap(self, deviceId):
+ return self.GetAllUserButtonMap(deviceId=deviceId)[0]
+ def GetActivityButtonMap(self, deviceId, activityId):
+ return self.GetAllUserButtonMap(deviceId, activityId)[1]
def UpdateUserButtonMap(self, userButtonMap, button, command):
accountId = self.GetAccountIdForDevice(userButtonMap.DeviceId)
@@ -710,7 +730,7 @@ class MHManager():
deviceWithCapabilities.PrioritizedCapabilities = \
device.DeviceCapabilitiesWithPriority
devicesWithCapabilities.DeviceWithCapabilities.append(
- deviceWithCapabilities)
+ deviceWithCapabilities)
return (self.client.service['ActivityManager'].GetActivityRoles(
account.Id, activityTypes, devicesWithCapabilities).\
KeyValueOfActivityTypeRoleToDeviceMapping_SFvkcgrh[0].Value.\
@@ -844,7 +864,7 @@ class MHManager():
activity.Type = saveActivityTemplate.activityType
activity.Name = saveActivityTemplate.activityName
activity.Roles = self.CreateRolesByTemplate(saveActivityTemplate)
- return self.SaveActivity(remoteId, activity)
+ return self.SaveActivity(remoteId, activity)
# Saves the specified activity
def SaveActivity(self, remoteId, activity):
@@ -923,7 +943,7 @@ class MHManager():
duration = "0"
else:
duration = action.Duration
- actions.append(("IRPressAction", action.IRCommandName,
+ actions.append(("IRPressAction", action.IRCommandName,
duration))
elif action.__class__.__name__ == "IRDelayAction":
actions.append(("IRDelayAction", None, action.Delay))
@@ -992,6 +1012,23 @@ class MHManager():
return "UpdateMultiple failed"
return None
+ def UpdateIRCommandKC(self, commandName, keyCode , deviceId):
+ operation = self.client.factory.create('{' + OPERATION_NS
+ + '}OperationBag')
+ operation.ParentAccount = self.GetAccountIdForDevice(deviceId)
+ operation.Items.Operation = self.client.factory.create(
+ '{' + DM_OPERATION_NS + '}AddCommandOperation'
+ )
+ operation.Items.Operation.ParentAccount = operation.ParentAccount
+ operation.Items.Operation.DeviceId = deviceId
+ operation.Items.Operation.KeyCode = keyCode
+ operation.Items.Operation.Name = commandName
+ operation.Items.Operation.RawInfrared = keyCode
+ result = self.client.service['DeviceManager'].UpdateMultiple(operation)
+ if result is None:
+ return "UpdateMultiple failed"
+ return None
+
# Deletes an IR command (if it is a user-added one) or removes the override
# if the command is an officially provided one.
def DeleteIRCommand(self, commandId, deviceId):
@@ -1019,6 +1056,101 @@ class MHManager():
return "UpdateMultiple failed"
return None
+ def GetJson2Uris(self, clientTypeId):
+ return self.client.service['Discovery'].GetJson2Uris(clientTypeId, \
+ None).ServiceDescription
+
+ def GetJson2Data(self, uris, requests):
+ responses = []
+ for request in requests:
+ realUri = self.GetRealUri(request["uri"], uris)
+ httpRequest = urllib2.Request(realUri)
+ try:
+ httpRequest.add_header("If-None-Match", request["etag"])
+ except KeyError:
+ pass
+ if httpRequest.get_host() == "content.dhg.myharmony.com":
+ httpRequest.add_header("Logitech-API-Key",
+ self.contentServiceAuthKey)
+ self.client.options.transport.cookiejar.add_cookie_header(
+ httpRequest)
+
+ response = collections.OrderedDict()
+ try:
+ fp = urllib2.urlopen(httpRequest)
+ headers = fp.info()
+ resource = json.loads(fp.read(),
+ object_pairs_hook=collections.OrderedDict)
+ statusCode = str(fp.getcode())
+ except urllib2.HTTPError as error:
+ headers = error.hdrs
+ resource = None
+ statusCode = str(error.code)
+ response["cache-Control"] = ""
+ response["body-type"] = "application/json"
+ response["etag"] = headers.get("ETag")
+ response["uri"] = request["uri"]
+ response["statusCode"] = statusCode
+ response["resource"] = resource
+
+ responses.append(response)
+
+ jsonStr = json.dumps({ "responses" : responses },
+ separators=(',', ':'), ensure_ascii=False)
+ # The dates appear to be getting changed to 'local' time but poorly.
+ # Only the local offset gets inserted, the timestamp doesn't change.
+ # Also, the slash needs to be escaped.
+ offset = '{0:0=+5}'.format(time.timezone / -36)
+ jsonStr = re.sub(r'\"/Date\(([0-9]+)[+-]([0-9]){4}\)/\"',
+ r'"\\/Date(\1' + offset + r')\\/"', jsonStr)
+ return urllib.quote_plus(jsonStr.encode("utf-8"), "()")
+
+ def GetRealUri(self, fakeUri, uris):
+ realUri = None
+ queryString = None
+ secondaryUri = None
+ if '?' in fakeUri:
+ queryString = fakeUri.split('?')[1]
+ fakeUri = fakeUri.split('?')[0]
+ if ';' in fakeUri:
+ secondaryUri = urllib.quote(fakeUri.split(';')[1], '')
+ fakeUri = fakeUri.split(';')[0]
+ if re.match("content://", fakeUri):
+ requestName = fakeUri.split('/')[3]
+ else:
+ requestName = fakeUri.split('/')[-1]
+ for uri in uris:
+ if str(uri.Name.lower()) == str(requestName.lower()):
+ realUri = uri.Address
+
+ # Fill out the placeholder fields
+ fakeUriSplit = fakeUri.split('/')
+ placeholders = ( ("{remoteId}", "Remote"),
+ ("{accountId}", "Account"),
+ ("{productId}", "product"),
+ ("{unitId}", "unit"),
+ ("{providerId}", "service"),
+ ("{countryCode}", "Country"),
+ ("{zipcode}", "Region"),
+ ("{stationId}", "station"), )
+ for placeholderTag, previous in placeholders:
+ if placeholderTag in realUri:
+ realData = fakeUriSplit[fakeUriSplit.index(previous) + 1]
+ realUri = realUri.replace(placeholderTag, realData)
+ uriPlaceholders = ("{userProfileUri}", "{deviceProfileUri}",
+ "{accountUri}")
+ for placeholderTag in uriPlaceholders:
+ if placeholderTag in realUri:
+ realUri = realUri.replace(placeholderTag, secondaryUri)
+
+ if queryString:
+ realUri += "?" + queryString + "&"
+ else:
+ realUri += "?"
+ realUri += "guid=" + str(uuid.uuid4())
+
+ return realUri
+
class ActivityTemplate:
def __init__(self):
self.devices = None # List of (Device Name, DeviceId)
diff --git a/congruity/user_button_mapping.xsd
b/congruity/user_button_mapping.xsd
index 50d86d6..4a4c561 100644
--- a/congruity/user_button_mapping.xsd
+++ b/congruity/user_button_mapping.xsd
@@ -13,6 +13,7 @@
<element name="EventType" type="xsd:string"/>
<element name="Id" type="xsd:string"/>
<element name="Order" type="xsd:string"/>
+ <element name="ActionName" type="xsd:string"/>
</sequence>
</complexType>
@@ -54,6 +55,10 @@
<element name="ButtonLongPressAction" type="ns15:AbstractButtonAction"
nillable="true"/>
<element name="ButtonState" type="xsd:string"/>
<element name="FunctionGroupType" type="xsd:string"/>
+ <element name="ButtonKey" type="xsd:string"/>
+ <!-- Not seen non-null yet -->
+ <element name="ButtonImageKey" type="xsd:string" nillable="true"/>
+ <element name="ButtonImagePath" type="xsd:string" nillable="true"/>
</sequence>
</complexType>
diff --git a/congruity/user_feature.xsd b/congruity/user_feature.xsd
index db7a69e..7dd5d07 100644
--- a/congruity/user_feature.xsd
+++ b/congruity/user_feature.xsd
@@ -72,6 +72,7 @@
<element name="PressDuration" type="xsd:string"/>
<element name="StateName" type="xsd:string" nillable="true"/>
<element name="StateValue" type="xsd:string" nillable="true"/>
+ <element name="Attributes" type="xsd:string" nillable="true"/> <!-- I've
never seen a non-nil one, so type is a guess -->
</sequence>
</complexType>
diff --git a/mhfetch b/mhfetch
new file mode 100755
index 0000000..a1b3886
--- /dev/null
+++ b/mhfetch
@@ -0,0 +1,311 @@
+#!/usr/bin/env python2
+import sys, os, re
+
+sys.path.append('/local/scratch/ijc/development/pvr/harmony/congruity/build/lib.linux-x86_64-2.7/congruity')
+
+from urllib2 import URLError
+
+sys.path.append('/usr/share/congruity')
+from mhmanager import MHManager
+from mhmanager import MHAccountDetails
+from mhmanager import SaveActivityTemplate
+from mhmanager import Secrets
+
+odir = "/home/ijc/development/pvr/lirc-remote-config/Harmony Ultimate One"
+try:
+ import argparse
+except:
+ use_local_wsdl = True
+ if '--use-global-wsdl' in sys.argv:
+ use_local_wsdl = False
+ if '--use-local-wsdl' in sys.argv:
+ use_local_wsdl = True
+ suds_debug = False
+ if '--suds-debug' in sys.argv:
+ suds_debug = True
+else:
+ parser = argparse.ArgumentParser(description='Manage Logitech Harmony
Remotes.')
+ parser.add_argument('-d', '--suds-debug', help='output SOAP messages',
+ action='store_true')
+ parser.add_argument('-l', '--use-local-wsdl', help='use local wsdl file',
+ action='store_true')
+ args = parser.parse_args()
+ suds_debug = args.suds_debug
+ use_local_wsdl = args.use_local_wsdl
+
+if not use_local_wsdl:
+ print >>sys.stderr, "Forcing local WSDL"
+ use_local_wsdl = True
+
+mhMgr = MHManager(use_local_wsdl, suds_debug)
+
+import libconcord
+
+username, password = "[email protected]", "«password»
+#from threading import Condition
+#username, password = None, None
+#
+#secrets = Secrets()
+#cv = Condition()
+#def OnUserFetched(_username, _password):
+# print "Got username+password"
+# username = _username
+# password = _password
+# cv.acquire()
+# cv.notify()
+# cv.release()
+#
+#cv.acquire()
+#try:
+# secrets.fetchUser(OnUserFetched)
+# print "Waiting for login details"
+# cv.wait(3)
+#except e:
+# print "Waiting for login details: %s" % e
+#cv.release()
+#if username is None or password is None:
+# print "No credentials"
+# sys.exit(1)
+try:
+ print "Logging in"
+ loginResult = mhMgr.Login(username, password)
+except URLError as e:
+ loginResult = e.reason
+
+if loginResult is True:
+ print "Login successful"
+else:
+ if loginResult is None:
+ msg = 'You appear to have used a members.harmonyremote.com ' \
+ 'account. Please create a myharmony.com account or ' \
+ 'login with an existing one.'
+ elif loginResult is False:
+ msg = 'Login failed. Username or password incorrect.'
+ else:
+ msg = 'Login failed. %s' % loginResult
+ print(msg)
+ os.exit(1)
+
+print "Found Remotes:"
+remotes = mhMgr.GetRemotes()
+products = {}
+for remote in remotes:
+ product = mhMgr.GetProduct(remote.SkinId)
+ products[remote] = product
+ print " - %s" % product.DisplayName
+print
+if len(remotes) != 1:
+ print "Unexpected number of remotes"
+
+remote = remotes[0]
+product = products[remote]
+
+print "Selected remote: %s" % product.DisplayName
+print
+
+devices = mhMgr.GetDevices(remote.Id)
+print "Devices:"
+for d in devices:
+ print " - %s - %s" % (d.ParentDeviceManufacturer, d.ParentDeviceModel)
+ if d.ParentDeviceManufacturer == "Microsoft" and d.ParentDeviceModel ==
"Windows Media Center":
+ device = d
+print
+
+def buttonMapToXml(n, mythsource, jumpcmd, fn, bm):
+ mythContexts = {
+ "Recorded TV": "TV Playback",
+ "Music": "Music",
+ "Movies": "TV Playback",
+ "Videos": "Video",
+ "Pictures": "Gallery",
+ "Live TV": "TV Playback",
+ }
+ f = open(os.path.join(odir,fn), "w")
+ if mythsource is not None:
+ mythcontext = mythContexts.get(mythsource, None)
+ if mythcontext is not None:
+ if jumpcmd is not None:
+ f.write("<!-- \t<jump source=\"%s\" command=\"%s\" /> -->\n" %
(mythsource, jumpcmd))
+ else:
+ f.write("<!-- \t<jump source=\"%s\" /> -->\n" % (mythsource))
+ f.write("\t<activity name=\"%s\" mythsource=\"%s\"
mythcontext=\"%s\">\n" % (n, mythsource, mythcontext))
+ else:
+ f.write("\t<activity name=\"%s\" mythsource=\"%s\">\n" % (n,
mythsource))
+ else:
+ f.write("\t<activity name=\"%s\">\n" % n)
+
+ nrcustom = 1
+ for b in bm.Buttons[0]:
+ #f.write("<!-- %s -->\n" % b)
+ try:
+ b.ButtonAction.ActionName # If present then is a submenu...
+ continue
+ except:
+ pass
+
+ name = None
+ soft = ""
+ try:
+ b.TextOnRemote
+ #name = "Soft %02d: %s" % (nrcustom, b.TextOnRemote)
+ name = b.TextOnRemote
+ #name = "Soft %d" % (nrcustom)
+ soft = " soft=\"true\""
+ nrcustom += 1
+ except:
+ pass
+ try:
+ name = b.ButtonKey
+ except:
+ pass
+
+ #Cannot detect gestures?
+
+ if name is None:
+ f.write("<!-- %s -->\n" % b)
+ continue
+
+ f.write("\t\t<mapping button=\"%s\"" % (name))
+ try:
+ f.write(" command=\"%s\"" % b.ButtonAction.CommandName)
+ except:
+ pass
+ try:
+ f.write(" long=\"%s\"" % b.ButtonLongPressAction.CommandName)
+ except:
+ pass
+ try:
+ f.write(" double=\"%s\"" % b.ButtonDoublePressAction.CommandName)
+ except:
+ pass
+ f.write(soft)
+ f.write(" />\n")
+ #print >>f, "%s: Press:%s Dbl:%s Long:%s" % (name,
+ # b.ButtonAction and
b.ButtonAction.CommandName or "<None>",
+ # b.ButtonDoublePressAction and
b.ButtonDoublePressAction.CommandName or "<None>",
+ # b.ButtonLongPressAction and
b.ButtonLongPressAction.CommandName or "<None>")
+ f.write("\t</activity>\n")
+ f.close()
+
+print "Device: %s (%s - %s)" % (device.Name, device.ParentDeviceManufacturer,
device.ParentDeviceModel)
+print
+
+ubm = mhMgr.GetUserButtonMap(device.Id)
+f = open(os.path.join(odir, "%s.%s.map" % (device.Name, "Device")), "w")
+print >>f, ubm
+f.close()
+
+buttonMapToXml(device.Name + " Device Mode", None, None, "%s.%s.xml" %
(device.Name, "Device"), ubm)
+
+
+f = open(os.path.join(odir, "%s.%s.xml" % (device.Name, "Command")), "w")
+f.write("<!-- %s Commands -->\n" % device.Name)
+ms30bit = re.compile("\(\)\(0[xX]3FF0(.*)\)\(\)")
+def parseKeyCode(kc):
+ g,p,sc,n = kc.split(":")
+ if g == "G" and p == "Microsoft 30 Bit" and n == "3":
+ r = ms30bit.match(sc)
+ if r is not None:
+ return r.group(1)
+ return None
+cs = mhMgr.GetCommands(device.Id)
+cs.sort(key=lambda x: x.KeyCode)
+for c in cs:
+ sc = parseKeyCode(c.KeyCode)
+ if sc is not None:
+ #f.write("\t<command name=\"%s\"\tscancode=\"0x0000%s\"\t/>\n" %
(c.Name, sc.lower()))
+ f.write("\t<command scancode=\"0x0000%s\"\tname=\"%s\"\t/>\n" %
(sc.lower(), c.Name))
+ else:
+ f.write("\t<!-- %s: %s /-->\n" % (c.Name,c.KeyCode))
+f.close()
+
+
+activities = mhMgr.GetActivities(remote.Id)
+print "Activities:"
+for activity in activities:
+ f = open(os.path.join(odir, "%s.%s.act" % (device.Name, activity.Name)),
"w")
+ print >>f, activity
+ f.close()
+
+ print " - %s" % activity.Name
+ #_, devices = mhMgr.GetActivityRolesAndDevices(remote.Id, activity.Type)
+ #for d in devices:
+ # print " - %s - %s (%x)" % (d.ParentDeviceManufacturer,
d.ParentDeviceModel, d.Id.Value)
+
+ ubm = mhMgr.GetActivityButtonMap(device.Id, activity.Id)
+ f = open(os.path.join(odir, "%s.%s.map" % (device.Name, activity.Name)),
"w")
+ print >>f, ubm
+ f.close()
+ i = mhMgr.GetDeviceInput(activity, device.Id) or None
+ js = [j.SelectedInput.Name for j in activity.Roles[0] if j.DeviceId.Value
== device.Id.Value]
+ if len(js) > 0:
+ j = js[0]
+ else:
+ j = None
+ buttonMapToXml(activity.Name, i, j, "%s.%s.xml" % (device.Name,
activity.Name), ubm)
+print
+
+#activity = activities[0]
+#print "Selected activity: %s" % activity.Name
+
+#template = mhMgr.GetActivityTemplate(remote.Id,activity)
+#print activity.Name, template
+#def interact():
+# import code
+# code.InteractiveConsole(locals=globals()).interact()
+#interact()
+
+#roles, devices = mhMgr.GetActivityRolesAndDevices(remote.Id, activity.Type)
+#print "Devices:"
+#for d in devices:
+# print " - %s - %s" % (d.ParentDeviceManufacturer, d.ParentDeviceModel)
+# if d.ParentDeviceManufacturer == "Microsoft" and d.ParentDeviceModel ==
"Windows Media Center":
+# device = d
+#print
+#
+#print "Device: %s - %s" % (device.ParentDeviceManufacturer,
device.ParentDeviceModel)
+
+#ubm = mhMgr.GetActivityButtonMap(device.Id, activity.Id)
+#ubm = mhMgr.GetAllUserButtonMap(device.Id, activity.Id)
+#f = open("%s.%s.map" % (device.Name, "Device"), "w")
+#print >>f, ubm[0]
+#f.close()
+#f = open("%s.%s.map" % (device.Name, activity.Name), "w")
+#print >>f, ubm[1]
+#f.close()
+
+#print
+#for b in ubm[1].Buttons[0]:
+# name = None
+# try:
+# name = b.TextOnRemote
+# except:
+# pass
+# try:
+# name = b.ButtonKey
+# except:
+# pass
+#
+# #if isinstance(b, suds.sudsobject.SoftRemoteButton):
+# # name = b.TextOnRemote
+# #elif isinstance(b, suds.sudsobject.HardRemoteButton):
+# # name = b.ButtonKey
+# #elif isinstance(b, suds.sudsobject.AbstraceRemoteButton):
+# # name = b.ButtonKey
+# #else:
+# # name = type(b)
+# print "%s: Press:%s Dbl:%s Long:%s" % (name,
+# b.ButtonAction and
b.ButtonAction.CommandName or "<None>",
+# b.ButtonDoublePressAction and
b.ButtonDoublePressAction.CommandName or "<None>",
+# b.ButtonLongPressAction and
b.ButtonLongPressAction.CommandName or "<None>")
+
+#t = mhMgr.GetActivityTemplate(remote.Id, activity.Type) #YYY b:Attributes
+#ubm = mhMgr.GetUserButtonMap(device.Id) #YYY a:ActionName
+#ubm = mhMgr.GetUserButtonMap(device.Id, activity)
+#bm = mhMgr.GetButtonMap(device.Id)
+#cs = mhMgr.GetCommands(device.Id)
+# mhMgr.GetRemoteCanvas(remote.SkinId)
+# mhMgr.GetRemoteCanvas(remote.Surfaces[0][0].SkinId)
+# mhMgr.GetConfig(remote, "foo") #XXX
+# mhMgr.GetUserFeatures(device.Id) #XXX b:Attributes
+# mhMgr.GetPowerFeature(device.Id) #XXX b:Attributes
diff --git a/mhprogram b/mhprogram
new file mode 100755
index 0000000..c349303
--- /dev/null
+++ b/mhprogram
@@ -0,0 +1,100 @@
+#!/usr/bin/env python2
+import sys, os, re
+
+from urllib2 import URLError
+
+sys.path.append('/usr/share/congruity')
+from mhmanager import MHManager
+from mhmanager import MHAccountDetails
+from mhmanager import SaveActivityTemplate
+from mhmanager import Secrets
+
+try:
+ import argparse
+except:
+ use_local_wsdl = True
+ if '--use-global-wsdl' in sys.argv:
+ use_local_wsdl = False
+ if '--use-local-wsdl' in sys.argv:
+ use_local_wsdl = True
+ suds_debug = False
+ if '--suds-debug' in sys.argv:
+ suds_debug = True
+else:
+ parser = argparse.ArgumentParser(description='Manage Logitech Harmony
Remotes.')
+ parser.add_argument('-d', '--suds-debug', help='output SOAP messages',
+ action='store_true')
+ parser.add_argument('-l', '--use-local-wsdl', help='use local wsdl file',
+ action='store_true')
+ args = parser.parse_args()
+ suds_debug = args.suds_debug
+ use_local_wsdl = args.use_local_wsdl
+
+mhMgr = MHManager(use_local_wsdl, suds_debug)
+
+import libconcord
+
+username, password = "[email protected]", "«password»"
+try:
+ print "Logging in"
+ loginResult = mhMgr.Login(username, password)
+except URLError as e:
+ loginResult = e.reason
+
+if loginResult is True:
+ print "Login successful"
+else:
+ if loginResult is None:
+ msg = 'You appear to have used a members.harmonyremote.com ' \
+ 'account. Please create a myharmony.com account or ' \
+ 'login with an existing one.'
+ elif loginResult is False:
+ msg = 'Login failed. Username or password incorrect.'
+ else:
+ msg = 'Login failed. %s' % loginResult
+ print(msg)
+ os.exit(1)
+
+print "Found Remotes:"
+remotes = mhMgr.GetRemotes()
+products = {}
+for remote in remotes:
+ product = mhMgr.GetProduct(remote.SkinId)
+ products[remote] = product
+ print " - %s" % product.DisplayName
+print
+if len(remotes) != 1:
+ print "Unexpected number of remotes"
+
+remote = remotes[0]
+product = products[remote]
+
+print "Selected remote: %s" % product.DisplayName
+print
+
+devices = mhMgr.GetDevices(remote.Id)
+print "Devices:"
+for d in devices:
+ print " - %s - %s" % (d.ParentDeviceManufacturer, d.ParentDeviceModel)
+ if d.ParentDeviceManufacturer == "Microsoft" and d.ParentDeviceModel ==
"Windows Media Center":
+ device = d
+print
+
+
+print "Device: %s (%s - %s)" % (device.Name, device.ParentDeviceManufacturer,
device.ParentDeviceModel)
+print
+
+#r = mhMgr.UpdateIRCommandKC("BAE", "G:Microsoft 30 Bit:()(0x3FF07BAE)():3",
device.Id)
+#r = mhMgr.UpdateIRCommandKC("AFE", "G:Microsoft 30 Bit:()(0x3FF07AFE)():3",
device.Id)
+#r = mhMgr.UpdateIRCommandKC("AFF", "G:Microsoft 30 Bit:()(0x3FF07AFF)():3",
device.Id)
+#r = mhMgr.UpdateIRCommandKC("C00", "G:Microsoft 30 Bit:()(0x3FF07C00)():3",
device.Id)
+#r = mhMgr.UpdateIRCommandKC("C01", "G:Microsoft 30 Bit:()(0x3FF07C01)():3",
device.Id)
+
+#for code in range(0xb00,0xb10):
+#for code in range(0xb10,0xb20)+range(0xba0,0xbb0):
+for code in range(0xb00,0xb02)+range(0xc01,0xc40):
+ n=hex(code).upper().lstrip("0X")
+ c=hex(0x3ff07000 + code).upper().lstrip("0X")
+ c="G:Microsoft 30 Bit:()(0x"+c+")():3"
+ t=mhMgr.UpdateIRCommandKC(n, c, device.Id)
+ print n,c,"Result",t
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
concordance-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/concordance-devel