remoteRF-server 1.7.16__tar.gz → 1.7.18__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 (223) hide show
  1. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/PKG-INFO +16 -2
  2. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/pyproject.toml +12 -2
  3. remoterf_server-1.7.18/src/remoteRF_server/drivers/bluetooth/bluetooth_schema.py +20 -0
  4. remoterf_server-1.7.18/src/remoteRF_server/drivers/gps/gps_schema.py +20 -0
  5. remoterf_server-1.7.18/src/remoteRF_server/drivers/krakensdr/krakensdr_schema.py +20 -0
  6. remoterf_server-1.7.18/src/remoteRF_server/drivers/rfbeam_kmd7/rfbeam_kmd7_schema.py +20 -0
  7. remoterf_server-1.7.18/src/remoteRF_server/drivers/ti_awr6843/__init__.py +14 -0
  8. remoterf_server-1.7.18/src/remoteRF_server/drivers/ti_awr6843/ti_awr6843_schema.py +20 -0
  9. remoterf_server-1.7.18/src/remoteRF_server/drivers/usrp/__init__.py +14 -0
  10. remoterf_server-1.7.18/src/remoteRF_server/drivers/usrp_phaser/__init__.py +14 -0
  11. remoterf_server-1.7.18/src/remoteRF_server/drivers/wifi/__init__.py +14 -0
  12. remoterf_server-1.7.18/src/remoteRF_server/drivers/wifi/wifi_schema.py +20 -0
  13. remoterf_server-1.7.18/src/remoteRF_server/host/__init__.py +14 -0
  14. remoterf_server-1.7.18/src/remoteRF_server/server/__init__.py +14 -0
  15. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/commands.py +2 -2
  16. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/device_manager.py +27 -9
  17. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/direct_path.py +13 -2
  18. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/global_onboarding.py +50 -4
  19. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/grpc_server.py +13 -2
  20. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/reservation.py +2 -0
  21. remoterf_server-1.7.18/src/remoteRF_server/server/transport_counters.py +138 -0
  22. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/serverrf_cli.py +222 -4
  23. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server.egg-info/PKG-INFO +16 -2
  24. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server.egg-info/SOURCES.txt +13 -0
  25. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server.egg-info/requires.txt +22 -1
  26. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_commands.py +108 -0
  27. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_default_wifi_schema.py +52 -11
  28. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_global_onboarding.py +76 -12
  29. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_json_commands.py +3 -1
  30. remoterf_server-1.7.18/tests/test_native_drivers.py +165 -0
  31. remoterf_server-1.7.18/tests/test_transport_counters.py +160 -0
  32. remoterf_server-1.7.16/src/remoteRF_server/drivers/wifi/__init__.py +0 -6
  33. remoterf_server-1.7.16/src/remoteRF_server/drivers/wifi/wifi_schema.py +0 -720
  34. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/LICENSE +0 -0
  35. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/README.md +0 -0
  36. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/setup.cfg +0 -0
  37. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/__init__.py +0 -0
  38. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/__init__.py +0 -0
  39. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/grpc/__init__.py +0 -0
  40. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/grpc/deployment_capabilities.proto +0 -0
  41. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/grpc/deployment_capabilities_pb2.py +0 -0
  42. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/grpc/deployment_capabilities_pb2_grpc.py +0 -0
  43. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/grpc/federation.proto +0 -0
  44. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/grpc/federation_pb2.py +0 -0
  45. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/grpc/federation_pb2_grpc.py +0 -0
  46. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
  47. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
  48. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
  49. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
  50. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/grpc/v2_codec.py +0 -0
  51. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/idl/__init__.py +0 -0
  52. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/idl/device_schema.py +0 -0
  53. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/idl/pluto_mimo_schema.py +0 -0
  54. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/idl/pluto_schema.py +0 -0
  55. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/idl/schema.py +0 -0
  56. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/idl/v2.py +0 -0
  57. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/utils/__init__.py +0 -0
  58. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
  59. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/utils/api_token.py +0 -0
  60. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/utils/db_connection.py +0 -0
  61. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/utils/db_location.py +0 -0
  62. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/utils/list_string.py +0 -0
  63. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/utils/panel.py +0 -0
  64. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/common/utils/process_arg.py +0 -0
  65. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/__init__.py +0 -0
  66. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
  67. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/adalm_pluto/pluto_diag.py +0 -0
  68. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/adalm_pluto/pluto_mimo_schema.py +0 -0
  69. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
  70. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/adalm_pluto/pluto_schema.py +0 -0
  71. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/adalm_pluto/poe.py +0 -0
  72. {remoterf_server-1.7.16/src/remoteRF_server/drivers/usrp → remoterf_server-1.7.18/src/remoteRF_server/drivers/bluetooth}/__init__.py +0 -0
  73. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/bluetooth_diag.py +0 -0
  74. {remoterf_server-1.7.16/src/remoteRF_server/drivers/usrp_phaser → remoterf_server-1.7.18/src/remoteRF_server/drivers/gps}/__init__.py +0 -0
  75. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/gps_diag.py +0 -0
  76. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/hackrf/__init__.py +0 -0
  77. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/hackrf/hackrf_schema.py +0 -0
  78. {remoterf_server-1.7.16/src/remoteRF_server/host → remoterf_server-1.7.18/src/remoteRF_server/drivers/krakensdr}/__init__.py +0 -0
  79. {remoterf_server-1.7.16/src/remoteRF_server/server → remoterf_server-1.7.18/src/remoteRF_server/drivers/rfbeam_kmd7}/__init__.py +0 -0
  80. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/rtl_sdr/__init__.py +0 -0
  81. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/rtl_sdr/rtl_sdr_diag.py +0 -0
  82. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/rtl_sdr/rtl_sdr_schema.py +0 -0
  83. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/ti_mmwave/__init__.py +0 -0
  84. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/ti_mmwave/ti_mmwave_schema.py +0 -0
  85. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/usrp/policy.py +0 -0
  86. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/usrp/profiles.py +0 -0
  87. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/usrp/uhd_manifest.py +0 -0
  88. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/usrp/usrp_diag.py +0 -0
  89. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/usrp/usrp_schema.py +0 -0
  90. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/drivers/usrp_phaser/usrp_phaser_schema.py +0 -0
  91. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/host/host_auth_token.py +0 -0
  92. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/host/host_directory_store.py +0 -0
  93. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/host/host_tunnel_server.py +0 -0
  94. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/remote_admin_shell.py +0 -0
  95. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/acc_perms.py +0 -0
  96. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/cert_provider.py +0 -0
  97. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/console.py +0 -0
  98. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/control.py +0 -0
  99. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/deployment/__init__.py +0 -0
  100. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/deployment/capabilities.py +0 -0
  101. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/diag.py +0 -0
  102. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/dynamic_v2.py +0 -0
  103. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/dynamic_v2_bridge.py +0 -0
  104. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/__init__.py +0 -0
  105. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/admin.py +0 -0
  106. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/cli.py +0 -0
  107. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/cluster.py +0 -0
  108. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/contracts.py +0 -0
  109. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/destination.py +0 -0
  110. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/devices.py +0 -0
  111. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/enrollment.py +0 -0
  112. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/global_home.py +0 -0
  113. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/home.py +0 -0
  114. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/home_devices.py +0 -0
  115. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/identity.py +0 -0
  116. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/legacy_state.py +0 -0
  117. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/links.py +0 -0
  118. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/migrations.py +0 -0
  119. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/peers.py +0 -0
  120. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/permissions.py +0 -0
  121. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/principals.py +0 -0
  122. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/runtime.py +0 -0
  123. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/federation/trust.py +0 -0
  124. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/host_direct.py +0 -0
  125. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/host_dynamic_v2.py +0 -0
  126. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/json_output.py +0 -0
  127. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/remote_admin.py +0 -0
  128. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/remote_admin_ssh.py +0 -0
  129. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/rpc_manager.py +0 -0
  130. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/schema_registry.py +0 -0
  131. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/startup_check.py +0 -0
  132. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/user_group_cli.py +0 -0
  133. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/user_group_handler.py +0 -0
  134. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/server/v2_errors.py +0 -0
  135. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/tools/__init__.py +0 -0
  136. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/tools/gen_certs.py +0 -0
  137. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server/tools/introspect_uhd.py +0 -0
  138. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server.egg-info/dependency_links.txt +0 -0
  139. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server.egg-info/entry_points.txt +0 -0
  140. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/remoteRF_server.egg-info/top_level.txt +0 -0
  141. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/rssi/__init__.py +0 -0
  142. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/rssi/cli.py +0 -0
  143. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/rssi/dongle.py +0 -0
  144. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/rssi/safety.py +0 -0
  145. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/src/rssi/scanner.py +0 -0
  146. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_account_hardening.py +0 -0
  147. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_account_names.py +0 -0
  148. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_account_removal.py +0 -0
  149. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_activity_log.py +0 -0
  150. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_availability.py +0 -0
  151. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_bind_host.py +0 -0
  152. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_cert_provider.py +0 -0
  153. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_client_sessions.py +0 -0
  154. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_cluster.py +0 -0
  155. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_console.py +0 -0
  156. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_contrib_drivers.py +0 -0
  157. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_default_rtl_sdr_schema.py +0 -0
  158. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_default_usrp_schema.py +0 -0
  159. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_deployment_capabilities.py +0 -0
  160. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_detached_device_rpc.py +0 -0
  161. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_device_core_aliases.py +0 -0
  162. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_device_handles.py +0 -0
  163. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_device_init.py +0 -0
  164. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_devices_add_wizard.py +0 -0
  165. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_devices_list.py +0 -0
  166. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_diag.py +0 -0
  167. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_direct_fixed_port.py +0 -0
  168. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_direct_path_server.py +0 -0
  169. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_dynamic_v2.py +0 -0
  170. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_federated_direct_path.py +0 -0
  171. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_federated_dynamic_v2.py +0 -0
  172. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_federation_edges.py +0 -0
  173. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_first_run.py +0 -0
  174. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_gate_d_contracts.py +0 -0
  175. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_gate_e_federation.py +0 -0
  176. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_gate_e_proto.py +0 -0
  177. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_gate_f_enrollment.py +0 -0
  178. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_gate_g1_logging.py +0 -0
  179. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_gate_g_permissions.py +0 -0
  180. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_gate_h_devices.py +0 -0
  181. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_gate_i_direct_path.py +0 -0
  182. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_gen_certs.py +0 -0
  183. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_global_accounts_migration.py +0 -0
  184. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_global_home.py +0 -0
  185. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_global_v1_disposal.py +0 -0
  186. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_hackrf_schema.py +0 -0
  187. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_home_device_plane.py +0 -0
  188. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_host_direct.py +0 -0
  189. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_host_directory_store.py +0 -0
  190. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_host_dynamic_v2.py +0 -0
  191. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_idl_schema.py +0 -0
  192. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_lab_logins.py +0 -0
  193. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_panel.py +0 -0
  194. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_peer_transport.py +0 -0
  195. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_pluto_compat.py +0 -0
  196. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_pluto_poe.py +0 -0
  197. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_process_arg.py +0 -0
  198. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_proxy_lifecycle.py +0 -0
  199. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_remote_admin.py +0 -0
  200. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_reservation_cancel.py +0 -0
  201. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_reservation_handoff.py +0 -0
  202. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_reservation_policy_b.py +0 -0
  203. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_reservation_timestamps.py +0 -0
  204. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_rpc_manager_batch.py +0 -0
  205. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_rpc_manager_calln.py +0 -0
  206. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_rpc_manager_host_legacy.py +0 -0
  207. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_rpc_manager_rx_n.py +0 -0
  208. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_rtl_sdr_schema.py +0 -0
  209. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_schema_registry.py +0 -0
  210. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_soak_federation.py +0 -0
  211. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_startup_check.py +0 -0
  212. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_ti_mmwave_hardware.py +0 -0
  213. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_ti_mmwave_schema.py +0 -0
  214. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_user_group_cli.py +0 -0
  215. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_usrp_b205mini_uhd410_hardware.py +0 -0
  216. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_usrp_client_server_hardware.py +0 -0
  217. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_usrp_client_server_localhost.py +0 -0
  218. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_usrp_n210_client_server_hardware.py +0 -0
  219. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_usrp_preload.py +0 -0
  220. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_usrp_profiles.py +0 -0
  221. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_usrp_remoterf_rx_hardware.py +0 -0
  222. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_usrp_uhd410_hardware.py +0 -0
  223. {remoterf_server-1.7.16 → remoterf_server-1.7.18}/tests/test_wipe.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoteRF-server
