Skip to content

Commit 2cf0414

Browse files
committed
Merge branch 'task/wifi-no-retry' into 'master'
provision: Provide a --no-retry option to fail gracefully and avoid user interfactive prompts See merge request app-frameworks/esp-rainmaker-cli!77
2 parents 15c6a1c + eef580e commit 2cf0414

5 files changed

Lines changed: 54 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
All major changes to ESP RainMaker CLI will be documented in this file.
44

5+
## [1.8.1] - 19-Dec-2025
6+
### Added
7+
- An option --no-retry for provisioning, to avoid user-interactive prompts asking to retry in case of failures
8+
59
## [1.8.0] - 18-Dec-2025
610
### Added
711
- Retry support for WiFi provisioning - allows users to retry provisioning on failure,

rainmaker/rainmaker.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,10 @@ def main():
401401
'Values from QR code will be used as defaults, but can be overridden\n'
402402
'by explicit --transport, --device_name, and --pop options')
403403

404+
provision_parser.add_argument('--no-retry',
405+
action='store_true',
406+
help='Exit immediately on provisioning failure without prompting for retry')
407+
404408
add_profile_argument(provision_parser)
405409
provision_parser.set_defaults(func=provision)
406410

rainmaker/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
# SPDX-License-Identifier: Apache-2.0
66

77
# This file contains the version information for the ESP RainMaker CLI
8-
VERSION = "1.8.0"
8+
VERSION = "1.8.1"

rmaker_cmd/provision.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def provision(vars=None):
4040
- `ssid`: WiFi SSID
4141
- `passphrase`: WiFi password
4242
- `profile`: Profile to use for the operation
43+
- `no_retry`: If True, exit immediately if prov-ctrl succeeds without prompting for retry
4344
:type vars: dict | None
4445
4546
:raises Exception: If there is an issue with provisioning or adding device
@@ -152,7 +153,8 @@ def provision(vars=None):
152153
sec2_username=sec2_username,
153154
sec2_password=sec2_password,
154155
device_name=device_name,
155-
session=curr_session
156+
session=curr_session,
157+
no_retry=vars.get('no_retry', False)
156158
)
157159
except RuntimeError as claim_err:
158160
# Handle claim requirement error specifically
@@ -161,14 +163,28 @@ def provision(vars=None):
161163
print(f'❌ Error: {error_msg}')
162164
sys.exit(1)
163165

164-
# Handle tuple return (node_id, challenge_response_performed)
166+
# Handle tuple return
167+
# Can be: (node_id, challenge_response_performed) on success
168+
# Or: (None, False, prov_ctrl_succeeded) on failure after prov-ctrl attempt
169+
# Or: None on early failure (before prov-ctrl attempt)
170+
prov_ctrl_succeeded = False
165171
if isinstance(result, tuple):
166-
node_id, challenge_response_performed = result
172+
if len(result) == 3:
173+
# Failure case with prov_ctrl_succeeded flag
174+
node_id, challenge_response_performed, prov_ctrl_succeeded = result
175+
else:
176+
# Success case
177+
node_id, challenge_response_performed = result
167178
else:
168179
node_id = result
169180
challenge_response_performed = False
170181

171182
if node_id is None:
183+
# If prov-ctrl succeeded, don't show factory defaults message
184+
# (whether --no-retry was used or user declined retry)
185+
if prov_ctrl_succeeded:
186+
# Message already printed by provision_device
187+
return
172188
# Error message already printed by provision_device when reset fails
173189
# Only log for debugging purposes
174190
log.error(PROVISION_FAILURE_MSG)

