Return update results as unified UpdateInfo objects. Prepared more

features in OptionParser.
This commit is contained in:
Markus Birth 2019-09-16 02:59:30 +02:00
parent 64be0eb894
commit 319197c88b
Signed by: mbirth
GPG Key ID: A9928D7A098C3A9A
2 changed files with 99 additions and 3 deletions

View File

@ -20,6 +20,8 @@ optp.add_option("-W", "--webupdater", action="store_false", dest="express", defa
optp.add_option("--id", dest="unit_id", help="Specify custom Unit ID")
optp.add_option("--code", action="append", dest="unlock_codes", metavar="UNLOCK_CODE", default=[], help="Specify map unlock codes")
optp.add_option("--devicexml", dest="devicexml", metavar="FILE", help="Use specified GarminDevice.xml (also implies -E)")
optp.add_option("--json", action="store_true", dest="json", default=False, help="Output JSON")
optp.add_option("--list-devices", action="store_true", dest="list_devices", default=False, help="Show a list of SKUs and product names")
optp.usage = """
%prog [options] SKU1 [SKU2..SKUn]

View File

@ -7,7 +7,7 @@ See https://github.com/AlexWhiter/GarminRelatedStuff/tree/master/GetFirmwareUpda
from . import devices
from .proto import GetAllUnitSoftwareUpdates_pb2
from xml.dom.minidom import getDOMImplementation
from xml.dom.minidom import getDOMImplementation, parseString
import requests
PROTO_API_GETALLUNITSOFTWAREUPDATES_URL = "http://omt.garmin.com/Rce/ProtobufApi/SoftwareUpdateService/GetAllUnitSoftwareUpdates"
@ -16,8 +16,85 @@ GRMN_CLIENT_VERSION = "6.17.0.0"
class UpdateInfo:
def __init__(self):
self.source = None
self.sku = None
self.device_name = None
self.fw_version = None
self.license_url = None
self.changelog = None
self.notes = None
self.language_code = None
self.update_type = None
self.local_filename = None
self.files = []
self.build_type = None
self.additional_info_url = None
def fill_from_protobuf(self, protobuf_info):
self.source = "Express"
self.sku = protobuf_info.product_sku
self.device_name = protobuf_info.device_name
self.fw_version = protobuf_info.fw_version
self.license_url = protobuf_info.license_url
self.changelog = "\n".join(protobuf_info.changelog)
self.language_code = protobuf_info.language
self.local_filename = protobuf_info.update_file
self.update_type = protobuf_info.file_type
for i in protobuf_info.files_list:
self.files.append( {
"url": i.url,
"md5": i.md5,
"size": i.file_size,
"size_is_rounded": False,
} )
# From https://docs.python.org/3/library/xml.dom.minidom.html
def dom_get_text(self, node_list):
rc = []
for rnode in node_list:
for node in rnode.childNodes:
if node.nodeType == node.TEXT_NODE:
rc.append(node.data)
return ''.join(rc)
def fill_from_response_dom(self, dom):
self.source = "WebUpdater"
self.sku = self.dom_get_text(dom.getElementsByTagName("RequestedPartNumber"))
self.device_name = self.dom_get_text(dom.getElementsByTagName("Description"))
version_major = self.dom_get_text(dom.getElementsByTagName("VersionMajor"))
version_minor = self.dom_get_text(dom.getElementsByTagName("VersionMinor"))
self.fw_version = "{}.{:0>2s}".format(version_major, version_minor)
self.license_url = self.dom_get_text(dom.getElementsByTagName("LicenseLocation"))
self.changelog = self.dom_get_text(dom.getElementsByTagName("ChangeDescription"))
self.notes = self.dom_get_text(dom.getElementsByTagName("Notes"))
self.language_code = self.dom_get_text(dom.getElementsByTagName("RequestedRegionId"))
self.build_type = self.dom_get_text(dom.getElementsByTagName("BuildType"))
self.additional_info_url = self.dom_get_text(dom.getElementsByTagName("AdditionalInfo"))
files = dom.getElementsByTagName("UpdateFile")
for f in files:
# For some reason, WebUpdater returns file size in KiB instead of Bytes
size_kb = self.dom_get_text(f.getElementsByTagName("Size"))
size_bytes = float(size_kb) * 1024
self.files.append( {
"url": self.dom_get_text(f.getElementsByTagName("Location")),
"md5": self.dom_get_text(f.getElementsByTagName("MD5Sum")),
"size": size_bytes,
"size_is_rounded": True,
} )
def get_json(self):
# TODO
pass
def __str__(self):
url = "-"
if len(self.files) > 0:
url = self.files[0]["url"]
return "[{}] {} {} {}: {}".format(self.source, self.sku, self.device_name, self.fw_version, url)
def __repr__(self):
return "[{}] {} {} {}".format(self.source, self.sku, self.device_name, self.fw_version)
class UpdateServer:
def __init__(self):
@ -29,13 +106,30 @@ class UpdateServer:
# Garmin Express Protobuf API
device_xml = self.get_device_xml(sku_numbers)
reply = self.get_unit_updates(device_xml)
return reply
results = []
for i in range(0, len(reply.update_info)):
ui = reply.update_info[i]
r = UpdateInfo()
r.fill_from_protobuf(ui)
results.append(r)
return results
def query_webupdater(self, sku_numbers):
# WebUpdater
requests_xml = self.get_requests_xml(sku_numbers)
reply = self.get_webupdater_softwareupdate(requests_xml)
return reply
# ElementTree might have been easier if it wouldn't be so obnoxious with namespaces
# See https://stackoverflow.com/questions/14853243/parsing-xml-with-namespace-in-python-via-elementtree
dom = parseString(reply)
results = []
for resp in dom.getElementsByTagName("Response"):
r = UpdateInfo()
r.fill_from_response_dom(resp)
results.append(r)
return results
def query_updates(self, sku_numbers, query_express=True, query_webupdater=True):
results = []