codexctl 1.1.0__tar.gz → 1.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,20 +1,22 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: codexctl
3
- Version: 1.1.0
3
+ Version: 1.2.1
4
4
  Summary: Automated update managment for the ReMarkable tablet
5
5
  License: GPLv3
6
+ License-File: LICENSE
6
7
  Author: Jayy001
7
8
  Author-email: github@skii.dev
8
- Requires-Python: >=3.12,<4.0
9
+ Requires-Python: >=3.12,<3.14
9
10
  Classifier: License :: Other/Proprietary License
10
11
  Classifier: Programming Language :: Python :: 3
11
12
  Classifier: Programming Language :: Python :: 3.12
12
- Requires-Dist: loguru (==0.7.2)
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Requires-Dist: loguru (==0.7.3)
13
15
  Requires-Dist: paramiko (==3.4.1)
14
16
  Requires-Dist: psutil (==6.0.0)
15
- Requires-Dist: remarkable-update-fuse (==1.1.2) ; sys_platform == "linux"
16
- Requires-Dist: remarkable-update-image (==1.1.3) ; sys_platform != "linux"
17
- Requires-Dist: requests (==2.31.0)
17
+ Requires-Dist: remarkable-update-fuse (==1.3) ; sys_platform == "linux"
18
+ Requires-Dist: remarkable-update-image (==1.3) ; sys_platform != "linux"
19
+ Requires-Dist: requests (==2.32.4)
18
20
  Description-Content-Type: text/markdown
19
21
 
20
22
 
@@ -22,11 +24,15 @@ Description-Content-Type: text/markdown
22
24
  # Codexctl
