Skip to content
This repository was archived by the owner on Sep 16, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*.dis
*.exe

# Packages
# Packages
############

# Logs and Databases
Expand Down Expand Up @@ -50,8 +50,9 @@ secure_boot_signing_key.pem
signature_verification_key.bin
secure-bootloader-key.bin
flash_encryption_key.bin

.DS_Store
org.eclipse.cdt.ui.prefs
org.eclipse.cdt.core.prefs
language.settings.xml
.idea
80 changes: 45 additions & 35 deletions esp32/frozen/Pybytes/_OTA.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
see the Pycom Licence v1.0 document supplied with this file, or
available at https://www.pycom.io/opensource/licensing
'''
try:
from pybytes_debug import print_debug
except:
from _pybytes_debug import print_debug

import network
import socket
Expand Down Expand Up @@ -56,9 +60,9 @@ def get_update_manifest(self):
gc.collect()
return manifest

def update(self):
def update(self, customManifest=None):
try:
manifest = self.get_update_manifest()
manifest = self.get_update_manifest() if not customManifest else customManifest
except Exception as e:
print('Error reading the manifest, aborting: {}'.format(e))
return 0
Expand All @@ -68,36 +72,38 @@ def update(self):
return 1

# Download new files and verify hashes
for f in manifest['new'] + manifest['update']:
# Upto 5 retries
for _ in range(5):
try:
self.get_file(f)
break
except Exception as e:
print(e)
msg = "Error downloading `{}` retrying..."
print(msg.format(f['URL']))
return 0
else:
raise Exception("Failed to download `{}`".format(f['URL']))

# Backup old files
# only once all files have been successfully downloaded
for f in manifest['update']:
self.backup_file(f)

# Rename new files to proper name
for f in manifest['new'] + manifest['update']:
new_path = "{}.new".format(f['dst_path'])
dest_path = "{}".format(f['dst_path'])

os.rename(new_path, dest_path)

# `Delete` files no longer required
# This actually makes a backup of the files incase we need to roll back
for f in manifest['delete']:
self.delete_file(f)
if "new" in manifest and "update" in manifest:
for f in manifest['new'] + manifest['update']:
# Upto 5 retries
for _ in range(5):
try:
self.get_file(f)
break
except Exception as e:
print(e)
msg = "Error downloading `{}` retrying..."
print(msg.format(f['URL']))
return 0
else:
raise Exception("Failed to download `{}`".format(f['URL']))

# Backup old files
# only once all files have been successfully downloaded
for f in manifest['update']:
self.backup_file(f)

# Rename new files to proper name
for f in manifest['new'] + manifest['update']:
new_path = "{}.new".format(f['dst_path'])
dest_path = "{}".format(f['dst_path'])

os.rename(new_path, dest_path)

if "delete" in manifest:
# `Delete` files no longer required
# This actually makes a backup of the files incase we need to roll back
for f in manifest['delete']:
self.delete_file(f)

# Flash firmware
if "firmware" in manifest:
Expand Down Expand Up @@ -162,9 +168,12 @@ def delete_file(self, f):
os.rename(dest_path, bak_path)

def write_firmware(self, f):
hash = self.get_data(f['URL'].split("/", 3)[-1],
hash=True,
firmware=True)
# hash =
self.get_data(
f['URL'].split("/", 3)[-1],
hash=True,
firmware=True
)
# TODO: Add verification when released in future firmware


Expand Down Expand Up @@ -258,6 +267,7 @@ def get_data(self, req, dest_path=None, hash=False, firmware=False):
if fp is not None:
fp.close()
if firmware:
print_debug(6, 'ota_finish')
pycom.ota_finish()

except Exception as e:
Expand Down
4 changes: 4 additions & 0 deletions esp32/frozen/Pybytes/_pybytes_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,7 @@ class constants:
__DEFAULT_PYCONFIG_DOMAIN = pycom.nvs_get('pyconfig_host', 'pyconfig.eu-central-1.elasticbeanstalk.com')
except:
__DEFAULT_PYCONFIG_DOMAIN = 'pyconfig.eu-central-1.elasticbeanstalk.com'
try:
__DEFAULT_PYCONFIG_PROTOCOL = pycom.nvs_get('pyconfig_protocol', 'https')
except:
__DEFAULT_PYCONFIG_PROTOCOL = "https"
130 changes: 106 additions & 24 deletions esp32/frozen/Pybytes/_pybytes_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ def __send_terminal_message(self, data):

def enable_terminal(self):
self.__terminal_enabled = True
#os.dupterm(self.__terminal)
# os.dupterm(self.__terminal)

def __send_pybytes_message(self, command, pin_number, value):
self.__send_message(
Expand All @@ -522,14 +522,28 @@ def __send_pybytes_message_variable(
def set_battery_level(self, battery_level):
self.__battery_level = battery_level

def deploy_new_release(self, body):
application = self.__conf.get('application')
try:
body = ujson.loads(body.decode())
except Exception as e:
print_debug(0, "error while loading body {}".format(e))
return
def write_firmware(self, customManifest=None):
ota = WiFiOTA(
self.__conf['wifi']['ssid'],
self.__conf['wifi']['password'],
self.__conf['ota_server']['domain'],
self.__conf['ota_server']['port']
)

if (self.__pybytes_connection.__connection_status == constants.__CONNECTION_STATUS_DISCONNECTED): # noqa
print_debug(5, 'Connecting to WiFi')
ota.connect()

print_debug(5, "Performing OTA")
result = ota.update(customManifest)
self.send_ota_response(result)
time.sleep(1.5)
if (result == 2):
# Reboot the device to run the new decode
machine.reset()

def get_application_details(self, body):
application = self.__conf.get('application')
if application is not None:
if 'id' in application and application['id']:
applicationID = application['id']
Expand All @@ -542,23 +556,32 @@ def deploy_new_release(self, body):
else:
applicationID = body['applicationId']
currentReleaseID = None
self.__conf['application'] = {
"id": "",
"release": {
"id": "",
"codeFilename": "",
"version": 0
}
}
return (applicationID, currentReleaseID)

baseWebConfigUrl = 'https://{}'.format(constants.__DEFAULT_PYCONFIG_DOMAIN)
manifestURL = '{}/manifest.json?'.format(baseWebConfigUrl)
fileUrl = '{}/files?'.format(baseWebConfigUrl)
newReleaseID = body["releaseId"]
targetURL = '{}app_id={}&target_ver={}&current_ver={}'.format(
def get_update_manifest(self, applicationID, newReleaseID, currentReleaseID):
manifestURL = '{}://{}/manifest.json?'.format(constants.__DEFAULT_PYCONFIG_PROTOCOL, constants.__DEFAULT_PYCONFIG_DOMAIN)
targetURL = '{}app_id={}&target_ver={}&current_ver={}&device_id={}'.format(
manifestURL,
applicationID,
newReleaseID,
currentReleaseID
currentReleaseID,
self.__conf['device_id']
)
print_debug(6, "manifest URL: {}".format(targetURL))
try:
pybytes_activation = urequest.get(targetURL, headers={'content-type': 'application/json'})
letResp = pybytes_activation.json()
pybytes_activation.close()
print_debug(6, "letResp: {}".format(letResp))
return letResp
except Exception as ex:
print_debug(1, "error while calling {}!: {}".format(targetURL, ex))
return
Expand All @@ -567,6 +590,8 @@ def deploy_new_release(self, body):
print_debug(1, letResp['errorMessage'])
return

def update_files(self, letResp, applicationID, newReleaseID):
fileUrl = '{}://{}/files?'.format(constants.__DEFAULT_PYCONFIG_PROTOCOL, constants.__DEFAULT_PYCONFIG_DOMAIN)
try:
newFiles = letResp['newFiles']
updatedFiles = letResp['updatedFiles']
Expand All @@ -591,22 +616,14 @@ def deploy_new_release(self, body):
fileContent = getFile.content
self.__FCOTA.update_file_content(file['fileName'], fileContent)

def delete_files(self, letResp):
if 'deletedFiles' in letResp:
deletedFiles = letResp['deletedFiles']
for file in deletedFiles:
self.__FCOTA.delete_file(file['fileName'])

def update_application_config(self, letResp, applicationID):
try:
if application is None:
self.__conf['application'] = {
"id": "",
"release": {
"id": "",
"codeFilename": "",
"version": 0
}
}

self.__conf['application']["id"] = applicationID
self.__conf['application']['release']['id'] = letResp['target_version']['id']
self.__conf['application']['release']['codeFilename'] = letResp['target_version']['codeFileName']
Expand All @@ -621,4 +638,69 @@ def deploy_new_release(self, body):
except Exception as e:
print_debug(1, "error while updating pybytes_config.json! {}".format(e))

def update_network_config(self, letResp):
try:
if 'networkConfig' in letResp:
netConf = letResp['networkConfig']
self.__conf['network_preferences'] = netConf['networkPreferences']
if 'wifi' in netConf:
self.__conf['wifi'] = netConf['wifi']
elif 'wifi' in self.__conf:
del self.__conf['wifi']

if 'lte' in netConf:
self.__conf['lte'] = netConf['lte']
elif 'lte' in self.__conf:
del self.__conf['lte']

if 'lora' in netConf:
self.__conf['lora'] = {
'otaa': netConf['lora']['otaa'],
'abp': netConf['lora']['abp']
}
elif 'lora' in self.__conf:
del self.__conf['lora']

json_string = ujson.dumps(self.__conf)
print_debug(1, "update_network_config : {}".format(json_string))
self.__FCOTA.update_file_content('/flash/pybytes_config.json', json_string)
except Exception as e:
print_debug(1, "error while updating network config pybytes_config.json! {}".format(e))

def update_firmware(self, body):
if "firmware" not in body:
print_debug(0, "no firmware to update")
return
version = body['firmware']["version"]
print_debug(0, "updating firmware to {}".format(version))
customManifest = {
"firmware": {
"URL": "https://{}/downloads/appimg/firmware_{}_{}.bin".format(
constants.__DEFAULT_SW_HOST,
os.uname().sysname,
version),
}
}
self.write_firmware(customManifest)

def deploy_new_release(self, body):
try:
body = ujson.loads(body.decode())
print_debug(6, "body {}".format(body))
except Exception as e:
print_debug(0, "error while loading body {}".format(e))
return

newReleaseID = body["releaseId"]
applicationID, currentReleaseID = self.get_application_details(body)

letResp = self.get_update_manifest(applicationID, newReleaseID, currentReleaseID)
if not letResp:
return

self.update_files(letResp, applicationID, newReleaseID)
self.delete_files(letResp)
self.update_application_config(letResp, applicationID)
self.update_network_config(letResp)
self.update_firmware(letResp)
machine.reset()
2 changes: 1 addition & 1 deletion esp32/pycom_version.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
#define SIGFOX_VERSION_NUMBER "1.0.1"

#if (VARIANT == PYBYTES)
#define PYBYTES_VERSION_NUMBER "1.1.3"
#define PYBYTES_VERSION_NUMBER "1.2.0"
#endif

#endif /* VERSION_H_ */