From fb497a9251a5e84aa0f98eb6c8a23fcd4e32aadd Mon Sep 17 00:00:00 2001 From: Peter Hudec Date: Tue, 21 Nov 2023 15:33:13 +0100 Subject: [PATCH] first commit --- README.md | 16 ++++ requirements.txt | 5 + unifi-firmware-downlaod.sh | 23 +++++ unifi-firmware-download.py | 190 +++++++++++++++++++++++++++++++++++++ unifi-product-codes.txt | 117 +++++++++++++++++++++++ 5 files changed, 351 insertions(+) create mode 100644 README.md create mode 100644 requirements.txt create mode 100644 unifi-firmware-downlaod.sh create mode 100644 unifi-firmware-download.py create mode 100644 unifi-product-codes.txt diff --git a/README.md b/README.md new file mode 100644 index 0000000..54ec13d --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# Offline Unifi Controller downloader + +## Purpose + +If your unifi controleer do not have access to the interner os is behing the corporate proxy +the controller is not able to download the updates for firmwares. + + +## Solution + +Download the firmwares and upload to the controller directory + + +## Run + +See `unifi-firmware-downlaod.sh` diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..35426c1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +certifi==2023.11.17 +charset-normalizer==3.3.2 +idna==3.4 +requests==2.31.0 +urllib3==2.1.0 diff --git a/unifi-firmware-downlaod.sh b/unifi-firmware-downlaod.sh new file mode 100644 index 0000000..808ea38 --- /dev/null +++ b/unifi-firmware-downlaod.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +DIR_NAME=$(dirname $0) + +PLATFORM=() +PLATFORM+=('U7PG2') +PLATFORM+=('U7MP') +PLATFORM+=('U7EDU') +PLATFORM+=('U7UKU') +PLATFORM+=('U7IW') +PLATFORM+=('U7IWP') +PLATFORM+=('U7MSH') +PLATFORM+=('U7LT') +PLATFORM+=('U7LR') +PLATFORM+=('U2O') +PLATFORM+=('BZ2') +PLATFORM+=('BZ2LR') +PLATFORM+=('U5O') +PLATFORM+=('U7P') + +for VALUE in "${PLATFORM[@]}"; do + ${DIR_NAME}/env/bin/python ${DIR_NAME}/unifi-firmware-downlaod.py -f /var/lib/unifi/firmware -p ${VALUE} +done diff --git a/unifi-firmware-download.py b/unifi-firmware-download.py new file mode 100644 index 0000000..6d27327 --- /dev/null +++ b/unifi-firmware-download.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python + +import logging +import os +import json +import requests + +def load_json(filename): + logging.info(f"reading json file {filename}") + try: + with open(filename) as f: + data = json.load(f) + return data + except: + logging.exception(f"could not read file {filename}") + return None + +def write_json(filename, data): + logging.info(f"writing json file {filename}") + try: + with open(filename, "w") as f: + json.dump(data, f) + except: + logging.exception(f"could not write file {filename}") + +def load_firmware_meta(path): + try: + firmware_meta = load_json(os.path.join(path, "firmware_meta.json")) + if firmware_meta is None: + return [] + return firmware_meta.get('cached_firmwares', []) + except: + logging.exception(f"could load firmware meta") + return [] + return firmware_meta + +def write_firmware_meta(path, data): + try: + firmware_meta = { + 'cached_firmwares': data + } + write_json(os.path.join(path, "firmware_meta.json"), firmware_meta) + except: + logging.exception(f"could write firmware meta") + +def online_firmware_check(): + try: + resp = requests.get("https://fw-update.ubnt.com/api/firmware-newest", + params={"filter": ["eq~~product~~unifi-firmware", "eq~~channel~~release"]}) + data = resp.json() + return data.get("_embedded").get("firmware", []) + except: + logging.exception(f"could load firmware updates") + return [] + +def online_firmware_download(url, filename): + try: + path = os.path.dirname(filename) + os.makedirs(path, exist_ok=True) + response = requests.get(url, stream=True) + with open(filename, mode="wb") as file: + for chunk in response.iter_content(chunk_size=10 * 1024): + file.write(chunk) + except: + logging.exception(f"could not download firmware {url}") + return False + else: + return True + +def main(args): + firmware_meta = load_firmware_meta(args.firmware_dir) + firmware_latest = online_firmware_check() + + # loop over the all available firmware + # if found in 'meta', just add device to ther list + # we assume, the firmware is already downloaded + # if not found in 'meta', add ne item and download firmware + + for firmware in firmware_latest: + firmware_platform = firmware.get('platform') + firmware_version = [ + str(firmware.get('version_major')), + str(firmware.get('version_minor')), + str(firmware.get('version_patch')), + str(firmware.get('version_build')), + ] + firmware_version = ".".join(firmware_version) + + if args.platform is not None: + if firmware_platform not in args.platform: + continue + + logging.info(f"processing firmware {firmware_platform}/{firmware_version}") + + found = None + + for meta in firmware_meta: + # compare some values + if meta.get('version') != firmware_version: + continue + if meta.get('md5') != firmware.get('md5'): + continue + if meta.get('size') != firmware.get('file_size'): + continue + # if all values match, we got our item + found = meta + break + + # just add device to the list + if found is not None: + if firmware.get('platform') not in found.get('devices'): + found['devices'].append(firmware.get('platform')) + continue + + firmware_url = firmware.get('_links', {}).get('data', {}).get('href', {}) + if firmware_url is None: + logging.error(f"unalbe to get firmware path for {firmware_platform}/{firmware_version}") + continue + found = { + 'md5': firmware.get('md5'), + 'version': firmware_version, + 'size': firmware.get('file_size'), + 'path': os.path.join(firmware.get('platform'), firmware_version,firmware_url.split("/")[-1]), + 'devices': [firmware.get('platform')] + } + if online_firmware_download(firmware_url, os.path.join(args.firmware_dir, found['path'])): + firmware_meta.append(found) + + write_firmware_meta(args.firmware_dir, firmware_meta) + +if __name__ == "__main__": + + format = "%(asctime)s: %(message)s" + logging.basicConfig(format=format, level=logging.DEBUG, datefmt="%H:%M:%S") + + import argparse + parser = argparse.ArgumentParser() + parser.add_argument("-f", "--firmware-dir", type=str, required=True, help="directory with firmwares") + parser.add_argument("-p","--platform", action='append', help='platform list to download', required=False) + args = parser.parse_args() + main(args) + +''' + { + "md5": "6c7257dd9ee9342f376f4b140df1f9a0", + "version": "4.3.28.11361", + "size": 7250217, + "path": "U7P/4.3.28.11361/a1e0-U7P-4.3.28-62a146a0d8dd4161a291a74fd562b9ce.bin", + "devices": [ + "U7P" + ] + } +''' + +''' + { + "channel": "release", + "created": "2021-02-10T09:03:12Z", + "file_size": 7252057, + "id": "3a44dd11-6c86-45ef-8d4e-5dbb91349ea7", + "md5": "02da6330a4cf868d02819bce4d583651", + "sha256_checksum": "255736db010c226897c10bb32818992401aaf9b942910caafae7c08d5fab06a7", + "platform": "BZ2", + "product": "unifi-firmware", + "updated": "2021-02-10T09:03:15Z", + "version": "v4.3.28+11361", + "version_major": 4, + "version_minor": 3, + "version_patch": 28, + "version_build": "11361", + "_links": { + "self": { + "href": "https://fw-update.ubnt.com/api/firmware/3a44dd11-6c86-45ef-8d4e-5dbb91349ea7" + }, + "upload": [ + { + "name": "data", + "href": "https://fw-update.ubnt.com/api/firmware/3a44dd11-6c86-45ef-8d4e-5dbb91349ea7/data" + }, + { + "name": "changelog", + "href": "https://fw-update.ubnt.com/api/firmware/3a44dd11-6c86-45ef-8d4e-5dbb91349ea7/changelog" + } + ], + "data": { + "href": "https://fw-download.ubnt.com/data/unifi-firmware/8822-BZ2-4.3.28-3a44dd116c8645ef8d4e5dbb91349ea7.bin" + } + } + }, +''' diff --git a/unifi-product-codes.txt b/unifi-product-codes.txt new file mode 100644 index 0000000..3dea2c9 --- /dev/null +++ b/unifi-product-codes.txt @@ -0,0 +1,117 @@ +https://ubntwiki.com/products/software/unifi-controller/api#site_endpoints +https://www.reddit.com/r/Ubiquiti/comments/kxudok/updated_list_device_codes/ + +Model Type ProductName +----- ---- ----------- +BZ2 uap UAP +BZ2LR uap UAP-LR +U2HSR uap UAP-Outdoor+ +U2IW uap UAP-IW +U2L48 uap UAP-LR +U2Lv2 uap UAP-LRv2 +U2M uap UAP-Mini +U2O uap UAP-Outdoor +U2S48 uap UAP +U2Sv2 uap UAPv2 +U5O uap UAP-Outdoor5 +U7E uap UAP-AC +U7EDU uap UAP-AC-EDU +U7Ev2 uap UAP-AC +U7HD uap UAP-AC-HD +U7SHD uap UAP-AC-SHD +U7NHD uap UAP-nanoHD +UFLHD uap UAP-FlexHD +UHDIW uap UAP-IW-HD +UAIW6 uap U6-IW +UAE6 uap U6-Extender +UAL6 uap U6-Lite +UAM6 uap U6-Mesh +UALR6 uap U6-LR-EA +UAP6 uap U6-LR +UALR6v2 uap U6-LR +UALR6v3 uap U6-LR +UCXG uap UAP-XG +UXSDM uap UWB-XG +UXBSDM uap UWB-XG-BK +UCMSH uap UAP-XG-Mesh +U7IW uap UAP-AC-IW +U7IWP uap UAP-AC-IW-Pro +U7MP uap UAP-AC-M-Pro +U7LR uap UAP-AC-LR +U7LT uap UAP-AC-Lite +U7O uap UAP-AC-Outdoor +U7P uap UAP-Pro +U7MSH uap UAP-AC-M +U7PG2 uap UAP-AC-Pro +p2N uap PICOM2HP +UDMB uap UAP-BeaconHD +USF5P usw USW-Flex +US8 usw US-8 +US8P60 usw US-8-60W +US8P150 usw US-8-150W +S28150 usw US-8-150W +USC8 usw US-8 +USC8P60 usw US-8-60W +USC8P150 usw US-8-150W +US16P150 usw US-16-150W +S216150 usw US-16-150W +US24 usw US-24-G1 +US24PRO usw USW-Pro-24-PoE +US24PRO2 usw USW-Pro-24 +US24P250 usw US-24-250W +US24PL2 usw US-L2-24-PoE +US24P500 usw US-24-500W +S224250 usw US-24-250W +S224500 usw US-24-500W +US48 usw US-48-G1 +US48PRO usw USW-Pro-48-PoE +US48PRO2 usw USW-Pro-48 +US48P500 usw US-48-500W +US48PL2 usw US-L2-48-PoE +US48P750 usw US-48-750W +S248500 usw US-48-500W +S248750 usw US-48-750W +US6XG150 usw US-XG-6PoE +USMINI usw USW-Flex-Mini +USXG usw US-16-XG +USC8P450 usw USW-Industrial +UDC48X6 usw USW-Leaf +USL8A usw UniFi Switch Aggregation +USL8LP usw USW-Lite-8-PoE +USL8MP usw USW-Mission-Critical +USL16P usw USW-16-PoE +USL16LP usw USW-Lite-16-PoE +USL24 usw USW-24-G2 +USL48 usw USW-48-G2 +USL24P usw USW-24-PoE +USL48P usw USW-48-PoE +UGW3 ugw USG-3P +UGW4 ugw USG-Pro-4 +UGWHD4 ugw USG +UGWXG ugw USG-XG-8 +UDM udm UDM +UDMSE udm UDM-SE +UDMPRO udm UDM-Pro +UP4 uph UVP-X +UP5 uph UVP +UP5t uph UVP-Pro +UP7 uph UVP-Executive +UP5c uph UVP +UP5tc uph UVP-Pro +UP7c uph UVP-Executive +UCK uck UCK +UCK-v2 uck UCK +UCK-v3 uck UCK +UCKG2 uck UCK-G2 +UCKP uck UCK-G2-Plus +UASXG uas UAS-XG +ULTE uap U-LTE +ULTEPUS uap U-LTE-Pro +ULTEPEU uap U-LTE-Pro +UP1 uap USP-Plug +UP6 uap USP-Strip +USPPDUP usw USP - Power Distribution Unit Pro +USPRPS usw USP-RPS +US624P usw UniFi6 Switch 24 +UBB ubb UBB +UXGPRO uxg UniFi NeXt-Gen Gateway PRO \ No newline at end of file