esp-rainmaker-cli 1.12.0__tar.gz → 1.13.0__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.
Files changed (126) hide show
  1. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/PKG-INFO +18 -2
  2. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/esp_rainmaker_cli.egg-info/PKG-INFO +18 -2
  3. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/esp_rainmaker_cli.egg-info/SOURCES.txt +4 -0
  4. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/esp_rainmaker_cli.egg-info/requires.txt +6 -1
  5. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rainmaker/rainmaker.py +98 -1
  6. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rainmaker/version.py +1 -1
  7. esp_rainmaker_cli-1.13.0/rmaker_cmd/automation.py +217 -0
  8. esp_rainmaker_cli-1.13.0/rmaker_cmd/stream.py +203 -0
  9. esp_rainmaker_cli-1.13.0/rmaker_lib/aws_credentials.py +220 -0
  10. esp_rainmaker_cli-1.13.0/rmaker_lib/kvs_streaming.py +2014 -0
  11. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/session.py +79 -0
  12. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/setup.py +2 -0
  13. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/LICENSE +0 -0
  14. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/README.md +0 -0
  15. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/esp_rainmaker_cli.egg-info/dependency_links.txt +0 -0
  16. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/esp_rainmaker_cli.egg-info/entry_points.txt +0 -0
  17. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/esp_rainmaker_cli.egg-info/top_level.txt +0 -0
  18. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rainmaker/__init__.py +0 -0
  19. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_cmd/__init__.py +0 -0
  20. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_cmd/browserlogin.py +0 -0
  21. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_cmd/cache.py +0 -0
  22. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_cmd/cmd_response.py +0 -0
  23. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_cmd/group.py +0 -0
  24. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_cmd/html/welcome.html +0 -0
  25. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_cmd/node.py +0 -0
  26. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_cmd/provision.py +0 -0
  27. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_cmd/test.py +0 -0
  28. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_cmd/user.py +0 -0
  29. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/__init__.py +0 -0
  30. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/cmd_response.py +0 -0
  31. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/configmanager.py +0 -0
  32. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/constants.py +0 -0
  33. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/device.py +0 -0
  34. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/envval.py +0 -0
  35. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/exceptions.py +0 -0
  36. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/local_control.py +0 -0
  37. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/logger.py +0 -0
  38. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/node.py +0 -0
  39. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/node_cache.py +0 -0
  40. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/profile_manager.py +0 -0
  41. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/profile_utils.py +0 -0
  42. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/schedule_utils.py +0 -0
  43. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/serverconfig.py +0 -0
  44. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/service.py +0 -0
  45. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/session_store.py +0 -0
  46. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/simple_local_control.py +0 -0
  47. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_lib/user.py +0 -0
  48. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/__init__.py +0 -0
  49. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/__init__.py +0 -0
  50. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/discovery/__init__.py +0 -0
  51. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/discovery/mdns_discovery.py +0 -0
  52. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/prov/__init__.py +0 -0
  53. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/prov/wifi_ctrl.py +0 -0
  54. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/prov/wifi_prov.py +0 -0
  55. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/prov/wifi_scan.py +0 -0
  56. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/security/__init__.py +0 -0
  57. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/security/security.py +0 -0
  58. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/security/security0.py +0 -0
  59. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/security/security1.py +0 -0
  60. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/security/security2.py +0 -0
  61. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/security/srp6a.py +0 -0
  62. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/transport/__init__.py +0 -0
  63. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/transport/ble_cli.py +0 -0
  64. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/transport/transport.py +0 -0
  65. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/transport/transport_ble.py +0 -0
  66. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/transport/transport_console.py +0 -0
  67. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/transport/transport_http.py +0 -0
  68. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/utils/__init__.py +0 -0
  69. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/common/utils/convenience.py +0 -0
  70. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_claim/__init__.py +0 -0
  71. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_claim/claim.py +0 -0
  72. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_claim/claim_config.py +0 -0
  73. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/__init__.py +0 -0
  74. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/constants_pb2.py +0 -0
  75. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/esp_local_ctrl.py +0 -0
  76. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/esp_local_ctrl_pb2.py +0 -0
  77. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/esp_prov.py +0 -0
  78. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/esp_rainmaker_ctrl.py +0 -0
  79. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/esp_rmaker_prov_local_ctrl_pb2.py +0 -0
  80. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/integration.py +0 -0
  81. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/proto/__init__.py +0 -0
  82. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/proto/proto_lc.py +0 -0
  83. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/prov/__init__.py +0 -0
  84. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/prov/custom_prov.py +0 -0
  85. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/prov/wifi_ctrl.py +0 -0
  86. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/raw_config.py +0 -0
  87. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/raw_params.py +0 -0
  88. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/sec0_pb2.py +0 -0
  89. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/sec1_pb2.py +0 -0
  90. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/sec2_pb2.py +0 -0
  91. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/security/__init__.py +0 -0
  92. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/session_pb2.py +0 -0
  93. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/transport/__init__.py +0 -0
  94. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_local_ctrl/utils/__init__.py +0 -0
  95. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/__init__.py +0 -0
  96. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/challenge_response.py +0 -0
  97. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/config/__init__.py +0 -0
  98. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/config/custom_cloud_config_pb2.py +0 -0
  99. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/config/esp_rmaker_chal_resp_pb2.py +0 -0
  100. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/config/esp_rmaker_claim_pb2.py +0 -0
  101. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/config/esp_rmaker_user_mapping_pb2.py +0 -0
  102. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/esp_rainmaker_prov.py +0 -0
  103. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/on_network_chal_resp.py +0 -0
  104. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/proto/__init__.py +0 -0
  105. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/protocomm/__init__.py +0 -0
  106. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/protocomm/python/__init__.py +0 -0
  107. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/protocomm/python/constants_pb2.py +0 -0
  108. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/protocomm/python/sec0_pb2.py +0 -0
  109. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/protocomm/python/sec1_pb2.py +0 -0
  110. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/protocomm/python/sec2_pb2.py +0 -0
  111. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/protocomm/python/session_pb2.py +0 -0
  112. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/prov/__init__.py +0 -0
  113. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/prov/prov_util.py +0 -0
  114. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/prov/user_mapping.py +0 -0
  115. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/security/__init__.py +0 -0
  116. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/transport/__init__.py +0 -0
  117. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/utils/__init__.py +0 -0
  118. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/wifi_provisioning/__init__.py +0 -0
  119. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/wifi_provisioning/python/__init__.py +0 -0
  120. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/wifi_provisioning/python/wifi_config_pb2.py +0 -0
  121. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/wifi_provisioning/python/wifi_constants_pb2.py +0 -0
  122. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/wifi_provisioning/python/wifi_ctrl_pb2.py +0 -0
  123. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/rmaker_tools/rmaker_prov/wifi_provisioning/python/wifi_scan_pb2.py +0 -0
  124. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/server_cert/__init__.py +0 -0
  125. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/server_cert/server_cert.pem +0 -0
  126. {esp_rainmaker_cli-1.12.0 → esp_rainmaker_cli-1.13.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: esp-rainmaker-cli
3
- Version: 1.12.0
3
+ Version: 1.13.0
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
@@ -18,6 +18,8 @@ Classifier: Topic :: Software Development :: Embedded Systems
18
18
  Classifier: Programming Language :: Python :: 3.9
19
19
  Classifier: Programming Language :: Python :: 3.10
20
20
  Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
21
23
  Requires-Python: >=3.9
22
24
  Description-Content-Type: text/markdown
23
25
  License-File: LICENSE
@@ -32,11 +34,16 @@ Requires-Dist: future>=0.15.2
32
34
  Requires-Dist: pyparsing>=2.4.0
33
35
  Requires-Dist: pyelftools>=0.22
34
36
  Requires-Dist: esptool>=4.4
35
- Requires-Dist: esp-secure-cert-tool==1.0.1
37
+ Requires-Dist: esp-secure-cert-tool>=2.3.6
36
38
  Requires-Dist: setuptools
37
39
  Requires-Dist: esp_idf_nvs_partition_gen>=0.1.6
38
40
  Requires-Dist: bleak>=0.20.0
39
41
  Requires-Dist: zeroconf>=0.131.0
42
+ Requires-Dist: aiortc>=1.6.0
43
+ Requires-Dist: boto3>=1.28.0
44
+ Requires-Dist: websockets>=11.0
45
+ Requires-Dist: opencv-python>=4.8.0
46
+ Requires-Dist: numpy>=1.24.0
40
47
  Dynamic: author
41
48
  Dynamic: classifier
42
49
  Dynamic: description
@@ -115,6 +122,15 @@ Changelog
115
122
 
116
123
  All major changes to ESP RainMaker CLI will be documented in this file.
117
124
 
125
+ ## [1.13.0] - 08-Apr-2026
126
+ ### Added
127
+ - New `stream` command for WebRTC video streaming from ESP RainMaker camera devices via Amazon Kinesis Video Streams (KVS) signaling:
128
+ - Live video display with optional recording to file (`--output` / `-o`)
129
+ - Duration-limited streaming (`--duration`)
130
+ - Parallel ICE server fetch and WebSocket connection for faster stream setup
131
+ - Graceful handling of device disconnects, reboots, and frame timeouts
132
+ - Suppression of macOS FFmpeg duplicate library warnings during import
133
+
118
134
  ## [1.12.0] - 19-Feb-2026
119
135
  ### Added
120
136
  - Local node cache layer for faster `--local` control operations:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: esp-rainmaker-cli
3
- Version: 1.12.0
3
+ Version: 1.13.0
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
@@ -18,6 +18,8 @@ Classifier: Topic :: Software Development :: Embedded Systems
18
18
  Classifier: Programming Language :: Python :: 3.9
19
19
  Classifier: Programming Language :: Python :: 3.10
20
20
  Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
21
23
  Requires-Python: >=3.9
22
24
  Description-Content-Type: text/markdown
23
25
  License-File: LICENSE
@@ -32,11 +34,16 @@ Requires-Dist: future>=0.15.2
32
34
  Requires-Dist: pyparsing>=2.4.0
33
35
  Requires-Dist: pyelftools>=0.22
34
36
  Requires-Dist: esptool>=4.4
35
- Requires-Dist: esp-secure-cert-tool==1.0.1
37
+ Requires-Dist: esp-secure-cert-tool>=2.3.6
36
38
  Requires-Dist: setuptools
37
39
  Requires-Dist: esp_idf_nvs_partition_gen>=0.1.6
38
40
  Requires-Dist: bleak>=0.20.0
39
41
  Requires-Dist: zeroconf>=0.131.0
42
+ Requires-Dist: aiortc>=1.6.0
43
+ Requires-Dist: boto3>=1.28.0
44
+ Requires-Dist: websockets>=11.0
45
+ Requires-Dist: opencv-python>=4.8.0
46
+ Requires-Dist: numpy>=1.24.0
40
47
  Dynamic: author
41
48
  Dynamic: classifier
42
49
  Dynamic: description
@@ -115,6 +122,15 @@ Changelog
115
122
 
116
123
  All major changes to ESP RainMaker CLI will be documented in this file.
117
124
 
125
+ ## [1.13.0] - 08-Apr-2026
126
+ ### Added
127
+ - New `stream` command for WebRTC video streaming from ESP RainMaker camera devices via Amazon Kinesis Video Streams (KVS) signaling:
128
+ - Live video display with optional recording to file (`--output` / `-o`)
129
+ - Duration-limited streaming (`--duration`)
130
+ - Parallel ICE server fetch and WebSocket connection for faster stream setup
131
+ - Graceful handling of device disconnects, reboots, and frame timeouts
132
+ - Suppression of macOS FFmpeg duplicate library warnings during import
133
+
118
134
  ## [1.12.0] - 19-Feb-2026
119
135
  ### Added
120
136
  - Local node cache layer for faster `--local` control operations:
@@ -11,22 +11,26 @@ rainmaker/__init__.py
11
11
  rainmaker/rainmaker.py
12
12
  rainmaker/version.py
13
13
  rmaker_cmd/__init__.py
14
+ rmaker_cmd/automation.py
14
15
  rmaker_cmd/browserlogin.py
15
16
  rmaker_cmd/cache.py
16
17
  rmaker_cmd/cmd_response.py
17
18
  rmaker_cmd/group.py
18
19
  rmaker_cmd/node.py
19
20
  rmaker_cmd/provision.py
21
+ rmaker_cmd/stream.py
20
22
  rmaker_cmd/test.py
21
23
  rmaker_cmd/user.py
22
24
  rmaker_cmd/html/welcome.html
23
25
  rmaker_lib/__init__.py
26
+ rmaker_lib/aws_credentials.py
24
27
  rmaker_lib/cmd_response.py
25
28
  rmaker_lib/configmanager.py
26
29
  rmaker_lib/constants.py
27
30
  rmaker_lib/device.py
28
31
  rmaker_lib/envval.py
29
32
  rmaker_lib/exceptions.py
33
+ rmaker_lib/kvs_streaming.py
30
34
  rmaker_lib/local_control.py
31
35
  rmaker_lib/logger.py
32
36
  rmaker_lib/node.py
@@ -9,8 +9,13 @@ future>=0.15.2
9
9
  pyparsing>=2.4.0
10
10
  pyelftools>=0.22
11
11
  esptool>=4.4
12
- esp-secure-cert-tool==1.0.1
12
+ esp-secure-cert-tool>=2.3.6
13
13
  setuptools
14
14
  esp_idf_nvs_partition_gen>=0.1.6
15
15
  bleak>=0.20.0
16
16
  zeroconf>=0.131.0
17
+ aiortc>=1.6.0
18
+ boto3>=1.28.0
19
+ websockets>=11.0
20
+ opencv-python>=4.8.0
21
+ numpy>=1.24.0
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  #
3
- # SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
3
+ # SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
4
4
  #
5
5
  # SPDX-License-Identifier: Apache-2.0
6
6
 
@@ -16,7 +16,9 @@ from rmaker_cmd.provision import provision
16
16
  from rmaker_cmd.test import test
17
17
  from rmaker_lib.logger import log
18
18
  from rmaker_cmd.group import group_add, group_remove, group_edit, group_list, group_show, group_add_nodes, group_remove_nodes, group_list_nodes
19
+ from rmaker_cmd.automation import automation_add, automation_edit, automation_remove, automation_get
19
20
  from rmaker_cmd.cache import cache_manage
21
+ from rmaker_cmd.stream import stream_video
20
22
 
21
23
  # Import the version
22
24
  from rainmaker.version import VERSION
@@ -909,6 +911,76 @@ def main():
909
911
  add_profile_argument(group_list_nodes_parser)
910
912
  group_list_nodes_parser.set_defaults(func=group_list_nodes)
911
913
 
914
+ # Automation Management
915
+ automation_parser = subparsers.add_parser('automations',
916
+ help='Manage automation triggers')
917
+ automation_parser.set_defaults(func=lambda vars=None: automation_parser.print_help())
918
+ automation_subparsers = automation_parser.add_subparsers(dest='automation_command',
919
+ help='Automation operations')
920
+
921
+ # automations add
922
+ auto_add_parser = automation_subparsers.add_parser('add', help='Create a new automation trigger')
923
+ auto_add_parser.add_argument('--name', type=str, required=True, help='Name of the automation')
924
+ auto_add_parser.add_argument('--event-type', type=str, required=True,
925
+ choices=['params', 'weather', 'daylight'],
926
+ help='Event type (params, weather, daylight)')
927
+ auto_add_parser.add_argument('--event', type=str, required=True, action='append',
928
+ help='Event JSON object (repeatable). '
929
+ 'E.g., \'{"params": {"Light": {"Brightness": 100}}, "check": "=="}\'')
930
+ auto_add_parser.add_argument('--action', type=str, required=True,
931
+ help='Action JSON (object or array of objects). '
932
+ 'E.g., \'{"node_id": "xxx", "params": {"Light": {"Output": true}}}\'')
933
+ auto_add_parser.add_argument('--node-id', type=str,
934
+ help='Node ID (required for event-type params)')
935
+ auto_add_parser.add_argument('--location', type=str,
936
+ help='Location as lat,long (required for event-type weather/daylight). '
937
+ 'E.g., 18.521428,73.8544541')
938
+ auto_add_parser.add_argument('--event-operator', type=str, choices=['AND', 'OR'],
939
+ help='Logical operator for multiple events (AND/OR)')
940
+ auto_add_parser.add_argument('--retrigger', action='store_true', default=False,
941
+ help='Allow retriggering for the same event')
942
+ auto_add_parser.add_argument('--metadata', type=str, help='Metadata as JSON string')
943
+ add_profile_argument(auto_add_parser)
944
+ auto_add_parser.set_defaults(func=automation_add)
945
+
946
+ # automations edit
947
+ auto_edit_parser = automation_subparsers.add_parser('edit', help='Edit an existing automation trigger')
948
+ auto_edit_parser.add_argument('--id', type=str, required=True, help='Automation ID')
949
+ auto_edit_parser.add_argument('--name', type=str, help='New name for the automation')
950
+ auto_edit_parser.add_argument('--event', type=str, action='append',
951
+ help='Event JSON object (repeatable)')
952
+ auto_edit_parser.add_argument('--action', type=str,
953
+ help='Action JSON (object or array of objects)')
954
+ auto_edit_parser.add_argument('--node-id', type=str, help='Node ID')
955
+ auto_edit_parser.add_argument('--location', type=str,
956
+ help='Location as lat,long')
957
+ auto_edit_parser.add_argument('--event-operator', type=str, choices=['AND', 'OR'],
958
+ help='Logical operator for multiple events (AND/OR)')
959
+ auto_edit_parser.add_argument('--retrigger', action='store_true', default=False,
960
+ help='Enable retriggering')
961
+ auto_edit_parser.add_argument('--no-retrigger', action='store_true', default=False,
962
+ help='Disable retriggering')
963
+ auto_edit_parser.add_argument('--enabled', action='store_true', default=False,
964
+ help='Enable the automation')
965
+ auto_edit_parser.add_argument('--disabled', action='store_true', default=False,
966
+ help='Disable the automation')
967
+ auto_edit_parser.add_argument('--metadata', type=str, help='Metadata as JSON string')
968
+ add_profile_argument(auto_edit_parser)
969
+ auto_edit_parser.set_defaults(func=automation_edit)
970
+
971
+ # automations remove
972
+ auto_remove_parser = automation_subparsers.add_parser('remove', help='Remove an automation trigger')
973
+ auto_remove_parser.add_argument('--id', type=str, required=True, help='Automation ID to remove')
974
+ add_profile_argument(auto_remove_parser)
975
+ auto_remove_parser.set_defaults(func=automation_remove)
976
+
977
+ # automations get
978
+ auto_get_parser = automation_subparsers.add_parser('get', help='Get automation triggers')
979
+ auto_get_parser.add_argument('--id', type=str, help='Automation ID (omit to list all)')
980
+ auto_get_parser.add_argument('--node-id', type=str, help='Filter by node ID')
981
+ add_profile_argument(auto_get_parser)
982
+ auto_get_parser.set_defaults(func=automation_get)
983
+
912
984
  # Node Management (tags, metadata)
913
985
  node_parser = subparsers.add_parser('node',
914
986
  help='Manage node properties (tags, metadata)')
@@ -1018,6 +1090,31 @@ def main():
1018
1090
  add_profile_argument(raw_api_parser)
1019
1091
  raw_api_parser.set_defaults(func=raw_api_call)
1020
1092
 
1093
+ # Video Streaming
1094
+ stream_parser = subparsers.add_parser('stream',
1095
+ help='Stream video from a camera device')
1096
+ stream_parser.add_argument('nodeid',
1097
+ type=str,
1098
+ metavar='<nodeid>',
1099
+ help='Node ID for the camera device')
1100
+ stream_parser.add_argument('--output', '-o',
1101
+ type=str,
1102
+ metavar='<path>',
1103
+ help='Save video to file (MP4)')
1104
+ stream_parser.add_argument('--region',
1105
+ type=str,
1106
+ metavar='<region>',
1107
+ help='AWS region override')
1108
+ stream_parser.add_argument('--duration',
1109
+ type=int,
1110
+ metavar='<seconds>',
1111
+ help='Stream duration in seconds')
1112
+ stream_parser.add_argument('--stats-only',
1113
+ action='store_true',
1114
+ help='Show statistics only, no video display')
1115
+ add_profile_argument(stream_parser)
1116
+ stream_parser.set_defaults(func=stream_video)
1117
+
1021
1118
  args = parser.parse_args()
1022
1119
 
1023
1120
  if args.func is not None:
@@ -5,4 +5,4 @@
5
5
  # SPDX-License-Identifier: Apache-2.0
6
6
 
7
7
  # This file contains the version information for the ESP RainMaker CLI
8
- VERSION = "1.12.0"
8
+ VERSION = "1.13.0"
@@ -0,0 +1,217 @@
1
+ # SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ from rmaker_lib.profile_utils import get_session_with_profile
6
+ import json
7
+
8
+
9
+ def _handle_error(e, operation):
10
+ """Helper function to handle errors and extract descriptions from API responses"""
11
+ error_msg = str(e)
12
+ try:
13
+ if hasattr(e, 'response') and e.response is not None:
14
+ resp = e.response.json()
15
+ if 'description' in resp:
16
+ error_msg = resp['description']
17
+ except Exception:
18
+ pass
19
+ print(f"Failed to {operation}: {error_msg}")
20
+
21
+
22
+ def _parse_json(value, field_name):
23
+ """Parse a JSON string, returning the parsed object or None on error."""
24
+ try:
25
+ return json.loads(value)
26
+ except Exception as e:
27
+ print(f"Invalid JSON for {field_name}: {e}")
28
+ return None
29
+
30
+
31
+ def _parse_location(location_str):
32
+ """Parse 'lat,long' string into location dict."""
33
+ parts = location_str.split(',')
34
+ if len(parts) != 2:
35
+ print("Invalid location format. Use: lat,long (e.g., 18.521428,73.8544541)")
36
+ return None
37
+ return {"latitude": parts[0].strip(), "longitude": parts[1].strip()}
38
+
39
+
40
+ def automation_add(vars=None):
41
+ try:
42
+ s = get_session_with_profile(vars or {})
43
+ name = vars['name']
44
+ event_type = vars['event_type']
45
+ event_jsons = vars['event']
46
+ action_json = vars['action']
47
+ node_id = vars.get('node_id')
48
+ location = vars.get('location')
49
+ event_operator = vars.get('event_operator')
50
+ retrigger = vars.get('retrigger', False)
51
+ metadata_arg = vars.get('metadata')
52
+
53
+ # Map 'params' to 'node_params' for the API
54
+ api_event_type = 'node_params' if event_type == 'params' else event_type
55
+
56
+ # Validate event_type-specific requirements
57
+ if event_type == 'params' and not node_id:
58
+ print("Error: --node-id is required when --event-type is 'params'")
59
+ return
60
+ if event_type in ('weather', 'daylight') and not location:
61
+ print(f"Error: --location is required when --event-type is '{event_type}'")
62
+ return
63
+
64
+ # Parse events
65
+ events = []
66
+ for ev in event_jsons:
67
+ parsed = _parse_json(ev, 'event')
68
+ if parsed is None:
69
+ return
70
+ events.append(parsed)
71
+
72
+ # Validate event_operator if multiple events
73
+ if len(events) > 1 and not event_operator:
74
+ print("Error: --event-operator (AND/OR) is required when multiple events are specified")
75
+ return
76
+
77
+ # Parse action
78
+ action = _parse_json(action_json, 'action')
79
+ if action is None:
80
+ return
81
+ if isinstance(action, dict):
82
+ actions = [action]
83
+ elif isinstance(action, list):
84
+ actions = action
85
+ else:
86
+ print("Error: --action must be a JSON object or array of objects")
87
+ return
88
+
89
+ # Build payload
90
+ payload = {
91
+ 'name': name,
92
+ 'event_type': api_event_type,
93
+ 'events': events,
94
+ 'actions': actions,
95
+ 'retrigger': retrigger,
96
+ }
97
+ if node_id:
98
+ payload['node_id'] = node_id
99
+ if location:
100
+ loc = _parse_location(location)
101
+ if loc is None:
102
+ return
103
+ payload['location'] = loc
104
+ if event_operator:
105
+ payload['event_operator'] = event_operator.lower()
106
+ if metadata_arg:
107
+ metadata = _parse_json(metadata_arg, 'metadata')
108
+ if metadata is None:
109
+ return
110
+ payload['metadata'] = metadata
111
+
112
+ resp = s.create_automation(payload)
113
+ print("Automation created successfully:")
114
+ print(json.dumps(resp, indent=2))
115
+ except Exception as e:
116
+ _handle_error(e, "create automation")
117
+
118
+
119
+ def automation_edit(vars=None):
120
+ try:
121
+ s = get_session_with_profile(vars or {})
122
+ automation_id = vars['id']
123
+
124
+ payload = {}
125
+ if vars.get('name'):
126
+ payload['name'] = vars['name']
127
+ if vars.get('node_id'):
128
+ payload['node_id'] = vars['node_id']
129
+ if vars.get('event'):
130
+ events = []
131
+ for ev in vars['event']:
132
+ parsed = _parse_json(ev, 'event')
133
+ if parsed is None:
134
+ return
135
+ events.append(parsed)
136
+ payload['events'] = events
137
+ if vars.get('action'):
138
+ action = _parse_json(vars['action'], 'action')
139
+ if action is None:
140
+ return
141
+ if isinstance(action, dict):
142
+ payload['actions'] = [action]
143
+ elif isinstance(action, list):
144
+ payload['actions'] = action
145
+ else:
146
+ print("Error: --action must be a JSON object or array of objects")
147
+ return
148
+ if vars.get('event_operator'):
149
+ payload['event_operator'] = vars['event_operator'].lower()
150
+ if vars.get('location'):
151
+ loc = _parse_location(vars['location'])
152
+ if loc is None:
153
+ return
154
+ payload['location'] = loc
155
+ if vars.get('metadata'):
156
+ metadata = _parse_json(vars['metadata'], 'metadata')
157
+ if metadata is None:
158
+ return
159
+ payload['metadata'] = metadata
160
+
161
+ # Handle retrigger/no-retrigger
162
+ if vars.get('retrigger'):
163
+ payload['retrigger'] = True
164
+ elif vars.get('no_retrigger'):
165
+ payload['retrigger'] = False
166
+
167
+ # Handle enabled/disabled
168
+ if vars.get('enabled'):
169
+ payload['enabled'] = True
170
+ elif vars.get('disabled'):
171
+ payload['enabled'] = False
172
+
173
+ if not payload:
174
+ print("No fields to update. Provide at least one option to edit.")
175
+ return
176
+
177
+ resp = s.update_automation(automation_id, payload)
178
+ print("Automation updated successfully:")
179
+ print(json.dumps(resp, indent=2))
180
+ except Exception as e:
181
+ _handle_error(e, "update automation")
182
+
183
+
184
+ def automation_remove(vars=None):
185
+ try:
186
+ s = get_session_with_profile(vars or {})
187
+ automation_id = vars['id']
188
+ resp = s.remove_automation(automation_id)
189
+ print("Automation removed successfully:")
190
+ print(json.dumps(resp, indent=2))
191
+ except Exception as e:
192
+ _handle_error(e, "remove automation")
193
+
194
+
195
+ def automation_get(vars=None):
196
+ try:
197
+ s = get_session_with_profile(vars or {})
198
+ automation_id = vars.get('id')
199
+ node_id = vars.get('node_id')
200
+ resp = s.get_automations(automation_id=automation_id, node_id=node_id)
201
+
202
+ if not resp:
203
+ print("No automations found.")
204
+ return
205
+
206
+ automations = resp.get('automation_trigger_actions', [])
207
+ if not automations:
208
+ print("No automations found.")
209
+ return
210
+
211
+ if automation_id:
212
+ print("Automation details:")
213
+ else:
214
+ print(f"Automations ({len(automations)}):")
215
+ print(json.dumps(automations, indent=2))
216
+ except Exception as e:
217
+ _handle_error(e, "get automations")