rmaker_tools/rmaker_prov/esp_rainmaker_prov.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ def get_wifi_creds_from_scanlist(transport_mode, obj_transport,
163163

164164
def provision_device(transport_mode, pop, userid, secretkey,
165165
ssid=None, passphrase=None, security_version=None,
166-
sec2_username='', sec2_password='', device_name=None, session=None):
166+
sec2_username='', sec2_password='', device_name=None, session=None, no_retry=False):
167167
"""
168168
Wi-Fi Provision a device
169169
@@ -209,6 +209,9 @@ def provision_device(transport_mode, pop, userid, secretkey,
209209
:param session: Authenticated session object for challenge-response
210210
:type session: object, optional
211211
212+
:param no_retry: If True, exit immediately if prov-ctrl succeeds without prompting for retry
213+
:type no_retry: bool, optional
214+
212215
:return: nodeid (Node Identifier) on Success, None on Failure
213216
:rtype: str | None
214217
"""
@@ -337,6 +340,7 @@ def provision_device(transport_mode, pop, userid, secretkey,
337340
print("No session provided, proceeding with traditional flow")
338341

339342
dynamic_credentials = not bool(ssid and passphrase)
343+
prov_ctrl_succeeded = False # Track if prov-ctrl reset succeeded
340344

341345
def prompt_yes_no(prompt, default=False):
342346
while True:
@@ -350,35 +354,48 @@ def prompt_yes_no(prompt, default=False):
350354
print('Please respond with yes or no (y/n).')
351355

352356
def request_device_reset():
357+
nonlocal prov_ctrl_succeeded
353358
# Try reset silently - only show messages if it succeeds
354359
try:
355360
reset_ok = esp_prov.ctrl_reset(obj_transport, obj_security)
356361
except RuntimeError as err:
362+
prov_ctrl_succeeded = False
357363
return False
358364

359365
if not reset_ok:
366+
prov_ctrl_succeeded = False
360367
return False
361368

362369
# Only show message if reset succeeded
363370
print('Device reset via prov-ctrl successful.')
371+
prov_ctrl_succeeded = True
364372
time.sleep(2)
365373
return True
366374

367375
def handle_retry(reason_msg):
368-
nonlocal ssid, passphrase, dynamic_credentials
376+
nonlocal ssid, passphrase, dynamic_credentials, prov_ctrl_succeeded
369377
if reason_msg:
370378
print(reason_msg)
371379

372380
# Try to reset device state before asking user if they want to retry
373381
if not request_device_reset():
374382
# If reset failed, print error message and exit without offering retry
375383
# Don't mention prov-ctrl since it failed silently
384+
prov_ctrl_succeeded = False
376385
print('Provisioning Failed. Reset your board to factory defaults and retry.')
377386
return False
378387

388+
# If --no-retry is set and reset succeeded, exit with message
389+
# Don't show factory defaults message since prov-ctrl succeeded
390+
if no_retry:
391+
print('Device is reset to provisioning. Please try again')
392+
return False
393+
379394
# Only offer retry if reset succeeded
380395
if not prompt_yes_no('Would you like to retry provisioning? [y/N]: '):
381-
print('Provisioning Failed. Reset your board to factory defaults and retry.')
396+
# User declined retry, but prov-ctrl succeeded, so don't ask to reset to factory defaults
397+
# Since prov-ctrl succeeded, device is already reset to provisioning state
398+
print('Provisioning cancelled.')
382399
return False
383400

384401
# If we have an SSID, offer to retry with the same SSID but new password
@@ -444,13 +461,15 @@ def wait_for_wifi_connection():
444461
passphrase):
445462
if handle_retry("Sending Wi-Fi credentials to node - Failed"):
446463
continue
447-
return None
464+
# Return tuple with prov_ctrl_succeeded flag
465+
return (None, False, prov_ctrl_succeeded)
448466
print("Sending Wi-Fi credentials to node - Successful")
449467

450468
if not esp_prov.apply_wifi_config(obj_transport, obj_security):
451469
if handle_retry("Applying Wi-Fi config to node - Failed"):
452470
continue
453-
return None
471+
# Return tuple with prov_ctrl_succeeded flag
472+
return (None, False, prov_ctrl_succeeded)
454473
print("Applying Wi-Fi config to node - Successful")
455474

456475
success, status = wait_for_wifi_connection()
@@ -464,7 +483,8 @@ def wait_for_wifi_connection():
464483
reason_msg = f"Wi-Fi Provisioning Failed. Unexpected status: {status}"
465484

466485
if not handle_retry(reason_msg):
467-
return None
486+
# Return tuple with prov_ctrl_succeeded flag
487+
return (None, False, prov_ctrl_succeeded)
468488

469489

470490
if __name__ == '__main__':

0 commit comments

Comments
 (0)