23
25
  A utility program that helps to manage the remarkable device version utilizing [ddvks update server](https://github.com/ddvk/remarkable-update)
24
26
 
25
- ## Caveat for downgrading to a version below 3.11
27
+ ## Caveat for downgrading from 3.11.2.5 or greater to a version less than 3.11.2.5
26
28
 
27
- If your reMarkable device is above 3.11 and you want to downgrade to a version below 3.11, codexctl cannot do this currently. Please refer to #71 for manual instructions.
29
+ If your reMarkable device is at or above 3.11.2.5 and you want to downgrade to a version below 3.11.2.5, codexctl cannot do this currently. Please refer to https://github.com/Jayy001/codexctl/issues/95#issuecomment-2305529048 for manual instructions.
28
30
 
29
- ## Installation
31
+ ## Paper Pro bootloader updates
32
+
33
+ When downgrading a Paper Pro device across the 3.20/3.22 firmware boundary, codexctl automatically handles bootloader updates. It will download the current version's firmware if needed and extract the necessary bootloader files (`update-bootloader.sh` and `imx-boot`) to ensure a safe downgrade.
34
+
35
+ ## Installation
30
36
 
31
37
  You can find pre-compiled binaries on the [releases](https://github.com/Jayy001/codexctl/releases/) page. This includes a build for the reMarkable itself, as well as well as builds for linux, macOS, and Windows. Alternatively, you can install directly from pypi with `pip install codexctl`. Codexctl currently only has support for a **command line interfaces** but a graphical interface is soon to come.
32
38
 
@@ -44,9 +50,9 @@ positional arguments:
44
50
  install Install the specified version (will download if not available on the device)
45
51
  download Download the specified version firmware file
46
52
  backup Download remote files to local directory
47
- cat Cat the contents of a file inside a firmware image
53
+ cat Cat the contents of a file inside a firmwareimage
48
54
  ls List files inside a firmware image
49
- extract Extract the specified version firmware file
55
+ extract Extract the specified version update file
50
56
  mount Mount the specified version firmware filesystem
51
57
  upload Upload folder/files to device (pdf only)
52
58
  status Get the current version of the device and other information
@@ -69,18 +75,27 @@ codexctl install latest
69
75
  ```
70
76
  - Downloading rmpp version 3.15.4.2 to a folder named `out` and then installing it
71
77
  ```
72
- codexctl download 3.15.4.2 -hw rmpp -o out
78
+ codexctl download 3.15.4.2 --hardware rmpp -o out
73
79
  codexctl install ./out/remarkable-ct-prototype-image-3.15.4.2-ferrari-public.swu
74
80
  ```
81
+ - Downloading rmppm version 3.23.0.64 to a folder named `out` and then installing it
82
+ ```
83
+ codexctl download 3.23.0.64 --hardware rmppm -o out
84
+ codexctl install ./out/remarkable-production-image-3.23.0.64-chiappa-public.swu
85
+ ```
75
86
  - Backing up all documents to the cwd
76
87
  ```
77
88
  codexctl backup
78
89
  ```
90
+ - Backing up modified/newer documents only, overwrites out-of-date files on disk
91
+ ```
92
+ codexctl backup --incremental
93
+ ```
79
94
  - Backing up only documents in a folder named "FM" to cwd, without overwriting any current files
80
95
  ```
81
96
  codexctl backup -l root -r FM --no-recursion --no-overwrite
82
97
  ```
83
- - Getting the version of the device and then switching to previous version (restore only for rm1/rm2)
98
+ - Getting the version of the device and then switching to previous version
84
99
  ```
85
100
  codexctl status
86
101
  codexctl restore
@@ -90,7 +105,8 @@ codexctl restore
90
105
  codexctl download 3.8.0.1944 --hardware rm2
91
106
  codexctl cat 3.8.0.1944_reMarkable2-7eGpAv7sYB.signed /etc/version
92
107
  ```
93
-
94
-
95
-
108
+ - Extract the filesystem image of an upgrade file as a file named `extracted`
109
+ ```
110
+ codexctl extract remarkable-production-image-3.22.0.64-ferrari-public.swu -o extracted
111
+ ```
96
112
 
@@ -3,11 +3,15 @@
3
3
  # Codexctl
4
4
  A utility program that helps to manage the remarkable device version utilizing [ddvks update server](https://github.com/ddvk/remarkable-update)
5
5
 
6
- ## Caveat for downgrading to a version below 3.11
6
+ ## Caveat for downgrading from 3.11.2.5 or greater to a version less than 3.11.2.5
7
7
 
8
- If your reMarkable device is above 3.11 and you want to downgrade to a version below 3.11, codexctl cannot do this currently. Please refer to #71 for manual instructions.
8
+ If your reMarkable device is at or above 3.11.2.5 and you want to downgrade to a version below 3.11.2.5, codexctl cannot do this currently. Please refer to https://github.com/Jayy001/codexctl/issues/95#issuecomment-2305529048 for manual instructions.
9
9
 
10
- ## Installation
10
+ ## Paper Pro bootloader updates
11
+
12
+ When downgrading a Paper Pro device across the 3.20/3.22 firmware boundary, codexctl automatically handles bootloader updates. It will download the current version's firmware if needed and extract the necessary bootloader files (`update-bootloader.sh` and `imx-boot`) to ensure a safe downgrade.
13
+
14
+ ## Installation
11
15
 
12
16
  You can find pre-compiled binaries on the [releases](https://github.com/Jayy001/codexctl/releases/) page. This includes a build for the reMarkable itself, as well as well as builds for linux, macOS, and Windows. Alternatively, you can install directly from pypi with `pip install codexctl`. Codexctl currently only has support for a **command line interfaces** but a graphical interface is soon to come.
13
17
 
@@ -25,9 +29,9 @@ positional arguments:
25
29
  install Install the specified version (will download if not available on the device)
26
30
  download Download the specified version firmware file
27
31
  backup Download remote files to local directory
28
- cat Cat the contents of a file inside a firmware image
32
+ cat Cat the contents of a file inside a firmwareimage
29
33
  ls List files inside a firmware image
30
- extract Extract the specified version firmware file
34
+ extract Extract the specified version update file
31
35
  mount Mount the specified version firmware filesystem
32
36
  upload Upload folder/files to device (pdf only)
33
37
  status Get the current version of the device and other information
@@ -50,18 +54,27 @@ codexctl install latest
50
54
  ```
51
55
  - Downloading rmpp version 3.15.4.2 to a folder named `out` and then installing it
52
56
  ```
53
- codexctl download 3.15.4.2 -hw rmpp -o out
57
+ codexctl download 3.15.4.2 --hardware rmpp -o out
54
58
  codexctl install ./out/remarkable-ct-prototype-image-3.15.4.2-ferrari-public.swu
55
59
  ```
60
+ - Downloading rmppm version 3.23.0.64 to a folder named `out` and then installing it
61
+ ```
62
+ codexctl download 3.23.0.64 --hardware rmppm -o out
63
+ codexctl install ./out/remarkable-production-image-3.23.0.64-chiappa-public.swu
64
+ ```
56
65
  - Backing up all documents to the cwd
57
66
  ```
58
67
  codexctl backup
59
68
  ```
69
+ - Backing up modified/newer documents only, overwrites out-of-date files on disk
70
+ ```
71
+ codexctl backup --incremental
72
+ ```
60
73
  - Backing up only documents in a folder named "FM" to cwd, without overwriting any current files
61
74
  ```
62
75
  codexctl backup -l root -r FM --no-recursion --no-overwrite
63
76
  ```
64
- - Getting the version of the device and then switching to previous version (restore only for rm1/rm2)
77
+ - Getting the version of the device and then switching to previous version
65
78
  ```
66
79
  codexctl status
67
80
  codexctl restore
@@ -71,6 +84,7 @@ codexctl restore
71
84
  codexctl download 3.8.0.1944 --hardware rm2
72
85
  codexctl cat 3.8.0.1944_reMarkable2-7eGpAv7sYB.signed /etc/version
73
86
  ```
74
-
75
-
76
-
87
+ - Extract the filesystem image of an upgrade file as a file named `extracted`
88
+ ```
89
+ codexctl extract remarkable-production-image-3.22.0.64-ferrari-public.swu -o extracted
90
+ ```
@@ -10,6 +10,7 @@ import shutil
10
10
  import json
11
11
  import re
12
12
 
13
+ from typing import cast
13
14
  from os import listdir
14
15
 
15
16
  try:
@@ -22,6 +23,7 @@ if importlib.util.find_spec("requests") is None:
22
23
  "Requests is required for accessing remote files. Please install it."
23
24
  )
24
25
 
26
+ from .device import HardwareType
25
27
  from .updates import UpdateManager
26
28
 
27
29
 
@@ -49,12 +51,13 @@ class Manager:
49
51
  args: What arguments to pass into the function
50
52
  """
51
53
 
52
- if "remarkable" not in self.device:
53
- remarkable_version = args.get("hardware")
54
- else:
55
- remarkable_version = self.device
54
+ try:
55
+ remarkable_version = HardwareType.parse(self.device)
56
+ except ValueError:
57
+ hw = args.get("hardware")
58
+ remarkable_version = HardwareType.parse(hw) if hw else None
56
59
 
57
- version = args.get("version", None)
60
+ version = cast(str | None, args.get("version", None))
58
61
 
59
62
  if remarkable_version:
60
63
  if version == "latest":
@@ -64,16 +67,30 @@ class Manager:
64
67
 
65
68
  ### Download functionalities
66
69
  if function == "list":
67
- remarkable_pp_versions = "\n".join(self.updater.remarkablepp_versions.keys())
70
+ remarkable_pp_versions = "\n".join(
71
+ self.updater.remarkablepp_versions.keys()
72
+ )
73
+ remarkable_ppm_versions = "\n".join(
74
+ self.updater.remarkableppm_versions.keys()
75
+ )
68
76
  remarkable_2_versions = "\n".join(self.updater.remarkable2_versions.keys())
69
77
  remarkable_1_versions = "\n".join(self.updater.remarkable1_versions.keys())
70
78
 
71
- print(
72
- f"ReMarkable Paper Pro:\n{remarkable_pp_versions}\n\nReMarkable 2:\n{remarkable_2_versions}\n\nReMarkable 1:\n{remarkable_1_versions}"
73
- )
79
+ version_blocks = []
80
+ if remarkable_version is None or remarkable_version == HardwareType.RMPP:
81
+ version_blocks.append(f"ReMarkable Paper Pro:\n{remarkable_pp_versions}")
82
+ if remarkable_version is None or remarkable_version == HardwareType.RMPPM:
83
+ version_blocks.append(f"ReMarkable Paper Pro Move:\n{remarkable_ppm_versions}")
84
+ if remarkable_version is None or remarkable_version == HardwareType.RM2:
85
+ version_blocks.append(f"ReMarkable 2:\n{remarkable_2_versions}")
86
+ if remarkable_version is None or remarkable_version == HardwareType.RM1:
87
+ version_blocks.append(f"ReMarkable 1:\n{remarkable_1_versions}")
88
+
89
+ print("\n\n".join(version_blocks))
74
90
 
75
91
  elif function == "download":
76
92
  logger.debug(f"Downloading version {version}")
93
+ assert remarkable_version is not None
77
94
  filename = self.updater.download_version(
78
95
  remarkable_version, version, args["out"]
79
96
  )
@@ -83,24 +100,35 @@ class Manager:
83
100
 
84
101
  ### Mounting functionalities
85
102
  elif function in ("extract", "mount"):
86
- try:
87
- from .analysis import get_update_image
88
- except ImportError:
89
- raise ImportError(
90
- "remarkable_update_image is required for analysis. Please install it!"
91
- )
92
-
93
103
  if function == "extract":
94
104
  if not args["out"]:
95
105
  args["out"] = os.getcwd() + "/extracted"
96
106
 
97
107
  logger.debug(f"Extracting {args['file']} to {args['out']}")
98
- image, volume = get_update_image(args["file"])
108
+
109
+ try:
110
+ from remarkable_update_image import UpdateImage
111
+ except ImportError:
112
+ raise ImportError(
113
+ "remarkable_update_image is required for extraction. Please install it!"
114
+ ) from None
115
+
116
+ image = UpdateImage(args["file"])
99
117
  image.seek(0)
100
118
 
101
119
  with open(args["out"], "wb") as f:
102
120
  f.write(image.read())
121
+
122
+ logger.info(f"Extracted image to {args['out']}")
103
123
  else:
124
+ try:
125
+ from .analysis import get_update_image
126
+ from remarkable_update_fuse import UpdateFS
127
+ except ImportError:
128
+ raise ImportError(
129
+ "remarkable_update_fuse is required for mounting. Please install it!"
130
+ )
131
+
104
132
  if args["out"] is None:
105
133
  args["out"] = "/opt/remarkable/"
106
134
 
@@ -110,8 +138,6 @@ class Manager:
110
138
  if not os.path.exists(args["filesystem"]):
111
139
  raise SystemExit("Firmware file does not exist!")
112
140
 
113
- from remarkable_update_fuse import UpdateFS
114
-
115
141
  server = UpdateFS()
116
142
  server.parse(
117
143
  args=[args["filesystem"], args["out"]], values=server, errex=1
@@ -161,25 +187,16 @@ class Manager:
161
187
  remoteFolder=args["remote"],
162
188
  recursive=not args["no_recursion"],
163
189
  overwrite=not args["no_overwrite"],
190
+ incremental=args["incremental"],
164
191
  )
165
192
  else:
166
193
  rmWeb.upload(input_paths=args["paths"], remoteFolder=args["remote"])
167
194
 
168
- ### Transfer & Download functionalities
169
- elif function in ("transfer", "download"):
170
- remarkable = DeviceManager(
171
- remote=remote,
172
- address=args["address"],
173
- logger=self.logger,
174
- authentication=args["password"],
175
- )
176
-
177
-
178
195
  ### Update & Version functionalities
179
196
  elif function in ("install", "status", "restore"):
180
197
  remote = False
181
198
 
182
- if "remarkable" not in self.device:
199
+ if "reMarkable" not in self.device:
183
200
  if importlib.util.find_spec("paramiko") is None:
184
201
  raise ImportError(
185
202
  "Paramiko is required for SSH access. Please install it."
@@ -190,6 +207,13 @@ class Manager:
190
207
  )
191
208
  remote = True
192
209
 
210
+ try:
211
+ from remarkable_update_image import UpdateImage
212
+ except ImportError:
213
+ raise ImportError(
214
+ "remarkable_update_image is required for install. Please install it!"
215
+ ) from None
216
+
193
217
  from .device import DeviceManager
194
218
  from .server import get_available_version
195
219
 
@@ -206,17 +230,15 @@ class Manager:
206
230
  version = self.updater.get_toltec_version(remarkable.hardware)
207
231
 
208
232
  if function == "status":
209
- beta, prev, current, version_id = remarkable.get_device_status()
233
+ beta, prev, current, version_id, backup = remarkable.get_device_status()
210
234
  print(
211
- f"\nCurrent version: {current}\nOld update engine: {prev}\nBeta active: {beta}\nVersion id: {version_id}"
235
+ f"\nCurrent version: {current}\nBackup version: {backup}\nOld update engine: {prev}\nBeta active: {beta}\nVersion id: {version_id}"
212
236
  )
213
237
 
214
238
  elif function == "restore":
215
- if remarkable.hardware == "ferrari":
216
- raise SystemError("Restore not available for rmpro.")
217
239
  remarkable.restore_previous_version()
218
240
  print(
219
- f"Device restored to previous version [{remarkable.get_device_status()[1]}]"
241
+ f"Device restored to previous version [{remarkable.get_device_status()[4]}]"
220
242
  )
221
243
  remarkable.reboot_device()
222
244
  print("Device rebooted")
@@ -229,16 +251,60 @@ class Manager:
229
251
  # Do we have a specific update file to serve?
230
252
 
231
253
  update_file = version if os.path.isfile(version) else None
232
-
233
- version_lookup = lambda version: re.search(r'\b\d+\.\d+\.\d+\.\d+\b', version)
234
- version_number = version_lookup(version)
254
+
255
+ def version_lookup(version: str | None) -> re.Match[str] | None:
256
+ return re.search(r"\b\d+\.\d+\.\d+\.\d+\b", cast(str, version))
257
+
258
+ version_number = None
259
+ swu_hardware = None
260
+
261
+ if update_file:
262
+ try:
263
+ from remarkable_update_image import UpdateImage
264
+ from remarkable_update_image.image import CPIOUpdateImage
265
+
266
+ image = UpdateImage(update_file)
267
+ if isinstance(image, CPIOUpdateImage):
268
+ if image.version is None:
269
+ raise SystemError(f"Could not determine version from SWU file: {update_file}")
270
+
271
+ version_number = image.version
272
+ hw_map = {
273
+ "reMarkable1": HardwareType.RM1,
274
+ "reMarkable2": HardwareType.RM2,
275
+ "ferrari": HardwareType.RMPP,
276
+ "chiappa": HardwareType.RMPPM,
277
+ }
278
+ if image.hardware_type not in hw_map:
279
+ raise SystemError(f"Unsupported hardware type in SWU file: {update_file}")
280
+
281
+ swu_hardware = hw_map[image.hardware_type]
282
+ logger.info(f"Extracted from SWU: version={version_number}, hardware={swu_hardware.name}")
283
+
284
+ if swu_hardware != remarkable.hardware:
285
+ raise SystemError(
286
+ f"Hardware mismatch!\n"
287
+ f"SWU file is for: {swu_hardware.name}\n"
288
+ f"Connected device is: {remarkable.hardware.name}\n"
289
+ f"Cannot install firmware for different hardware."
290
+ )
291
+ except ValueError as e:
292
+ logger.warning(f"Could not extract metadata from update file: {e}")
235
293
 
236
294
  if not version_number:
237
- version_number = input("Failed to get the version number from the filename, please enter it: ")
238
- if not version_lookup(version_number):
239
- raise SystemError("Invalid version!")
295
+ version_match = version_lookup(version)
296
+ if not version_match:
297
+ version_match = version_lookup(
298
+ input(
299
+ "Failed to get the version number from the filename, please enter it: "
300
+ )
301
+ )
302
+ if not version_match:
303
+ raise SystemError("Invalid version!")
240
304
 
241
- version_number = version_number.group()
305
+ version_number = version_match.group()
306
+ else:
307
+ version_number = str(version_number)
242
308
 
243
309
  update_file_requires_new_engine = UpdateManager.uses_new_update_engine(
244
310
  version_number
@@ -251,23 +317,91 @@ class Manager:
251
317
 
252
318
  if device_version_uses_new_engine:
253
319
  if not update_file_requires_new_engine:
254
- raise SystemError("Cannot downgrade to this version as it uses the old update engine, please manually downgrade.")
320
+ raise SystemError(
321
+ "Cannot downgrade to this version as it uses the old update engine, please manually downgrade."
322
+ )
255
323
  # TODO: Implement manual downgrading.
256
324
  # `codexctl download --out . 3.11.2.5`
257
325
  # `codexctl extract --out 3.11.2.5.img 3.11.2.5_reMarkable2-qLFGoqPtPL.signed`
258
326
  # `codexctl transfer 3.11.2.5.img ~/root`
259
327
  # `dd if=/home/root/3.11.2.5.img of=/dev/mmcblk2p2` (depending on fallback partition)
260
328
  # `codexctl restore`
261
-
329
+
262
330
  else:
263
331
  if update_file_requires_new_engine:
264
- raise SystemError("This version requires the new update engine, please upgrade your device to version 3.11.2.5 first.")
332
+ raise SystemError(
333
+ "This version requires the new update engine, please upgrade your device to version 3.11.2.5 first."
334
+ )
265
335
 
266
336
  #############################################################
267
337
 
338
+ bootloader_files_for_install = None
339
+
340
+ if (device_version_uses_new_engine and
341
+ remarkable.hardware == HardwareType.RMPP):
342
+
343
+ current_version = remarkable.get_device_status()[2]
344
+
345
+ if UpdateManager.is_bootloader_boundary_downgrade(current_version, version_number):
346
+ print("\n" + "="*60)
347
+ print("WARNING: Bootloader Update Required")
348
+ print("="*60)
349
+ print(f"Current version: {current_version}")
350
+ print(f"Target version: {version_number}")
351
+ print()
352
+ print("Downgrading from 3.22+ to <3.22 requires updating the")
353
+ print("bootloader on both partitions. This process will:")
354
+ print(" 1. Download the current version's bootloader files")
355
+ print(" 2. Download the target OS version")
356
+ print(" 3. Install the target OS version")
357
+ print(" 4. Update bootloader on both partitions")
358
+ print(" 5. Reboot")
359
+ print()
360
+
361
+ response = input("Do you want to continue? (y/N): ")
362
+ if response.lower() != 'y':
363
+ raise SystemExit("Installation cancelled by user")
364
+
365
+ expected_swu_name = f"remarkable-production-memfault-image-{current_version}-{remarkable.hardware.new_download_hw}-public"
366
+ expected_swu_path = f"./{expected_swu_name}"
367
+
368
+ if os.path.isfile(expected_swu_path):
369
+ print(f"\nUsing existing {expected_swu_name} for bootloader extraction...")
370
+ current_swu_path = expected_swu_path
371
+ else:
372
+ print("\nDownloading current version's SWU for bootloader extraction...")
373
+ current_swu_path = self.updater.download_version(
374
+ remarkable.hardware,
375
+ current_version,
376
+ "./"
377
+ )
378
+
379
+ if not current_swu_path:
380
+ raise SystemError(
381
+ f"Failed to download current version {current_version} for bootloader extraction. "
382
+ f"This is required for safe downgrade across bootloader boundary."
383
+ )
384
+
385
+ print("Extracting bootloader files...")
386
+ from remarkable_update_image import UpdateImage
387
+ swu_image = UpdateImage(current_swu_path)
388
+ bootloader_files_for_install = {
389
+ 'update-bootloader.sh': swu_image.archive[b'update-bootloader.sh'].read(),
390
+ 'imx-boot': swu_image.archive[b'imx-boot'].read(),
391
+ }
392
+
393
+ if not all(bootloader_files_for_install.values()):
394
+ raise SystemError("Failed to extract bootloader files from current version")
395
+
396
+ print(f"✓ Extracted update-bootloader.sh ({len(bootloader_files_for_install['update-bootloader.sh'])} bytes)")
397
+ print(f"✓ Extracted imx-boot ({len(bootloader_files_for_install['imx-boot'])} bytes)")
398
+ print()
399
+
268
400
  if not update_file_requires_new_engine:
269
- if update_file: # Check if file exists
270
- if not (os.path.dirname(os.path.abspath(update_file)) == os.path.abspath("updates")):
401
+ if update_file: # Check if file exists
402
+ if os.path.dirname(
403
+ os.path.abspath(update_file)
404
+ ) != os.path.abspath("updates"):
271
405
  if not os.path.exists("updates"):
272
406
  os.mkdir("updates")
273
407
  shutil.move(update_file, "updates")
@@ -303,7 +437,7 @@ class Manager:
303
437
  )
304
438
 
305
439
  if device_version_uses_new_engine:
306
- remarkable.install_sw_update(update_file)
440
+ remarkable.install_sw_update(update_file, bootloader_files=bootloader_files_for_install)
307
441
  else:
308
442
  remarkable.install_ohma_update(update_file)
309
443
 
@@ -328,6 +462,7 @@ def main() -> None:
328
462
  required=False,
329
463
  help="Enable verbose logging",
330
464
  action="store_true",
465
+ dest="verbose",
331
466
  )
332
467
  parser.add_argument(
333
468
  "--address",
@@ -335,12 +470,14 @@ def main() -> None:
335
470
  required=False,
336
471
  help="Specify the address of the device",
337
472
  default=None,
473
+ dest="address",
338
474
  )
339
475
  parser.add_argument(
340
476
  "--password",
341
477
  "-p",
342
478
  required=False,
343
479
  help="Specify password or path to SSH key for remote access",
480
+ dest="password",
344
481
  )
345
482
  subparsers = parser.add_subparsers(dest="command")
346
483
  subparsers.required = True # This fixes a bug with older versions of python
@@ -359,7 +496,12 @@ def main() -> None:
359
496
  download.add_argument("version", help="Version to download")
360
497
  download.add_argument("--out", "-o", help="Folder to download to", default=None)
361
498
  download.add_argument(
362
- "--hardware", "-hd", help="Hardware to download for", required=True
499
+ "--hardware",
500
+ "--device",
501
+ "-d",
502
+ help="Hardware to download for",
503
+ required=True,
504
+ dest="hardware",
363
505
  )
364
506
 
365
507
  ### Backup subcommand
@@ -371,21 +513,34 @@ def main() -> None:
371
513
  "--remote",
372
514
  help="Remote directory to backup. Defaults to download folder",
373
515
  default="",
516
+ dest="remote",
374
517
  )
375
518
  backup.add_argument(
376
519
  "-l",
377
520
  "--local",
378
521
  help="Local directory to backup to. Defaults to download folder",
379
522
  default="./",
523
+ dest="local",
380
524
  )
381
525
  backup.add_argument(
382
- "-nr",
526
+ "-R",
383
527
  "--no-recursion",
384
528
  help="Disables recursively backup remote directory",
385
529
  action="store_true",
530
+ dest="no_recursion",
386
531
  )
387
532
  backup.add_argument(
388
- "-no-ow", "--no-overwrite", help="Disables overwrite", action="store_true"
533
+ "-O",
534
+ "--no-overwrite",
535
+ help="Disables overwrite",
536
+ action="store_true",
537
+ dest="no_overwrite",
538
+ )
539
+ backup.add_argument(
540
+ "-i",
541
+ "--incremental",
542
+ help="Overwrite out-of-date files only",
543
+ action="store_true",
389
544
  )
390
545
 
391
546
  ### Cat subcommand
@@ -405,7 +560,7 @@ def main() -> None:
405
560
  "extract", help="Extract the specified version update file"
406
561
  )
407
562
  extract.add_argument("file", help="Path to update file to extract", default=None)
408
- extract.add_argument("--out", help="Folder to extract to", default=None)
563
+ extract.add_argument("--out", help="Folder to extract to", default=None, dest="out")
409
564
 
410
565
  ### Mount subcommand
411
566
  mount = subparsers.add_parser(
@@ -430,6 +585,7 @@ def main() -> None:
430
585
  "--remote",
431
586
  help="Remote directory to upload to. Defaults to root folder",
432
587
  default="",
588
+ dest="remote",
433
589
  )
434
590
 
435
591
  ### Status subcommand
@@ -443,7 +599,14 @@ def main() -> None:
443
599
  )
444
600
 
445
601
  ### List subcommand
446
- subparsers.add_parser("list", help="List all available versions")
602
+ list_ = subparsers.add_parser("list", help="List all available versions")
603
+ list_.add_argument(
604
+ "--hardware",
605
+ "--device",
606
+ "-d",
607
+ help="Hardware to list for",
608
+ dest="hardware",
609
+ )
447
610
 
448
611
  ### Setting logging level
449
612
  args = parser.parse_args()
@@ -1,9 +1,10 @@
1
1
  import ext4
2
- import warnings
2
+ import warnings
3
3
  import errno
4
4
 
5
5
  from remarkable_update_image import UpdateImage
6
6
  from remarkable_update_image import UpdateImageSignatureException
7
+ from .device import HardwareType
7
8
 
8
9
 
9
10
  def get_update_image(file: str):