3
- Version: 1.7.16
3
+ Version: 1.7.18
4
4
  Summary: RemoteRF server-side control package
5
5
  License: GPL-3.0-or-later
6
6
  Classifier: Programming Language :: Python :: 3
@@ -10,7 +10,7 @@ Requires-Python: >=3.10
10
10
  Description-Content-Type: text/markdown
11
11
  License-File: LICENSE
12
12
  Requires-Dist: remoterf-federation-core==0.22.0
13
- Requires-Dist: remoterf-device-core==0.1.3
13
+ Requires-Dist: remoterf-device-core==0.1.4
14
14
  Requires-Dist: grpcio<2.0.0,>=1.81.1
15
15
  Requires-Dist: protobuf<7.0.0,>=6.33.5
16
16
  Requires-Dist: numpy
@@ -29,6 +29,20 @@ Requires-Dist: cryptography<44,>=42
29
29
  Requires-Dist: aioice<0.11,>=0.10
30
30
  Requires-Dist: aioquic<2.0,>=1.2
31
31
  Requires-Dist: paramiko<6,>=3.4
32
+ Provides-Extra: bluetooth
33
+ Requires-Dist: remoterf-device-core[bluetooth]; extra == "bluetooth"
34
+ Provides-Extra: gps
35
+ Requires-Dist: remoterf-device-core[gps]; extra == "gps"
36
+ Provides-Extra: krakensdr
37
+ Requires-Dist: remoterf-device-core[krakensdr]; extra == "krakensdr"
38
+ Provides-Extra: rfbeam-kmd7
39
+ Requires-Dist: remoterf-device-core[rfbeam_kmd7]; extra == "rfbeam-kmd7"
40
+ Provides-Extra: ti-awr6843
41
+ Requires-Dist: remoterf-device-core[ti_awr6843]; extra == "ti-awr6843"
42
+ Provides-Extra: wifi
43
+ Requires-Dist: remoterf-device-core[wifi]; extra == "wifi"
44
+ Provides-Extra: drivers
45
+ Requires-Dist: remoterf-device-core[drivers]; extra == "drivers"
32
46
  Dynamic: license-file
