esp-rainmaker-cli 1.13.1__tar.gz → 1.14.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.
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/PKG-INFO +21 -1
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/esp_rainmaker_cli.egg-info/PKG-INFO +21 -1
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rainmaker/rainmaker.py +139 -1
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rainmaker/version.py +1 -1
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/group.py +218 -2
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/node.py +22 -2
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/provision.py +9 -2
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/node_cache.py +4 -1
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/session.py +148 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/security/security2.py +66 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/integration.py +105 -21
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/raw_config.py +4 -2
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/raw_params.py +5 -2
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/esp_rainmaker_prov.py +35 -16
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/LICENSE +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/README.md +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/esp_rainmaker_cli.egg-info/SOURCES.txt +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/esp_rainmaker_cli.egg-info/dependency_links.txt +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/esp_rainmaker_cli.egg-info/entry_points.txt +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/esp_rainmaker_cli.egg-info/requires.txt +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/esp_rainmaker_cli.egg-info/top_level.txt +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rainmaker/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/automation.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/browserlogin.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/cache.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/cmd_response.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/html/welcome.html +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/stream.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/test.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/user.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/aws_credentials.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/cmd_response.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/configmanager.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/constants.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/device.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/envval.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/exceptions.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/kvs_streaming.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/local_control.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/logger.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/node.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/profile_manager.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/profile_utils.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/schedule_utils.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/serverconfig.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/service.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/session_store.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/simple_local_control.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_lib/user.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/discovery/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/discovery/mdns_discovery.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/prov/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/prov/wifi_ctrl.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/prov/wifi_prov.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/prov/wifi_scan.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/security/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/security/security.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/security/security0.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/security/security1.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/security/srp6a.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/transport/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/transport/ble_cli.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/transport/transport.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/transport/transport_ble.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/transport/transport_console.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/transport/transport_http.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/utils/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/utils/convenience.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_claim/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_claim/claim.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_claim/claim_config.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/constants_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/esp_local_ctrl.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/esp_local_ctrl_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/esp_prov.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/esp_rainmaker_ctrl.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/esp_rmaker_prov_local_ctrl_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/proto/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/proto/proto_lc.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/prov/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/prov/custom_prov.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/prov/wifi_ctrl.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/sec0_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/sec1_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/sec2_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/security/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/session_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/transport/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/utils/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/challenge_response.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/config/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/config/custom_cloud_config_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/config/esp_rmaker_chal_resp_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/config/esp_rmaker_claim_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/config/esp_rmaker_user_mapping_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/on_network_chal_resp.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/proto/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/protocomm/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/protocomm/python/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/protocomm/python/constants_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/protocomm/python/sec0_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/protocomm/python/sec1_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/protocomm/python/sec2_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/protocomm/python/session_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/prov/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/prov/prov_util.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/prov/user_mapping.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/security/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/transport/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/utils/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/wifi_provisioning/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/wifi_provisioning/python/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/wifi_provisioning/python/wifi_config_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/wifi_provisioning/python/wifi_constants_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/wifi_provisioning/python/wifi_ctrl_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/wifi_provisioning/python/wifi_scan_pb2.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/server_cert/__init__.py +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/server_cert/server_cert.pem +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/setup.cfg +0 -0
- {esp_rainmaker_cli-1.13.1 → esp_rainmaker_cli-1.14.1}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: esp-rainmaker-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.14.1
|
|
4
4
|
Summary: A python utility to perform host based claiming
|
|
5
5
|
Home-page: https://github.com/espressif/esp-rainmaker-cli
|
|
6
6
|
Author: Espressif Systems
|
|
@@ -122,6 +122,26 @@ Changelog
|
|
|
122
122
|
|
|
123
123
|
All major changes to ESP RainMaker CLI will be documented in this file.
|
|
124
124
|
|
|
125
|
+
## [1.14.1] - 05-Jun-2026
|
|
126
|
+
### Bugfixes
|
|
127
|
+
- Security 2 fixes across provisioning and local control:
|
|
128
|
+
- Provisioning auto-detect now reads `prov.sec_ver` from device capabilities
|
|
129
|
+
(was forcing sec1 on sec2 devices, causing firmware "Security version mismatch")
|
|
130
|
+
- `sec_patch_ver` discovered from device capabilities / `esp_local_ctrl/version`,
|
|
131
|
+
enabling the counter-in-IV scheme on ESP-IDF v5.4+ firmware
|
|
132
|
+
- `--sec2_username` option added to `--local` commands (`getnodeconfig`,
|
|
133
|
+
`getparams`, `setparams`); defaults to `wifiprov`, picks up cached
|
|
134
|
+
`local_control_username` from cloud params; `--pop` used as the SRP6a password
|
|
135
|
+
- QR code `username` / `password` fields now honoured by `provision`
|
|
136
|
+
- `Security2.serialize`/`deserialize` for local-control session resume parity with Security 1
|
|
137
|
+
|
|
138
|
+
## [1.14.0] - 24-Apr-2026
|
|
139
|
+
### Added
|
|
140
|
+
- New `group sharing` subcommand for sharing device groups / Matter fabrics between users:
|
|
141
|
+
- `add`, `remove`, `list`, `list-requests`, `accept`, `decline`, `cancel` operations
|
|
142
|
+
- Supports primary/secondary roles, sub-roles (1-4), metadata, and ownership transfer (`--transfer`, `--new-role`)
|
|
143
|
+
- Parsed, human-readable output by default; pass `--raw` on any operation to get the underlying JSON response
|
|
144
|
+
|
|
125
145
|
## [1.13.1] - 23-Apr-2026
|
|
126
146
|
### Added
|
|
127
147
|
- Node sharing enhancements:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: esp-rainmaker-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.14.1
|
|
4
4
|
Summary: A python utility to perform host based claiming
|
|
5
5
|
Home-page: https://github.com/espressif/esp-rainmaker-cli
|
|
6
6
|
Author: Espressif Systems
|
|
@@ -122,6 +122,26 @@ Changelog
|
|
|
122
122
|
|
|
123
123
|
All major changes to ESP RainMaker CLI will be documented in this file.
|
|
124
124
|
|
|
125
|
+
## [1.14.1] - 05-Jun-2026
|
|
126
|
+
### Bugfixes
|
|
127
|
+
- Security 2 fixes across provisioning and local control:
|
|
128
|
+
- Provisioning auto-detect now reads `prov.sec_ver` from device capabilities
|
|
129
|
+
(was forcing sec1 on sec2 devices, causing firmware "Security version mismatch")
|
|
130
|
+
- `sec_patch_ver` discovered from device capabilities / `esp_local_ctrl/version`,
|
|
131
|
+
enabling the counter-in-IV scheme on ESP-IDF v5.4+ firmware
|
|
132
|
+
- `--sec2_username` option added to `--local` commands (`getnodeconfig`,
|
|
133
|
+
`getparams`, `setparams`); defaults to `wifiprov`, picks up cached
|
|
134
|
+
`local_control_username` from cloud params; `--pop` used as the SRP6a password
|
|
135
|
+
- QR code `username` / `password` fields now honoured by `provision`
|
|
136
|
+
- `Security2.serialize`/`deserialize` for local-control session resume parity with Security 1
|
|
137
|
+
|
|
138
|
+
## [1.14.0] - 24-Apr-2026
|
|
139
|
+
### Added
|
|
140
|
+
- New `group sharing` subcommand for sharing device groups / Matter fabrics between users:
|
|
141
|
+
- `add`, `remove`, `list`, `list-requests`, `accept`, `decline`, `cancel` operations
|
|
142
|
+
- Supports primary/secondary roles, sub-roles (1-4), metadata, and ownership transfer (`--transfer`, `--new-role`)
|
|
143
|
+
- Parsed, human-readable output by default; pass `--raw` on any operation to get the underlying JSON response
|
|
144
|
+
|
|
125
145
|
## [1.13.1] - 23-Apr-2026
|
|
126
146
|
### Added
|
|
127
147
|
- Node sharing enhancements:
|
|
@@ -15,7 +15,12 @@ from rmaker_cmd.cmd_response import get_cmd_requests, create_cmd_request
|
|
|
15
15
|
from rmaker_cmd.provision import provision
|
|
16
16
|
from rmaker_cmd.test import test
|
|
17
17
|
from rmaker_lib.logger import log
|
|
18
|
-
from rmaker_cmd.group import group_add, group_remove, group_edit, group_list,
|
|
18
|
+
from rmaker_cmd.group import (group_add, group_remove, group_edit, group_list,
|
|
19
|
+
group_show, group_add_nodes, group_remove_nodes,
|
|
20
|
+
group_list_nodes, group_sharing_add,
|
|
21
|
+
group_sharing_remove, group_sharing_list,
|
|
22
|
+
group_sharing_list_requests, group_sharing_accept,
|
|
23
|
+
group_sharing_decline, group_sharing_cancel)
|
|
19
24
|
from rmaker_cmd.automation import automation_add, automation_edit, automation_remove, automation_get
|
|
20
25
|
from rmaker_cmd.cache import cache_manage
|
|
21
26
|
from rmaker_cmd.stream import stream_video
|
|
@@ -280,6 +285,10 @@ def main():
|
|
|
280
285
|
choices=[0, 1, 2],
|
|
281
286
|
default=None,
|
|
282
287
|
help='Security version for local control (default: 1)')
|
|
288
|
+
getnodeconfig_parser.add_argument('--sec2_username',
|
|
289
|
+
type=str,
|
|
290
|
+
default=None,
|
|
291
|
+
help='SRP6a username for Security 2 local control. Defaults to the username cached from cloud params, else \'wifiprov\'. --pop is used as the password.')
|
|
283
292
|
getnodeconfig_parser.add_argument('--local-raw',
|
|
284
293
|
action='store_true',
|
|
285
294
|
help='Use local control via raw endpoints (get_config with fragmentation) instead of esp_local_ctrl')
|
|
@@ -350,6 +359,10 @@ def main():
|
|
|
350
359
|
choices=[0, 1, 2],
|
|
351
360
|
default=None,
|
|
352
361
|
help='Security version for local control (default: 1)')
|
|
362
|
+
setparams_parser.add_argument('--sec2_username',
|
|
363
|
+
type=str,
|
|
364
|
+
default=None,
|
|
365
|
+
help='SRP6a username for Security 2 local control. Defaults to the username cached from cloud params, else \'wifiprov\'. --pop is used as the password.')
|
|
353
366
|
setparams_parser.add_argument('--local-raw',
|
|
354
367
|
action='store_true',
|
|
355
368
|
help='Use local control via raw endpoints (get_params/set_params) instead of esp_local_ctrl')
|
|
@@ -393,6 +406,10 @@ def main():
|
|
|
393
406
|
choices=[0, 1, 2],
|
|
394
407
|
default=None,
|
|
395
408
|
help='Security version for local control (default: 1)')
|
|
409
|
+
getparams_parser.add_argument('--sec2_username',
|
|
410
|
+
type=str,
|
|
411
|
+
default=None,
|
|
412
|
+
help='SRP6a username for Security 2 local control. Defaults to the username cached from cloud params, else \'wifiprov\'. --pop is used as the password.')
|
|
396
413
|
getparams_parser.add_argument('--local-raw',
|
|
397
414
|
action='store_true',
|
|
398
415
|
help='Use local control via raw endpoints (get_params/set_params) instead of esp_local_ctrl')
|
|
@@ -934,6 +951,127 @@ def main():
|
|
|
934
951
|
add_profile_argument(group_list_nodes_parser)
|
|
935
952
|
group_list_nodes_parser.set_defaults(func=group_list_nodes)
|
|
936
953
|
|
|
954
|
+
# group sharing
|
|
955
|
+
group_sharing_parser = group_subparsers.add_parser(
|
|
956
|
+
'sharing',
|
|
957
|
+
help='Share groups / matter fabrics between users',
|
|
958
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
959
|
+
description='Share groups or matter fabrics with other users.\n'
|
|
960
|
+
'Primary users can share groups; secondary users can view '
|
|
961
|
+
'their sharing or leave groups they have access to.')
|
|
962
|
+
group_sharing_parser.set_defaults(
|
|
963
|
+
func=lambda vars=None: group_sharing_parser.print_help())
|
|
964
|
+
group_sharing_subparsers = group_sharing_parser.add_subparsers(
|
|
965
|
+
dest='sharing_command', help='Group sharing operations')
|
|
966
|
+
|
|
967
|
+
# group sharing add
|
|
968
|
+
gs_add_parser = group_sharing_subparsers.add_parser(
|
|
969
|
+
'add', help='Share group(s) with a user (or add self-sharing without --user)',
|
|
970
|
+
formatter_class=argparse.RawTextHelpFormatter)
|
|
971
|
+
gs_add_parser.add_argument('--groups', type=str, required=True,
|
|
972
|
+
metavar='<group_ids>',
|
|
973
|
+
help='Comma separated group ids (max 10)')
|
|
974
|
+
gs_add_parser.add_argument('--user', type=str, metavar='<user_name>',
|
|
975
|
+
help='User name (email) to share with. Optional.')
|
|
976
|
+
gs_add_parser.add_argument('--primary', action='store_true',
|
|
977
|
+
help='Share with primary access (default: secondary)')
|
|
978
|
+
gs_add_parser.add_argument('--sub-role', type=int, choices=range(1, 5),
|
|
979
|
+
metavar='<1-4>',
|
|
980
|
+
help='Custom sub role (1-4)')
|
|
981
|
+
gs_add_parser.add_argument('--metadata', type=str, metavar='<json>',
|
|
982
|
+
help='Custom metadata as a JSON string')
|
|
983
|
+
gs_add_parser.add_argument('--transfer', action='store_true',
|
|
984
|
+
help='Transfer ownership of the group(s) to the user')
|
|
985
|
+
gs_add_parser.add_argument('--new-role', type=str, choices=['secondary'],
|
|
986
|
+
help='Role to assign to self after transfer '
|
|
987
|
+
'(only valid with --transfer)')
|
|
988
|
+
gs_add_parser.add_argument('--raw', action='store_true',
|
|
989
|
+
help='Print the raw JSON response instead of parsed output')
|
|
990
|
+
add_profile_argument(gs_add_parser)
|
|
991
|
+
gs_add_parser.set_defaults(func=group_sharing_add)
|
|
992
|
+
|
|
993
|
+
# group sharing remove
|
|
994
|
+
gs_remove_parser = group_sharing_subparsers.add_parser(
|
|
995
|
+
'remove', help='Remove group sharing with a user',
|
|
996
|
+
formatter_class=argparse.RawTextHelpFormatter)
|
|
997
|
+
gs_remove_parser.add_argument('--groups', type=str, required=True,
|
|
998
|
+
metavar='<group_ids>',
|
|
999
|
+
help='Comma separated group ids')
|
|
1000
|
+
gs_remove_parser.add_argument('--user', type=str, required=True,
|
|
1001
|
+
metavar='<user_name>',
|
|
1002
|
+
help='User name (email) to unshare with')
|
|
1003
|
+
gs_remove_parser.add_argument('--raw', action='store_true',
|
|
1004
|
+
help='Print the raw JSON response instead of parsed output')
|
|
1005
|
+
add_profile_argument(gs_remove_parser)
|
|
1006
|
+
gs_remove_parser.set_defaults(func=group_sharing_remove)
|
|
1007
|
+
|
|
1008
|
+
# group sharing list
|
|
1009
|
+
gs_list_parser = group_sharing_subparsers.add_parser(
|
|
1010
|
+
'list', help='Show sharing details for groups / matter fabrics',
|
|
1011
|
+
formatter_class=argparse.RawTextHelpFormatter)
|
|
1012
|
+
gs_list_parser.add_argument('--group-id', type=str, metavar='<group_id>',
|
|
1013
|
+
help='Fetch sharing details for a single group')
|
|
1014
|
+
gs_list_parser.add_argument('--sub-groups', action='store_true',
|
|
1015
|
+
help='Include sharing details of sub-groups')
|
|
1016
|
+
gs_list_parser.add_argument('--parent-groups', action='store_true',
|
|
1017
|
+
help='Include sharing details of parent groups')
|
|
1018
|
+
gs_list_parser.add_argument('--metadata', action='store_true',
|
|
1019
|
+
help='Include metadata set during sharing')
|
|
1020
|
+
gs_list_parser.add_argument('--raw', action='store_true',
|
|
1021
|
+
help='Print the raw JSON response instead of parsed output')
|
|
1022
|
+
add_profile_argument(gs_list_parser)
|
|
1023
|
+
gs_list_parser.set_defaults(func=group_sharing_list)
|
|
1024
|
+
|
|
1025
|
+
# group sharing list-requests
|
|
1026
|
+
gs_list_req_parser = group_sharing_subparsers.add_parser(
|
|
1027
|
+
'list-requests', help='List pending group sharing requests',
|
|
1028
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
1029
|
+
description='List pending group sharing requests.\n'
|
|
1030
|
+
'Use --primary-user to list requests raised by you.\n'
|
|
1031
|
+
'Without the flag, lists requests you have received.')
|
|
1032
|
+
gs_list_req_parser.add_argument('--id', type=str, metavar='<request_id>',
|
|
1033
|
+
help='Fetch a specific request by id')
|
|
1034
|
+
gs_list_req_parser.add_argument('--primary-user', action='store_true',
|
|
1035
|
+
help='List requests raised by current user '
|
|
1036
|
+
'(default: requests received)')
|
|
1037
|
+
gs_list_req_parser.add_argument('--raw', action='store_true',
|
|
1038
|
+
help='Print the raw JSON response instead of parsed output')
|
|
1039
|
+
add_profile_argument(gs_list_req_parser)
|
|
1040
|
+
gs_list_req_parser.set_defaults(func=group_sharing_list_requests)
|
|
1041
|
+
|
|
1042
|
+
# group sharing accept
|
|
1043
|
+
gs_accept_parser = group_sharing_subparsers.add_parser(
|
|
1044
|
+
'accept', help='Accept a pending group sharing request')
|
|
1045
|
+
gs_accept_parser.add_argument('--id', type=str, required=True,
|
|
1046
|
+
metavar='<request_id>',
|
|
1047
|
+
help='Id of the sharing request')
|
|
1048
|
+
gs_accept_parser.add_argument('--raw', action='store_true',
|
|
1049
|
+
help='Print the raw JSON response instead of parsed output')
|
|
1050
|
+
add_profile_argument(gs_accept_parser)
|
|
1051
|
+
gs_accept_parser.set_defaults(func=group_sharing_accept)
|
|
1052
|
+
|
|
1053
|
+
# group sharing decline
|
|
1054
|
+
gs_decline_parser = group_sharing_subparsers.add_parser(
|
|
1055
|
+
'decline', help='Decline a pending group sharing request')
|
|
1056
|
+
gs_decline_parser.add_argument('--id', type=str, required=True,
|
|
1057
|
+
metavar='<request_id>',
|
|
1058
|
+
help='Id of the sharing request')
|
|
1059
|
+
gs_decline_parser.add_argument('--raw', action='store_true',
|
|
1060
|
+
help='Print the raw JSON response instead of parsed output')
|
|
1061
|
+
add_profile_argument(gs_decline_parser)
|
|
1062
|
+
gs_decline_parser.set_defaults(func=group_sharing_decline)
|
|
1063
|
+
|
|
1064
|
+
# group sharing cancel
|
|
1065
|
+
gs_cancel_parser = group_sharing_subparsers.add_parser(
|
|
1066
|
+
'cancel', help='Cancel a pending group sharing request (primary side)')
|
|
1067
|
+
gs_cancel_parser.add_argument('--id', type=str, required=True,
|
|
1068
|
+
metavar='<request_id>',
|
|
1069
|
+
help='Id of the sharing request')
|
|
1070
|
+
gs_cancel_parser.add_argument('--raw', action='store_true',
|
|
1071
|
+
help='Print the raw JSON response instead of parsed output')
|
|
1072
|
+
add_profile_argument(gs_cancel_parser)
|
|
1073
|
+
gs_cancel_parser.set_defaults(func=group_sharing_cancel)
|
|
1074
|
+
|
|
937
1075
|
# Automation Management
|
|
938
1076
|
automation_parser = subparsers.add_parser('automations',
|
|
939
1077
|
help='Manage automation triggers')
|
|
@@ -3,8 +3,17 @@
|
|
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
|
|
5
5
|
from rmaker_lib.profile_utils import get_session_with_profile
|
|
6
|
+
import datetime
|
|
6
7
|
import json
|
|
7
|
-
|
|
8
|
+
import sys
|
|
9
|
+
from rmaker_cmd.node import (
|
|
10
|
+
_get_description,
|
|
11
|
+
_get_request_expiration,
|
|
12
|
+
_get_request_id,
|
|
13
|
+
_get_status,
|
|
14
|
+
_print_api_error,
|
|
15
|
+
_print_node_details,
|
|
16
|
+
)
|
|
8
17
|
|
|
9
18
|
|
|
10
19
|
def _handle_error(e, operation):
|
|
@@ -249,4 +258,211 @@ def group_list_nodes(vars=None):
|
|
|
249
258
|
else:
|
|
250
259
|
print(f"{idx}. {name} (ID: {group_id})")
|
|
251
260
|
except Exception as e:
|
|
252
|
-
_handle_error(e, "list nodes in group")
|
|
261
|
+
_handle_error(e, "list nodes in group")
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _split_groups(groups_arg):
|
|
265
|
+
return [g.strip() for g in groups_arg.split(',') if g.strip()]
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _print_group_sharing_request_details(resp, is_primary_user=False):
|
|
269
|
+
requests_in_resp = resp.get('sharing_requests') or []
|
|
270
|
+
if not requests_in_resp:
|
|
271
|
+
print("No pending requests")
|
|
272
|
+
return
|
|
273
|
+
request_exists = False
|
|
274
|
+
for request in requests_in_resp:
|
|
275
|
+
if request.get('request_status', '').lower() == 'declined':
|
|
276
|
+
continue
|
|
277
|
+
request_exists = True
|
|
278
|
+
print("\n{:<15}: {}".format('Request Id', request['request_id']))
|
|
279
|
+
print("{:<15}: {}".format('Group Id(s)', ','.join(request.get('group_ids', []))))
|
|
280
|
+
group_names = request.get('group_names') or []
|
|
281
|
+
if group_names:
|
|
282
|
+
print("{:<15}: {}".format('Group Name(s)', ','.join(group_names)))
|
|
283
|
+
if is_primary_user:
|
|
284
|
+
if 'user_name' in request and request['user_name']:
|
|
285
|
+
print("{:<15}: {}".format('Shared with', request['user_name']))
|
|
286
|
+
else:
|
|
287
|
+
if 'primary_user_name' in request and request['primary_user_name']:
|
|
288
|
+
print("{:<15}: {}".format('Shared by', request['primary_user_name']))
|
|
289
|
+
if 'user_role' in request and request['user_role']:
|
|
290
|
+
print("{:<15}: {}".format('Role', request['user_role']))
|
|
291
|
+
print(_get_request_expiration(request))
|
|
292
|
+
|
|
293
|
+
if not request_exists:
|
|
294
|
+
print("No pending requests")
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def _print_group_sharing_details(resp):
|
|
298
|
+
try:
|
|
299
|
+
groups_in_resp = resp.get('group_sharing') or []
|
|
300
|
+
if not groups_in_resp:
|
|
301
|
+
print("No shared groups")
|
|
302
|
+
return
|
|
303
|
+
for group in groups_in_resp:
|
|
304
|
+
print("\nGroup Id: {}".format(group.get('group_id', '')))
|
|
305
|
+
users = group.get('users') or {}
|
|
306
|
+
primary_users = ','.join(users.get('primary') or [])
|
|
307
|
+
secondary_users = ','.join(users.get('secondary') or [])
|
|
308
|
+
print("{:<7}: {:<9}: {}".format('Users', 'Primary', primary_users), end='')
|
|
309
|
+
if secondary_users:
|
|
310
|
+
print("\n{:>18}: {}".format('Secondary', secondary_users), end='')
|
|
311
|
+
print()
|
|
312
|
+
except KeyError:
|
|
313
|
+
print("Error in displaying details...Please check API Json...Exiting...")
|
|
314
|
+
sys.exit(0)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _emit(resp, raw, render):
|
|
318
|
+
if raw:
|
|
319
|
+
print(json.dumps(resp, indent=2))
|
|
320
|
+
return
|
|
321
|
+
if isinstance(resp, dict) and resp.get('status', '').lower() != 'success' and 'error_code' in resp:
|
|
322
|
+
_print_api_error(resp)
|
|
323
|
+
return
|
|
324
|
+
render(resp)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def group_sharing_add(vars=None):
|
|
328
|
+
try:
|
|
329
|
+
s = get_session_with_profile(vars or {})
|
|
330
|
+
groups = _split_groups(vars['groups'])
|
|
331
|
+
if not groups:
|
|
332
|
+
print("At least one group id must be provided in --groups")
|
|
333
|
+
return
|
|
334
|
+
user = vars.get('user')
|
|
335
|
+
primary = vars.get('primary') if vars.get('primary') else None
|
|
336
|
+
sub_role = vars.get('sub_role')
|
|
337
|
+
transfer = vars.get('transfer') if vars.get('transfer') else None
|
|
338
|
+
new_role = vars.get('new_role')
|
|
339
|
+
metadata = None
|
|
340
|
+
metadata_arg = vars.get('metadata')
|
|
341
|
+
if metadata_arg:
|
|
342
|
+
try:
|
|
343
|
+
metadata = json.loads(metadata_arg)
|
|
344
|
+
except Exception as e:
|
|
345
|
+
print("Invalid JSON for --metadata:", e)
|
|
346
|
+
return
|
|
347
|
+
print("Sharing group(s) with user")
|
|
348
|
+
resp = s.add_user_group_sharing(
|
|
349
|
+
groups=groups,
|
|
350
|
+
user_name=user,
|
|
351
|
+
primary=primary,
|
|
352
|
+
sub_role=sub_role,
|
|
353
|
+
metadata=metadata,
|
|
354
|
+
transfer=transfer,
|
|
355
|
+
new_role=new_role,
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
def render(r):
|
|
359
|
+
if r.get('status', '').lower() == 'success':
|
|
360
|
+
print("{:<11}: {}".format('Status', _get_status(r)))
|
|
361
|
+
print("{:<11}: {}".format('Description', _get_description(r)))
|
|
362
|
+
if 'request_id' in r:
|
|
363
|
+
print("{:<11}: {}".format('Request Id', _get_request_id(r)))
|
|
364
|
+
|
|
365
|
+
_emit(resp, vars.get('raw', False), render)
|
|
366
|
+
except Exception as e:
|
|
367
|
+
_handle_error(e, "add group sharing")
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def group_sharing_remove(vars=None):
|
|
371
|
+
try:
|
|
372
|
+
s = get_session_with_profile(vars or {})
|
|
373
|
+
groups = _split_groups(vars['groups'])
|
|
374
|
+
if not groups:
|
|
375
|
+
print("At least one group id must be provided in --groups")
|
|
376
|
+
return
|
|
377
|
+
print("Removing group sharing")
|
|
378
|
+
resp = s.remove_user_group_sharing(
|
|
379
|
+
groups=','.join(groups),
|
|
380
|
+
user_name=vars['user'],
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
def render(r):
|
|
384
|
+
if r.get('status', '').lower() == 'success':
|
|
385
|
+
print("{}: {}".format(_get_status(r), _get_description(r)))
|
|
386
|
+
|
|
387
|
+
_emit(resp, vars.get('raw', False), render)
|
|
388
|
+
except Exception as e:
|
|
389
|
+
_handle_error(e, "remove group sharing")
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def group_sharing_list(vars=None):
|
|
393
|
+
try:
|
|
394
|
+
s = get_session_with_profile(vars or {})
|
|
395
|
+
resp = s.get_user_group_sharing(
|
|
396
|
+
group_id=vars.get('group_id'),
|
|
397
|
+
sub_groups=vars.get('sub_groups', False),
|
|
398
|
+
parent_groups=vars.get('parent_groups', False),
|
|
399
|
+
metadata=vars.get('metadata', False),
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
def render(r):
|
|
403
|
+
print("Displaying group sharing details")
|
|
404
|
+
_print_group_sharing_details(r)
|
|
405
|
+
|
|
406
|
+
_emit(resp, vars.get('raw', False), render)
|
|
407
|
+
except Exception as e:
|
|
408
|
+
_handle_error(e, "list group sharing")
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def group_sharing_list_requests(vars=None):
|
|
412
|
+
try:
|
|
413
|
+
s = get_session_with_profile(vars or {})
|
|
414
|
+
is_primary = vars.get('primary_user', False)
|
|
415
|
+
resp = s.get_user_group_sharing_requests(
|
|
416
|
+
request_id=vars.get('id'),
|
|
417
|
+
primary_user=is_primary,
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
def render(r):
|
|
421
|
+
print("Displaying pending group sharing requests")
|
|
422
|
+
print("Current (logged-in) user is set as {} user".format(
|
|
423
|
+
'Primary' if is_primary else 'Secondary'))
|
|
424
|
+
_print_group_sharing_request_details(r, is_primary_user=is_primary)
|
|
425
|
+
|
|
426
|
+
_emit(resp, vars.get('raw', False), render)
|
|
427
|
+
except Exception as e:
|
|
428
|
+
_handle_error(e, "list group sharing requests")
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def _respond_group_sharing(vars, accept):
|
|
432
|
+
action = 'accept' if accept else 'decline'
|
|
433
|
+
try:
|
|
434
|
+
s = get_session_with_profile(vars or {})
|
|
435
|
+
resp = s.respond_user_group_sharing_request(
|
|
436
|
+
request_id=vars['id'], accept=accept)
|
|
437
|
+
|
|
438
|
+
def render(r):
|
|
439
|
+
if r.get('status', '').lower() == 'success':
|
|
440
|
+
print("{:<11}: {}".format('Status', _get_status(r)))
|
|
441
|
+
print("{:<11}: {}".format('Description', _get_description(r)))
|
|
442
|
+
|
|
443
|
+
_emit(resp, vars.get('raw', False), render)
|
|
444
|
+
except Exception as e:
|
|
445
|
+
_handle_error(e, "{} group sharing request".format(action))
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def group_sharing_accept(vars=None):
|
|
449
|
+
_respond_group_sharing(vars, accept=True)
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def group_sharing_decline(vars=None):
|
|
453
|
+
_respond_group_sharing(vars, accept=False)
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def group_sharing_cancel(vars=None):
|
|
457
|
+
try:
|
|
458
|
+
s = get_session_with_profile(vars or {})
|
|
459
|
+
print("Cancelling request")
|
|
460
|
+
resp = s.remove_user_group_sharing_request(request_id=vars['id'])
|
|
461
|
+
|
|
462
|
+
def render(r):
|
|
463
|
+
if r.get('status', '').lower() == 'success':
|
|
464
|
+
print("{}: {}".format(_get_status(r), _get_description(r)))
|
|
465
|
+
|
|
466
|
+
_emit(resp, vars.get('raw', False), render)
|
|
467
|
+
except Exception as e:
|
|
468
|
+
_handle_error(e, "cancel group sharing request")
|
|
@@ -78,6 +78,9 @@ def _build_local_options_with_cache(vars_dict, node_cache=None, session_store=No
|
|
|
78
78
|
sec_ver = raw_sec_ver if raw_sec_ver is not None else 1
|
|
79
79
|
nodeid = vars_dict.get('nodeid', '')
|
|
80
80
|
|
|
81
|
+
user_sec2_username = vars_dict.get('sec2_username') # None => not supplied
|
|
82
|
+
sec2_username = user_sec2_username
|
|
83
|
+
|
|
81
84
|
if node_cache and not pop:
|
|
82
85
|
capability = node_cache.get_local_control_capability(nodeid)
|
|
83
86
|
if capability:
|
|
@@ -99,6 +102,9 @@ def _build_local_options_with_cache(vars_dict, node_cache=None, session_store=No
|
|
|
99
102
|
log.debug(f"Using cached POP for node {nodeid}")
|
|
100
103
|
if lc_info.get('sec_ver') is not None and not explicit_sec_ver:
|
|
101
104
|
sec_ver = lc_info['sec_ver']
|
|
105
|
+
if sec2_username is None and lc_info.get('username'):
|
|
106
|
+
sec2_username = lc_info['username']
|
|
107
|
+
log.debug(f"Using cached sec2 username for node {nodeid}")
|
|
102
108
|
|
|
103
109
|
if not pop and not explicit_sec_ver:
|
|
104
110
|
try:
|
|
@@ -114,15 +120,23 @@ def _build_local_options_with_cache(vars_dict, node_cache=None, session_store=No
|
|
|
114
120
|
pop = lc_info.get('pop', '')
|
|
115
121
|
if lc_info.get('sec_ver') is not None:
|
|
116
122
|
sec_ver = lc_info['sec_ver']
|
|
123
|
+
if sec2_username is None and lc_info.get('username'):
|
|
124
|
+
sec2_username = lc_info['username']
|
|
117
125
|
log.debug(f"Resolved POP from cloud for node {nodeid}")
|
|
118
126
|
except Exception as e:
|
|
119
127
|
log.debug(f"Failed to auto-resolve POP from cloud: {e}")
|
|
120
128
|
|
|
129
|
+
if not sec2_username:
|
|
130
|
+
sec2_username = 'wifiprov'
|
|
131
|
+
if sec_ver == 2:
|
|
132
|
+
print(f"Using default Security 2 username '{sec2_username}'")
|
|
133
|
+
|
|
121
134
|
local_options = {
|
|
122
135
|
'pop': pop,
|
|
123
136
|
'transport': vars_dict.get('transport', 'http'),
|
|
124
137
|
'port': vars_dict.get('port', 8080),
|
|
125
138
|
'sec_ver': sec_ver,
|
|
139
|
+
'sec2_username': sec2_username,
|
|
126
140
|
'local_raw': vars_dict.get('local_raw', False),
|
|
127
141
|
'device_name': vars_dict.get('device_name', None),
|
|
128
142
|
'node_cache': node_cache,
|
|
@@ -1259,6 +1273,7 @@ def get_node_config(vars=None):
|
|
|
1259
1273
|
'transport': vars.get('transport', 'ble'),
|
|
1260
1274
|
'port': vars.get('port', 8080),
|
|
1261
1275
|
'sec_ver': vars.get('sec_ver') if vars.get('sec_ver') is not None else 1,
|
|
1276
|
+
'sec2_username': vars.get('sec2_username', 'wifiprov') or 'wifiprov',
|
|
1262
1277
|
'device_name': vars.get('device_name', None),
|
|
1263
1278
|
'timestamp': timestamp
|
|
1264
1279
|
}
|
|
@@ -1312,7 +1327,8 @@ def get_node_config(vars=None):
|
|
|
1312
1327
|
'pop': vars.get('pop', ''),
|
|
1313
1328
|
'transport': vars.get('transport', 'http'),
|
|
1314
1329
|
'port': vars.get('port', 8080),
|
|
1315
|
-
'sec_ver': vars.get('sec_ver') if vars.get('sec_ver') is not None else 1
|
|
1330
|
+
'sec_ver': vars.get('sec_ver') if vars.get('sec_ver') is not None else 1,
|
|
1331
|
+
'sec2_username': vars.get('sec2_username', 'wifiprov') or 'wifiprov',
|
|
1316
1332
|
}
|
|
1317
1333
|
|
|
1318
1334
|
node_config = run_local_control_operation(
|
|
@@ -1326,7 +1342,8 @@ def get_node_config(vars=None):
|
|
|
1326
1342
|
'pop': vars.get('pop', ''),
|
|
1327
1343
|
'transport': vars.get('transport', 'http'),
|
|
1328
1344
|
'port': vars.get('port', 8080),
|
|
1329
|
-
'sec_ver': vars.get('sec_ver') if vars.get('sec_ver') is not None else 0
|
|
1345
|
+
'sec_ver': vars.get('sec_ver') if vars.get('sec_ver') is not None else 0,
|
|
1346
|
+
'sec2_username': vars.get('sec2_username', 'wifiprov') or 'wifiprov',
|
|
1330
1347
|
}
|
|
1331
1348
|
|
|
1332
1349
|
log.info("Using simplified local control (security level 0)")
|
|
@@ -1567,6 +1584,7 @@ def set_params(vars=None):
|
|
|
1567
1584
|
'transport': vars.get('transport', 'http'),
|
|
1568
1585
|
'port': vars.get('port', 8080),
|
|
1569
1586
|
'sec_ver': vars.get('sec_ver') if vars.get('sec_ver') is not None else 0,
|
|
1587
|
+
'sec2_username': vars.get('sec2_username', 'wifiprov') or 'wifiprov',
|
|
1570
1588
|
'local_raw': vars.get('local_raw', False),
|
|
1571
1589
|
'device_name': vars.get('device_name', None)
|
|
1572
1590
|
}
|
|
@@ -1753,6 +1771,7 @@ def get_params(vars=None):
|
|
|
1753
1771
|
'transport': vars.get('transport', 'http'),
|
|
1754
1772
|
'port': vars.get('port', 8080),
|
|
1755
1773
|
'sec_ver': vars.get('sec_ver') if vars.get('sec_ver') is not None else 1,
|
|
1774
|
+
'sec2_username': vars.get('sec2_username', 'wifiprov') or 'wifiprov',
|
|
1756
1775
|
'local_raw': vars.get('local_raw', False),
|
|
1757
1776
|
'device_name': vars.get('device_name', None),
|
|
1758
1777
|
'timestamp': timestamp if not (proxy_report and vars.get('local_raw', False) and not timestamp_explicitly_provided) else timestamp,
|
|
@@ -1779,6 +1798,7 @@ def get_params(vars=None):
|
|
|
1779
1798
|
'transport': vars.get('transport', 'http'),
|
|
1780
1799
|
'port': vars.get('port', 8080),
|
|
1781
1800
|
'sec_ver': vars.get('sec_ver') if vars.get('sec_ver') is not None else 0,
|
|
1801
|
+
'sec2_username': vars.get('sec2_username', 'wifiprov') or 'wifiprov',
|
|
1782
1802
|
'local_raw': vars.get('local_raw', False),
|
|
1783
1803
|
'device_name': vars.get('device_name', None),
|
|
1784
1804
|
'timestamp': timestamp if not (proxy_report and vars.get('local_raw', False) and not timestamp_explicitly_provided) else timestamp,
|
|
@@ -117,8 +117,15 @@ def provision(vars=None):
|
|
|
117
117
|
raise ValueError("Proof of possession (pop) may be required depending on security scheme. "
|
|
118
118
|
"Use --pop, provide via --qrcode, or specify --sec_ver to skip pop requirement "
|
|
119
119
|
"(Security 0 and 2 don't require pop).")
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
|
|
121
|
+
# sec2 credentials: explicit options override QR code values.
|
|
122
|
+
# QR code fields: 'username' and 'password' (sec2 SRP6a credentials).
|
|
123
|
+
sec2_username = (vars.get('sec2_username')
|
|
124
|
+
or qrcode_data.get('username')
|
|
125
|
+
or '')
|
|
126
|
+
sec2_password = (vars.get('sec2_password')
|
|
127
|
+
or qrcode_data.get('password')
|
|
128
|
+
or '')
|
|
122
129
|
ssid = vars.get('ssid')
|
|
123
130
|
passphrase = vars.get('passphrase')
|
|
124
131
|
no_wifi = vars.get('no_wifi', False)
|
|
@@ -56,7 +56,7 @@ def _get_cache_base_dir(profile_config=None):
|
|
|
56
56
|
|
|
57
57
|
def extract_local_control_info(params_or_details):
|
|
58
58
|
"""
|
|
59
|
-
Extract local control info (POP, sec_ver, transport, port) from
|
|
59
|
+
Extract local control info (POP, sec_ver, username, transport, port) from
|
|
60
60
|
node params or node details response data.
|
|
61
61
|
|
|
62
62
|
Scans for a service/device named "Local Control" or similar that
|
|
@@ -74,6 +74,9 @@ def extract_local_control_info(params_or_details):
|
|
|
74
74
|
lower_key = key.lower().replace(' ', '_').replace('-', '_')
|
|
75
75
|
if lower_key in ('pop', 'proof_of_possession'):
|
|
76
76
|
info['pop'] = str(value)
|
|
77
|
+
elif lower_key in ('username', 'user_name', 'local_control_username'):
|
|
78
|
+
if value:
|
|
79
|
+
info['username'] = str(value)
|
|
77
80
|
elif lower_key == 'sec_ver':
|
|
78
81
|
try:
|
|
79
82
|
info['sec_ver'] = int(value)
|