codexctl 1.0.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,33 +1,38 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: codexctl
3
- Version: 1.0.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
- <p align="center">
21
- <img src="media/demoLocal.gif">
22
+
22
23
 
23
24
  # Codexctl
24
25
  A utility program that helps to manage the remarkable device version utilizing [ddvks update server](https://github.com/ddvk/remarkable-update)
25
26
 
26
- ## 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
28
+
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.
30
+
31
+ ## Paper Pro bootloader updates
27
32
 
28
- 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.
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.
29
34
 
30
- ## Installation
35
+ ## Installation
31
36
 
32
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.
33
38
 
@@ -45,9 +50,9 @@ positional arguments:
45
50
  install Install the specified version (will download if not available on the device)
46
51
  download Download the specified version firmware file
47
52
  backup Download remote files to local directory
48
- cat Cat the contents of a file inside a firmware image
53
+ cat Cat the contents of a file inside a firmwareimage
49
54
  ls List files inside a firmware image
50
- extract Extract the specified version firmware file
55
+ extract Extract the specified version update file
51
56
  mount Mount the specified version firmware filesystem
52
57
  upload Upload folder/files to device (pdf only)
53
58
  status Get the current version of the device and other information
@@ -70,18 +75,27 @@ codexctl install latest
70
75
  ```
71
76
  - Downloading rmpp version 3.15.4.2 to a folder named `out` and then installing it
72
77
  ```
73
- codexctl download 3.0.4.1305 -hw rmpp -o out
78
+ codexctl download 3.15.4.2 --hardware rmpp -o out
74
79
  codexctl install ./out/remarkable-ct-prototype-image-3.15.4.2-ferrari-public.swu
75
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
+ ```
76
86
  - Backing up all documents to the cwd
77
87
  ```
78
88
  codexctl backup
79
89
  ```
90
+ - Backing up modified/newer documents only, overwrites out-of-date files on disk
91
+ ```
92
+ codexctl backup --incremental
93
+ ```
80
94
  - Backing up only documents in a folder named "FM" to cwd, without overwriting any current files
81
95
  ```
82
96
  codexctl backup -l root -r FM --no-recursion --no-overwrite
83
97
  ```
84
- - 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
85
99
  ```
86
100
  codexctl status
87
101
  codexctl restore
@@ -91,7 +105,8 @@ codexctl restore
91
105
  codexctl download 3.8.0.1944 --hardware rm2
92
106
  codexctl cat 3.8.0.1944_reMarkable2-7eGpAv7sYB.signed /etc/version
93
107
  ```
94
-
95
-
96
-
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
+ ```
97
112
 
@@ -1,14 +1,17 @@
1
- <p align="center">
2
- <img src="media/demoLocal.gif">
1
+
3
2
 
4
3
  # Codexctl
5
4
  A utility program that helps to manage the remarkable device version utilizing [ddvks update server](https://github.com/ddvk/remarkable-update)
6
5
 
7
- ## 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
+
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
+
10
+ ## Paper Pro bootloader updates
8
11
 
9
- 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.
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.
10
13
 
11
- ## Installation
14
+ ## Installation
12
15
 
13
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.
14
17
 
@@ -26,9 +29,9 @@ positional arguments:
26
29
  install Install the specified version (will download if not available on the device)
27
30
  download Download the specified version firmware file
28
31
  backup Download remote files to local directory
29
- cat Cat the contents of a file inside a firmware image
32
+ cat Cat the contents of a file inside a firmwareimage
30
33
  ls List files inside a firmware image
31
- extract Extract the specified version firmware file
34
+ extract Extract the specified version update file
32
35
  mount Mount the specified version firmware filesystem
33
36
  upload Upload folder/files to device (pdf only)
34
37
  status Get the current version of the device and other information
@@ -51,18 +54,27 @@ codexctl install latest
51
54
  ```
52
55
  - Downloading rmpp version 3.15.4.2 to a folder named `out` and then installing it
53
56
  ```
54
- codexctl download 3.0.4.1305 -hw rmpp -o out
57
+ codexctl download 3.15.4.2 --hardware rmpp -o out
55
58
  codexctl install ./out/remarkable-ct-prototype-image-3.15.4.2-ferrari-public.swu
56
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
+ ```
57
65
  - Backing up all documents to the cwd
58
66
  ```
59
67
  codexctl backup
60
68
  ```
69
+ - Backing up modified/newer documents only, overwrites out-of-date files on disk
70
+ ```
71
+ codexctl backup --incremental
72
+ ```
61
73
  - Backing up only documents in a folder named "FM" to cwd, without overwriting any current files
62
74
  ```
63
75
  codexctl backup -l root -r FM --no-recursion --no-overwrite
64
76
  ```
65
- - 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
66
78
  ```
67
79
  codexctl status
68
80
  codexctl restore
@@ -72,6 +84,7 @@ codexctl restore
72
84
  codexctl download 3.8.0.1944 --hardware rm2
73
85
  codexctl cat 3.8.0.1944_reMarkable2-7eGpAv7sYB.signed /etc/version
74
86
  ```
75
-
76
-
77
-
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,6 +187,7 @@ 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"])
@@ -169,7 +196,7 @@ class Manager:
169
196
  elif function in ("install", "status", "restore"):
170
197
  remote = False
171
198
 
172
- if "remarkable" not in self.device:
199
+ if "reMarkable" not in self.device:
173
200
  if importlib.util.find_spec("paramiko") is None:
174
201
  raise ImportError(
175
202
  "Paramiko is required for SSH access. Please install it."
@@ -180,6 +207,13 @@ class Manager:
180
207
  )
181
208
  remote = True
182
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
+
183
217
  from .device import DeviceManager
184
218
  from .server import get_available_version
185
219
 
@@ -196,17 +230,15 @@ class Manager:
196
230
  version = self.updater.get_toltec_version(remarkable.hardware)
197
231
 
198
232
  if function == "status":
199
- beta, prev, current, version_id = remarkable.get_device_status()
233
+ beta, prev, current, version_id, backup = remarkable.get_device_status()
200
234
  print(
201
- 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}"
202
236
  )
203
237
 
204
238
  elif function == "restore":
205
- if remarkable.hardware == "ferrari":
206
- raise SystemError("Restore not available for rmpro.")
207
239
  remarkable.restore_previous_version()
208
240
  print(
209
- f"Device restored to previous version [{remarkable.get_device_status()[1]}]"
241
+ f"Device restored to previous version [{remarkable.get_device_status()[4]}]"
210
242
  )
211
243
  remarkable.reboot_device()
212
244
  print("Device rebooted")
@@ -219,16 +251,60 @@ class Manager:
219
251
  # Do we have a specific update file to serve?
220
252
 
221
253
  update_file = version if os.path.isfile(version) else None
222
-
223
- version_lookup = lambda version: re.search(r'\b\d+\.\d+\.\d+\.\d+\b', version)
224
- 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}")
225
293
 
226
294
  if not version_number:
227
- version_number = input("Failed to get the version number from the filename, please enter it: ")
228
- if not version_lookup(version_number):
229
- 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!")
230
304
 
231
- version_number = version_number.group()
305
+ version_number = version_match.group()
306
+ else:
307
+ version_number = str(version_number)
232
308
 
233
309
  update_file_requires_new_engine = UpdateManager.uses_new_update_engine(
234
310
  version_number
@@ -241,26 +317,91 @@ class Manager:
241
317
 
242
318
  if device_version_uses_new_engine:
243
319
  if not update_file_requires_new_engine:
244
- 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
+ )
245
323
  # TODO: Implement manual downgrading.
246
324
  # `codexctl download --out . 3.11.2.5`
247
325
  # `codexctl extract --out 3.11.2.5.img 3.11.2.5_reMarkable2-qLFGoqPtPL.signed`
248
326
  # `codexctl transfer 3.11.2.5.img ~/root`
249
327
  # `dd if=/home/root/3.11.2.5.img of=/dev/mmcblk2p2` (depending on fallback partition)
250
328
  # `codexctl restore`
251
-
329
+
252
330
  else:
253
331
  if update_file_requires_new_engine:
254
- 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
+ )
255
335
 
256
336
  #############################################################
257
337
 
258
- update_file_requires_new_engine = False
259
- device_version_uses_new_engine = False
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()
260
399
 
261
400
  if not update_file_requires_new_engine:
262
- if update_file: # Check if file exists
263
- 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"):
264
405
  if not os.path.exists("updates"):
265
406
  os.mkdir("updates")
266
407
  shutil.move(update_file, "updates")
@@ -296,7 +437,7 @@ class Manager:
296
437
  )
297
438
 
298
439
  if device_version_uses_new_engine:
299
- remarkable.install_sw_update(update_file)
440
+ remarkable.install_sw_update(update_file, bootloader_files=bootloader_files_for_install)
300
441
  else:
301
442
  remarkable.install_ohma_update(update_file)
302
443
 
@@ -321,6 +462,7 @@ def main() -> None:
321
462
  required=False,
322
463
  help="Enable verbose logging",
323
464
  action="store_true",
465
+ dest="verbose",
324
466
  )
325
467
  parser.add_argument(
326
468
  "--address",
@@ -328,12 +470,14 @@ def main() -> None:
328
470
  required=False,
329
471
  help="Specify the address of the device",
330
472
  default=None,
473
+ dest="address",
331
474
  )
332
475
  parser.add_argument(
333
476
  "--password",
334
477
  "-p",
335
478
  required=False,
336
479
  help="Specify password or path to SSH key for remote access",
480
+ dest="password",
337
481
  )
338
482
  subparsers = parser.add_subparsers(dest="command")
339
483
  subparsers.required = True # This fixes a bug with older versions of python
@@ -352,7 +496,12 @@ def main() -> None:
352
496
  download.add_argument("version", help="Version to download")
353
497
  download.add_argument("--out", "-o", help="Folder to download to", default=None)
354
498
  download.add_argument(
355
- "--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",
356
505
  )
357
506
 
358
507
  ### Backup subcommand
@@ -364,21 +513,34 @@ def main() -> None:
364
513
  "--remote",
365
514
  help="Remote directory to backup. Defaults to download folder",
366
515
  default="",
516
+ dest="remote",
367
517
  )
368
518
  backup.add_argument(
369
519
  "-l",
370
520
  "--local",
371
521
  help="Local directory to backup to. Defaults to download folder",
372
522
  default="./",
523
+ dest="local",
373
524
  )
374
525
  backup.add_argument(
375
- "-nr",
526
+ "-R",
376
527
  "--no-recursion",
377
528
  help="Disables recursively backup remote directory",
378
529
  action="store_true",
530
+ dest="no_recursion",
379
531
  )
380
532
  backup.add_argument(
381
- "-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",
382
544
  )
383
545
 
384
546
  ### Cat subcommand
@@ -398,7 +560,7 @@ def main() -> None:
398
560
  "extract", help="Extract the specified version update file"
399
561
  )
400
562
  extract.add_argument("file", help="Path to update file to extract", default=None)
401
- 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")
402
564
 
403
565
  ### Mount subcommand
404
566
  mount = subparsers.add_parser(
@@ -423,6 +585,7 @@ def main() -> None:
423
585
  "--remote",
424
586
  help="Remote directory to upload to. Defaults to root folder",
425
587
  default="",
588
+ dest="remote",
426
589
  )
427
590
 
428
591
  ### Status subcommand
@@ -436,7 +599,14 @@ def main() -> None:
436
599
  )
437
600
 
438
601
  ### List subcommand
439
- 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
+ )
440
610
 
441
611
  ### Setting logging level
442
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):