33
47
 
34
48
  # Remote RF Server Guide (Linux)
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "remoteRF-server"
7
- version = "1.7.16"
7
+ version = "1.7.18"
8
8
  description = "RemoteRF server-side control package"
9
9
  license = { text = "GPL-3.0-or-later" }
10
10
  requires-python = ">=3.10"
11
11
  dependencies = [
12
12
  "remoterf-federation-core==0.22.0",
13
- "remoterf-device-core==0.1.3",
13
+ "remoterf-device-core==0.1.4",
14
14
  "grpcio>=1.81.1,<2.0.0",
15
15
  "protobuf>=6.33.5,<7.0.0",
16
16
  "numpy",
@@ -37,6 +37,16 @@ classifiers = [
37
37
  "Operating System :: OS Independent",
38
38
  ]
39
39
 
40
+ [project.optional-dependencies]
41
+ # Libraries for the built-in drivers that are not in the base install (see remoterf-device-core).
42
+ bluetooth = ["remoterf-device-core[bluetooth]"]
43
+ gps = ["remoterf-device-core[gps]"]
44
+ krakensdr = ["remoterf-device-core[krakensdr]"]
45
+ rfbeam_kmd7 = ["remoterf-device-core[rfbeam_kmd7]"]
46
+ ti_awr6843 = ["remoterf-device-core[ti_awr6843]"]
47
+ wifi = ["remoterf-device-core[wifi]"]
48
+ drivers = ["remoterf-device-core[drivers]"]
49
+
40
50
  [project.scripts]
41
51
  # RRRFserver = "remoteRF_server.server.grpc_server:main"
42
52
  serverrf = "remoteRF_server.serverrf_cli:main"
@@ -0,0 +1,20 @@
1
+ # Copyright (C) 2026 RemoteRF
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ """Lives in remoterf-device-core (remoterf_device_core.drivers.bluetooth.bluetooth_schema); this name stays importable."""
17
+ import importlib
18
+ import sys
19
+
20
+ sys.modules[__name__] = importlib.import_module("remoterf_device_core.drivers.bluetooth.bluetooth_schema")
@@ -0,0 +1,20 @@
1
+ # Copyright (C) 2026 RemoteRF
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ """Lives in remoterf-device-core (remoterf_device_core.drivers.gps.gps_schema); this name stays importable."""
17
+ import importlib
18
+ import sys
19
+
20
+ sys.modules[__name__] = importlib.import_module("remoterf_device_core.drivers.gps.gps_schema")
@@ -0,0 +1,20 @@
1
+ # Copyright (C) 2026 RemoteRF
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ """Lives in remoterf-device-core (remoterf_device_core.drivers.krakensdr.krakensdr_schema); this name stays importable."""
17
+ import importlib
18
+ import sys
19
+
20
+ sys.modules[__name__] = importlib.import_module("remoterf_device_core.drivers.krakensdr.krakensdr_schema")
@@ -0,0 +1,20 @@
1
+ # Copyright (C) 2026 RemoteRF
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ """Lives in remoterf-device-core (remoterf_device_core.drivers.rfbeam_kmd7.rfbeam_kmd7_schema); this name stays importable."""
17
+ import importlib
18
+ import sys
19
+
20
+ sys.modules[__name__] = importlib.import_module("remoterf_device_core.drivers.rfbeam_kmd7.rfbeam_kmd7_schema")
@@ -0,0 +1,14 @@
1
+ # Copyright (C) 2026 RemoteRF
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
@@ -0,0 +1,20 @@
1
+ # Copyright (C) 2026 RemoteRF
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ """Lives in remoterf-device-core (remoterf_device_core.drivers.ti_awr6843.ti_awr6843_schema); this name stays importable."""
17
+ import importlib
18
+ import sys
19
+
20
+ sys.modules[__name__] = importlib.import_module("remoterf_device_core.drivers.ti_awr6843.ti_awr6843_schema")
@@ -0,0 +1,14 @@
1
+ # Copyright (C) 2026 RemoteRF
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
@@ -0,0 +1,14 @@
1
+ # Copyright (C) 2026 RemoteRF
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
@@ -0,0 +1,14 @@
1
+ # Copyright (C) 2026 RemoteRF
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
@@ -0,0 +1,20 @@
1
+ # Copyright (C) 2026 RemoteRF
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ """Lives in remoterf-device-core (remoterf_device_core.drivers.wifi.wifi_schema); this name stays importable."""
17
+ import importlib
18
+ import sys
19
+
20
+ sys.modules[__name__] = importlib.import_module("remoterf_device_core.drivers.wifi.wifi_schema")
@@ -0,0 +1,14 @@
1
+ # Copyright (C) 2026 RemoteRF
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
@@ -0,0 +1,14 @@
1
+ # Copyright (C) 2026 RemoteRF
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
@@ -130,7 +130,7 @@ def _table() -> list[Command]:
130
130
  Command(("reservations purge",), "Remove ALL reservations", _daemon("cmd_reservations_purge"), "Reservations", where=D),
131
131
  # ---- groups ----
132
132
  Command(("groups list",), "List user groups", _shell("cmd_groups_list"), "Groups"),
133
- Command(("groups create",), "Create a user group (interactive)", _shell("cmd_groups_create"), "Groups"),
133
+ Command(("groups create",), "Create a group: groups create <name> --devices <ids> [--max-reservations 3 --max-time 1800 --horizon 604800 --lifetime 0 --lan-only --public --share <clusters>] --yes [--json]; no name is interactive", _shell("cmd_groups_create"), "Groups"),
134
134
  Command(("groups edit",), "Edit a user group (interactive)", _shell("cmd_groups_edit"), "Groups"),
135
135
  Command(("groups remove",), "Delete a user group (interactive)", _shell("cmd_groups_remove"), "Groups"),
136
136
  Command(("groups public",), "Share a group with anyone on RemoteRF Global, or stop: groups public <group> on|off", _federation("public"), "Groups"),
@@ -138,7 +138,7 @@ def _table() -> list[Command]:
138
138
  Command(("groups csv",), "Export ALL user groups as CSV to Downloads", _shell("cmd_groups_csv"), "Groups"),
139
139
  # ---- enrollment codes ----
140
140
  Command(("codes list",), "List enrollment codes", _shell("cmd_codes_list"), "Enrollment codes"),
141
- Command(("codes create",), "Create enrollment codes for a group or a cluster (interactive)", _shell("cmd_codes_create"), "Enrollment codes"),
141
+ Command(("codes create",), "Create codes: codes create <group name|id|none> --count N [--length 10 --uses 1 --duration 3600] --yes [--json]; no group is interactive", _shell("cmd_codes_create"), "Enrollment codes"),
142
142
  Command(("codes remove",), "Delete an enrollment code (interactive)", _shell("cmd_codes_remove"), "Enrollment codes"),
143
143
  Command(("codes csv",), "Export ALL enrollment codes as CSV to Downloads", _shell("cmd_codes_csv"), "Enrollment codes"),
144
144
  # ---- hosts ----
@@ -18,6 +18,7 @@
18
18
  from __future__ import annotations
19
19
 
20
20
  import importlib
21
+ import importlib.util
21
22
  import inspect
22
23
  import logging
23
24
  import os
@@ -36,8 +37,8 @@ from contextlib import contextmanager
36
37
  from remoterf_federation_core import is_device_handle
37
38
 
38
39
  from ..common.utils import validate_token, get_remoterf_root, get_drivers_dir, device_name_key
39
- from ..common.idl.schema import (OVERRIDE_PREFIX, get_driver_class, last_load_results, load_drivers,
40
- override_stale)
40
+ from ..common.idl.schema import (OVERRIDE_PREFIX, get_driver_class, last_load_results, load_builtin_drivers,
41
+ load_drivers, override_stale)
41
42
  from ..host import host_tunnel_server as hts
42
43
 
43
44
  logger = logging.getLogger(__name__)
@@ -694,12 +695,14 @@ def _seed_default_wifi_driver() -> None:
694
695
  legacy = drivers_dir / "wifi_dongle_schema.py"
695
696
  if not _remove_managed_driver(legacy) and legacy.is_file():
696
697
  print(f"[drivers] Kept {legacy}: not a managed copy. Remove it, or two Wi-Fi drivers register.")
697
- _seed_default_driver("wifi_schema.py", "wifi")
698
+ # No wifi_schema.py copy any more: the built-in serves natively, and an existing copy (managed
699
+ # or not) is left exactly as it is, so it keeps winning over the built-in.
698
700
 
699
701
 
700
702
  # Only the C library from conda: pip already installed pyadi-iio, and conda's
701
703
  # pulls a cryptography that breaks the pip-installed pyOpenSSL aioquic needs.
702
704
  _LIBIIO_FIX = "conda install -c conda-forge libiio (in the server's conda env; not pyadi-iio, pip already has it)"
705
+ _OPTIONAL_NATIVE_TYPES = frozenset({"bluetooth", "gps", "krakensdr", "rfbeam_kmd7", "ti_awr6843"})
703
706
 
704
707
 
705
708
  def _load_all_drivers() -> None:
@@ -725,7 +728,8 @@ def _load_all_drivers() -> None:
725
728
  return
726
729
  print(line)
727
730
 
728
- for label, module in (
731
+ # Built-ins first, the drivers dir after: a file there of the same type registers last and wins.
732
+ builtins = {importlib.util.resolve_name(module, __package__): label for label, module in (
729
733
  ("built-in pluto driver", "..common.idl.pluto_schema"),
730
734
  ("built-in pluto mimo driver", "..common.idl.pluto_mimo_schema"),
731
735
  ("built-in usrp driver", "..drivers.usrp.usrp_schema"),
@@ -734,11 +738,25 @@ def _load_all_drivers() -> None:
734
738
  ("built-in TI mmWave driver", "..drivers.ti_mmwave.ti_mmwave_schema"),
735
739
  ("built-in USRP Phaser driver", "..drivers.usrp_phaser.usrp_phaser_schema"),
736
740
  ("built-in Wi-Fi driver", "..drivers.wifi.wifi_schema"),
737
- ):
738
- try:
739
- importlib.import_module(module, __package__)
740
- except Exception as e:
741
- _warn(f"Warning: could not load {label}: {e}", str(e))
741
+ ("built-in Bluetooth driver", "..drivers.bluetooth.bluetooth_schema"),
742
+ ("built-in GPS driver", "..drivers.gps.gps_schema"),
743
+ ("built-in KrakenSDR driver", "..drivers.krakensdr.krakensdr_schema"),
744
+ ("built-in RFbeam K-MD7 driver", "..drivers.rfbeam_kmd7.rfbeam_kmd7_schema"),
745
+ ("built-in TI AWR6843 driver", "..drivers.ti_awr6843.ti_awr6843_schema"),
746
+ )}
747
+ configured = None
748
+ for module, status in load_builtin_drivers(builtins).items():
749
+ if status == "ok":
750
+ continue
751
+ dtype = module.rsplit(".", 1)[1].removesuffix("_schema")
752
+ if dtype in _OPTIONAL_NATIVE_TYPES:
753
+ # Their libraries are optional extras: a line only when devices.yml has one (`schema served` lists all).
754
+ if configured is None:
755
+ configured = {(r.get("TYPE") or "").strip().lower() for r in _load_device_records().values()}
756
+ if dtype not in configured:
757
+ continue
758
+ error = status.removeprefix("error: ")
759
+ _warn(f"Warning: could not load {builtins[module]}: {error}", error)
742
760
 
743
761
  for fname, status in load_drivers().items():
744
762
  if status != "ok":
@@ -61,6 +61,7 @@ from remoterf_device_core.direct import (
61
61
  peer_address, quiet_aioice_errors, read_frame, stun_server,
62
62
  )
63
63
  from .global_onboarding import RELAYED, hostname, is_offsite
64
+ from .transport_counters import transport_counters
64
65
 
65
66
  logger = logging.getLogger("remoteRF_server.direct")
66
67
 
@@ -247,13 +248,18 @@ class _Path:
247
248
  async def _serve_call(path, reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
248
249
  """One stream = one device call, on an ICE path or a fixed-port connection."""
249
250
  bad_frame = refused = None
251
+ request = None
252
+ request_frame = response_frame = b""
250
253
  try:
251
254
  try:
252
255
  request = grpc_pb2.GenericRPCRequest()
253
- request.ParseFromString(await read_frame(reader))
256
+ request_frame = await read_frame(reader)
257
+ request.ParseFromString(request_frame)
254
258
  except asyncio.IncompleteReadError:
259
+ request = None
255
260
  return # the client went away mid-frame
256
261
  except Exception as exc: # noqa: BLE001 - not our framing: say so, then drop the path
262
+ request = None
257
263
  bad_frame = f"bad frame: {type(exc).__name__}: {exc}"
258
264
  results = {"a": map_arg(bad_frame)}
259
265
  else:
@@ -264,12 +270,17 @@ async def _serve_call(path, reader: asyncio.StreamReader, writer: asyncio.Stream
264
270
  except PathRefused as exc:
265
271
  refused = str(exc)
266
272
  results = {"a": map_arg(refused)}
267
- writer.write(frame(grpc_pb2.GenericRPCResponse(results=results).SerializeToString()))
273
+ response_frame = grpc_pb2.GenericRPCResponse(results=results).SerializeToString()
274
+ writer.write(frame(response_frame))
268
275
  writer.write_eof()
269
276
  path.calls += 1
270
277
  except Exception as exc: # noqa: BLE001 - one lost stream must not take the path down
271
278
  logger.debug("direct path stream: %s", exc)
272
279
  finally:
280
+ if request is not None:
281
+ transport_counters.record(
282
+ "direct", request.function_name, len(request_frame), len(response_frame)
283
+ )
273
284
  # Ends our side of the stream on every path; Python < 3.12 does not
274
285
  # close an abandoned StreamWriter, and the half-open stream lingers.
275
286
  writer.close()
@@ -66,7 +66,8 @@ and your LAN address. For the devices you share on Global it also sends how
66
66
  many reservations start in each hour of the day and on each day of the week
67
67
  (UTC), the reservations and time reserved per device type, and for each
68
68
  device shared on Global its name and its daily reservation counts and time
69
- reserved over the last 31 days (counts only).
69
+ reserved over the last 31 days. It also sends daily per-path, per-device-type,
70
+ per-function device call and byte counts (counts only, no users).
70
71
  Nothing else leaves your server -- no credentials, no user or reservation
71
72
  details -- and the connection only goes outward. Global publishes the network
72
73
  totals, these breakdowns, and each deployment's name, version, devices, when
@@ -815,6 +816,7 @@ STATUS_FIELDS = frozenset(
815
816
  "reservations",
816
817
  "reserved_s",
817
818
  "usage",
819
+ "transport",
818
820
  "lan_address",
819
821
  "cert_sha256",
820
822
  }
@@ -969,6 +971,44 @@ def usage_summary() -> dict:
969
971
  return usage
970
972
 
971
973
 
974
+ def transport_summary() -> dict:
975
+ """Daily device RPC calls and application bytes, without user or device identity."""
976
+ from datetime import datetime, timedelta, timezone
977
+
978
+ from .transport_counters import _FN_RE, _MAX_INTEGER, _PATHS, _TYPE_RE, transport_counters
979
+
980
+ transport_counters.flush()
981
+ today = datetime.now(timezone.utc).date()
982
+ days = []
983
+ for day, path, device_type, fn, calls, bytes_in, bytes_out in _rows(
984
+ _db_dir() / "reservations.db",
985
+ "SELECT day, path, type, fn, calls, bytes_in, bytes_out FROM TransportDays "
986
+ "WHERE day BETWEEN ? AND ? AND (calls > 0 OR bytes_in > 0 OR bytes_out > 0) "
987
+ "ORDER BY day DESC, path, type, fn LIMIT 500",
988
+ # Global keeps every day it was sent; only today and yesterday still change.
989
+ (str(today - timedelta(days=1)), str(today)),
990
+ ):
991
+ if (not re.fullmatch(r"\d{4}-\d{2}-\d{2}", str(day)) or path not in _PATHS
992
+ or not _TYPE_RE.fullmatch(str(device_type)) or not _FN_RE.fullmatch(str(fn))):
993
+ continue
994
+ try:
995
+ values = [min(max(int(value), 0), _MAX_INTEGER) for value in (calls, bytes_in, bytes_out)]
996
+ except (TypeError, ValueError):
997
+ continue
998
+ if not any(values):
999
+ continue
1000
+ days.append({
1001
+ "day": day,
1002
+ "path": path,
1003
+ "type": device_type,
1004
+ "fn": fn,
1005
+ "calls": values[0],
1006
+ "bytes_in": values[1],
1007
+ "bytes_out": values[2],
1008
+ })
1009
+ return {"days": days}
1010
+
1011
+
972
1012
  def cert_sha256() -> Optional[str]:
973
1013
  import hashlib
974
1014
 
@@ -1000,6 +1040,7 @@ def status_payload() -> dict:
1000
1040
  "reservations": _count(reservations, "SELECT n FROM ReservationCount"),
1001
1041
  "reserved_s": _count(reservations, "SELECT CAST(ROUND(seconds) AS INTEGER) FROM ReservedTotal"),
1002
1042
  "usage": usage_summary(),
1043
+ "transport": transport_summary(),
1003
1044
  "lan_address": lan_endpoint(),
1004
1045
  "cert_sha256": cert_sha256(),
1005
1046
  }
@@ -1019,11 +1060,16 @@ def send_status() -> None:
1019
1060
 
1020
1061
  payload = status_payload()
1021
1062
  # A Global older than a field refuses the note: drop the newest first
1022
- # (`usage.by_device`, `usage`, then `reservations`), then
1063
+ # (`transport`, `usage.by_device`, `usage`, then `reservations`), then
1023
1064
  # `proxies`/`reserved_s`, as the note was before each of them.
1024
- steps = [payload, dict(payload, usage={k: v for k, v in payload["usage"].items() if k != "by_device"})]
1065
+ without_transport = {k: v for k, v in payload.items() if k != "transport"}
1066
+ steps = [
1067
+ payload,
1068
+ without_transport,
1069
+ dict(without_transport, usage={k: v for k, v in payload["usage"].items() if k != "by_device"}),
1070
+ ]
1025
1071
  for drop in (("usage",), ("usage", "reservations"), ("usage", "reservations", "proxies", "reserved_s")):
1026
- steps.append({k: v for k, v in payload.items() if k not in drop})
1072
+ steps.append({k: v for k, v in without_transport.items() if k not in drop})
1027
1073
  for status in steps:
1028
1074
  try:
1029
1075
  call("POST", REPORT_PATH, {"kind": "status", "status": status})
@@ -60,6 +60,7 @@ from .device_manager import (close_local_devices, reload_devices, get_registered
60
60
  from .user_group_cli import make_user_group_local, edit_user_group_local, list_user_groups_local, remove_user_group_local, make_enrollment_codes_local, remove_enrollment_code_local, list_enrollment_codes_local, export_user_groups_csv_local, export_enrollment_codes_csv_local
61
61
  from .user_group_handler import start_usergroup_cleanup_loop
62
62
  from .global_onboarding import start_agent
63
+ from .transport_counters import transport_counters
63
64
 
64
65
  from ..host import host_tunnel_server as hts
65
66
  from ..host.host_tunnel_server import HostTunnelServicer
@@ -190,14 +191,23 @@ class GenericRPCServicer(grpc_pb2_grpc.GenericRPCServicer):
190
191
  # LAN-only groups do not exist to a call that came through the relay.
191
192
  from .global_onboarding import CLIENT_VERSION, RELAYED, client_metadata_version, is_relayed
192
193
 
193
- token = RELAYED.set(is_relayed(context.peer()))
194
+ relayed = is_relayed(context.peer())
195
+ token = RELAYED.set(relayed)
194
196
  version = CLIENT_VERSION.set(client_metadata_version(context.invocation_metadata()))
197
+ reply = None
195
198
  try:
196
199
  response = rpc_manager.run_rpc(function_name=request.function_name, args=request.args)
200
+ reply = grpc_pb2.GenericRPCResponse(results=response)
201
+ return reply
197
202
  finally:
198
203
  CLIENT_VERSION.reset(version)
199
204
  RELAYED.reset(token)
200
- return grpc_pb2.GenericRPCResponse(results=response)
205
+ transport_counters.record_protobuf(
206
+ "relay" if relayed else "lan",
207
+ request.function_name,
208
+ request,
209
+ reply,
210
+ )
201
211
 
202
212
 
203
213
  @dataclass
@@ -231,6 +241,7 @@ class ServerRuntime:
231
241
  try:
232
242
  termination.wait(timeout=max(1.0, float(grace_sec) + 2.0))
233
243
  finally:
244
+ transport_counters.flush(force=True)
234
245
  if direct_path is not None:
235
246
  direct_path.shutdown()
236
247
  session_manager.close_all()
@@ -73,6 +73,7 @@ def _fanout(call, empty):
73
73
  _LAN_SECRET = re.compile(r"[0-9a-f]{64}")
74
74
  from .user_group_handler import user_group_handler
75
75
  from . import global_onboarding
76
+ from .transport_counters import create_table as create_transport_days
76
77
  from .global_onboarding import CLIENT_VERSION, MIN_CLIENT_VERSION, RELAYED, client_is_current, lan_only_hint
77
78
  from .federation.migrations import (
78
79
  FEDERATED_PASSWORD_HASH,
@@ -310,6 +311,7 @@ class ReservationHandler:
310
311
  END;
311
312
  COMMIT;
312
313
  ''')
314
+ create_transport_days(self.db)
313
315
 
314
316
  # "Stay logged in" for the deployment protocol: a random token the
315
317
  # client keeps instead of the password. Only its hash lives here, so