wxcli 1.2.0__py3-none-any.whl

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 (413) hide show
  1. wxcli/__init__.py +4 -0
  2. wxcli/_playbook/.claude/agents/migration-advisor.md +239 -0
  3. wxcli/_playbook/.claude/agents/wxc-calling-builder.md +946 -0
  4. wxcli/_playbook/.claude/rules/cleanup.md +29 -0
  5. wxcli/_playbook/.claude/rules/cucm-migration.md +58 -0
  6. wxcli/_playbook/.claude/rules/org-health.md +14 -0
  7. wxcli/_playbook/.claude/settings.json +8 -0
  8. wxcli/_playbook/.claude/skills/audit-compliance/SKILL.md +515 -0
  9. wxcli/_playbook/.claude/skills/call-control/SKILL.md +747 -0
  10. wxcli/_playbook/.claude/skills/configure-features/SKILL.md +623 -0
  11. wxcli/_playbook/.claude/skills/configure-routing/SKILL.md +627 -0
  12. wxcli/_playbook/.claude/skills/contact-center/SKILL.md +1181 -0
  13. wxcli/_playbook/.claude/skills/cucm-migrate/SKILL.md +820 -0
  14. wxcli/_playbook/.claude/skills/customer-assist/SKILL.md +443 -0
  15. wxcli/_playbook/.claude/skills/device-platform/SKILL.md +407 -0
  16. wxcli/_playbook/.claude/skills/manage-call-settings/SKILL.md +757 -0
  17. wxcli/_playbook/.claude/skills/manage-devices/SKILL.md +790 -0
  18. wxcli/_playbook/.claude/skills/manage-identity/SKILL.md +638 -0
  19. wxcli/_playbook/.claude/skills/manage-licensing/SKILL.md +528 -0
  20. wxcli/_playbook/.claude/skills/manage-meetings/SKILL.md +708 -0
  21. wxcli/_playbook/.claude/skills/messaging-bots/SKILL.md +365 -0
  22. wxcli/_playbook/.claude/skills/messaging-spaces/SKILL.md +520 -0
  23. wxcli/_playbook/.claude/skills/org-health/SKILL.md +159 -0
  24. wxcli/_playbook/.claude/skills/provision-calling/SKILL.md +421 -0
  25. wxcli/_playbook/.claude/skills/query-live/SKILL.md +183 -0
  26. wxcli/_playbook/.claude/skills/query-live/domains/call-flow-trace.md +223 -0
  27. wxcli/_playbook/.claude/skills/query-live/domains/call-history.md +44 -0
  28. wxcli/_playbook/.claude/skills/query-live/domains/features.md +169 -0
  29. wxcli/_playbook/.claude/skills/query-live/domains/numbers.md +93 -0
  30. wxcli/_playbook/.claude/skills/query-live/domains/people-and-settings.md +146 -0
  31. wxcli/_playbook/.claude/skills/query-live/domains/routing.md +124 -0
  32. wxcli/_playbook/.claude/skills/reporting/SKILL.md +501 -0
  33. wxcli/_playbook/.claude/skills/reporting/references/cdr-recipes.md +1628 -0
  34. wxcli/_playbook/.claude/skills/reporting-cc/SKILL.md +251 -0
  35. wxcli/_playbook/.claude/skills/reporting-meetings/SKILL.md +290 -0
  36. wxcli/_playbook/.claude/skills/teardown/SKILL.md +214 -0
  37. wxcli/_playbook/.claude/skills/video-mesh/SKILL.md +379 -0
  38. wxcli/_playbook/.claude/skills/wxc-calling-debug/SKILL.md +475 -0
  39. wxcli/_playbook/CLAUDE.md +373 -0
  40. wxcli/_playbook/docs/reference/CLAUDE.md +25 -0
  41. wxcli/_playbook/docs/reference/admin-apps-data.md +643 -0
  42. wxcli/_playbook/docs/reference/admin-audit-security.md +322 -0
  43. wxcli/_playbook/docs/reference/admin-hybrid.md +383 -0
  44. wxcli/_playbook/docs/reference/admin-identity-scim.md +1007 -0
  45. wxcli/_playbook/docs/reference/admin-licensing.md +414 -0
  46. wxcli/_playbook/docs/reference/admin-org-management.md +586 -0
  47. wxcli/_playbook/docs/reference/admin-partner.md +357 -0
  48. wxcli/_playbook/docs/reference/archive/wxc-sdk-patterns.md +1229 -0
  49. wxcli/_playbook/docs/reference/archive/wxcadm-advanced.md +835 -0
  50. wxcli/_playbook/docs/reference/archive/wxcadm-core.md +743 -0
  51. wxcli/_playbook/docs/reference/archive/wxcadm-devices-workspaces.md +1305 -0
  52. wxcli/_playbook/docs/reference/archive/wxcadm-features.md +849 -0
  53. wxcli/_playbook/docs/reference/archive/wxcadm-locations.md +938 -0
  54. wxcli/_playbook/docs/reference/archive/wxcadm-person.md +1255 -0
  55. wxcli/_playbook/docs/reference/archive/wxcadm-routing.md +1262 -0
  56. wxcli/_playbook/docs/reference/authentication.md +965 -0
  57. wxcli/_playbook/docs/reference/call-control.md +1205 -0
  58. wxcli/_playbook/docs/reference/call-features-additional.md +2541 -0
  59. wxcli/_playbook/docs/reference/call-features-major.md +1320 -0
  60. wxcli/_playbook/docs/reference/call-routing.md +2304 -0
  61. wxcli/_playbook/docs/reference/contact-center-analytics.md +1049 -0
  62. wxcli/_playbook/docs/reference/contact-center-core.md +1614 -0
  63. wxcli/_playbook/docs/reference/contact-center-journey.md +457 -0
  64. wxcli/_playbook/docs/reference/contact-center-routing.md +1138 -0
  65. wxcli/_playbook/docs/reference/devices-core.md +1869 -0
  66. wxcli/_playbook/docs/reference/devices-dect.md +1150 -0
  67. wxcli/_playbook/docs/reference/devices-platform.md +467 -0
  68. wxcli/_playbook/docs/reference/devices-workspaces.md +1426 -0
  69. wxcli/_playbook/docs/reference/emergency-services.md +1122 -0
  70. wxcli/_playbook/docs/reference/location-calling-core.md +2037 -0
  71. wxcli/_playbook/docs/reference/location-calling-media.md +1334 -0
  72. wxcli/_playbook/docs/reference/location-recording-advanced.md +1482 -0
  73. wxcli/_playbook/docs/reference/meetings-content.md +473 -0
  74. wxcli/_playbook/docs/reference/meetings-core.md +903 -0
  75. wxcli/_playbook/docs/reference/meetings-infrastructure.md +899 -0
  76. wxcli/_playbook/docs/reference/meetings-settings.md +850 -0
  77. wxcli/_playbook/docs/reference/messaging-bots.md +1091 -0
  78. wxcli/_playbook/docs/reference/messaging-spaces.md +695 -0
  79. wxcli/_playbook/docs/reference/person-call-settings-behavior.md +1639 -0
  80. wxcli/_playbook/docs/reference/person-call-settings-handling.md +1394 -0
  81. wxcli/_playbook/docs/reference/person-call-settings-media.md +1898 -0
  82. wxcli/_playbook/docs/reference/person-call-settings-permissions.md +1329 -0
  83. wxcli/_playbook/docs/reference/provisioning.md +1366 -0
  84. wxcli/_playbook/docs/reference/reporting-analytics.md +1318 -0
  85. wxcli/_playbook/docs/reference/self-service-call-settings.md +1020 -0
  86. wxcli/_playbook/docs/reference/virtual-lines.md +1132 -0
  87. wxcli/_playbook/docs/reference/webhooks-events.md +1444 -0
  88. wxcli/_playbook/docs/reference/wxcadm-xsi-realtime.md +953 -0
  89. wxcli/_version.py +24 -0
  90. wxcli/auth.py +137 -0
  91. wxcli/commands/__init__.py +0 -0
  92. wxcli/commands/_registry.py +181 -0
  93. wxcli/commands/activation_email.py +103 -0
  94. wxcli/commands/admin_recordings.py +585 -0
  95. wxcli/commands/analytics.py +105 -0
  96. wxcli/commands/announcement_playlists.py +219 -0
  97. wxcli/commands/announcements.py +519 -0
  98. wxcli/commands/archive_users.py +36 -0
  99. wxcli/commands/attachment_actions.py +60 -0
  100. wxcli/commands/audit_events.py +83 -0
  101. wxcli/commands/authorizations.py +113 -0
  102. wxcli/commands/auto_attendant.py +642 -0
  103. wxcli/commands/broadworks_billing_reports.py +126 -0
  104. wxcli/commands/broadworks_enterprises.py +160 -0
  105. wxcli/commands/broadworks_subscribers.py +319 -0
  106. wxcli/commands/broadworks_workspaces.py +108 -0
  107. wxcli/commands/call_controls.py +1304 -0
  108. wxcli/commands/call_park.py +494 -0
  109. wxcli/commands/call_pickup.py +229 -0
  110. wxcli/commands/call_queue.py +1878 -0
  111. wxcli/commands/call_recording.py +707 -0
  112. wxcli/commands/call_routing.py +1610 -0
  113. wxcli/commands/call_settings_for_me_phase_5.py +212 -0
  114. wxcli/commands/caller_reputation.py +169 -0
  115. wxcli/commands/calling_service.py +305 -0
  116. wxcli/commands/cc_address_book.py +770 -0
  117. wxcli/commands/cc_agent_greetings.py +242 -0
  118. wxcli/commands/cc_agent_summaries.py +39 -0
  119. wxcli/commands/cc_agent_wellbeing.py +216 -0
  120. wxcli/commands/cc_agents.py +425 -0
  121. wxcli/commands/cc_ai_assistant.py +55 -0
  122. wxcli/commands/cc_ai_feature.py +294 -0
  123. wxcli/commands/cc_audio_files.py +149 -0
  124. wxcli/commands/cc_auto_csat.py +311 -0
  125. wxcli/commands/cc_aux_code.py +458 -0
  126. wxcli/commands/cc_business_hour.py +353 -0
  127. wxcli/commands/cc_call_monitoring.py +228 -0
  128. wxcli/commands/cc_callbacks.py +194 -0
  129. wxcli/commands/cc_campaign.py +212 -0
  130. wxcli/commands/cc_captures.py +39 -0
  131. wxcli/commands/cc_contact_list.py +169 -0
  132. wxcli/commands/cc_contact_number.py +295 -0
  133. wxcli/commands/cc_data_sources.py +235 -0
  134. wxcli/commands/cc_desktop_layout.py +380 -0
  135. wxcli/commands/cc_desktop_profile.py +322 -0
  136. wxcli/commands/cc_dial_number.py +511 -0
  137. wxcli/commands/cc_dial_plan.py +347 -0
  138. wxcli/commands/cc_dnc.py +101 -0
  139. wxcli/commands/cc_entry_point.py +545 -0
  140. wxcli/commands/cc_ewt.py +51 -0
  141. wxcli/commands/cc_flow.py +477 -0
  142. wxcli/commands/cc_global_vars.py +430 -0
  143. wxcli/commands/cc_holiday_list.py +335 -0
  144. wxcli/commands/cc_journey.py +1261 -0
  145. wxcli/commands/cc_legacy_flows.py +49 -0
  146. wxcli/commands/cc_multimedia_profile.py +436 -0
  147. wxcli/commands/cc_notification.py +51 -0
  148. wxcli/commands/cc_outdial_ani.py +604 -0
  149. wxcli/commands/cc_overrides.py +341 -0
  150. wxcli/commands/cc_queue.py +1083 -0
  151. wxcli/commands/cc_queue_stats.py +55 -0
  152. wxcli/commands/cc_realtime.py +51 -0
  153. wxcli/commands/cc_resource_collection.py +312 -0
  154. wxcli/commands/cc_search.py +46 -0
  155. wxcli/commands/cc_site.py +400 -0
  156. wxcli/commands/cc_skill.py +350 -0
  157. wxcli/commands/cc_skill_profile.py +289 -0
  158. wxcli/commands/cc_subscriptions.py +426 -0
  159. wxcli/commands/cc_summaries.py +130 -0
  160. wxcli/commands/cc_tasks.py +844 -0
  161. wxcli/commands/cc_team.py +325 -0
  162. wxcli/commands/cc_user_profiles.py +349 -0
  163. wxcli/commands/cc_users.py +701 -0
  164. wxcli/commands/cc_work_types.py +363 -0
  165. wxcli/commands/cdr.py +89 -0
  166. wxcli/commands/classifications.py +38 -0
  167. wxcli/commands/cleanup.py +1427 -0
  168. wxcli/commands/client_settings.py +72 -0
  169. wxcli/commands/conference.py +328 -0
  170. wxcli/commands/configure.py +72 -0
  171. wxcli/commands/converged_recordings.py +347 -0
  172. wxcli/commands/converged_recordings_export.py +310 -0
  173. wxcli/commands/cq_playlists.py +42 -0
  174. wxcli/commands/cucm.py +2794 -0
  175. wxcli/commands/cucm_config.py +129 -0
  176. wxcli/commands/customer_assist.py +373 -0
  177. wxcli/commands/data_sources.py +227 -0
  178. wxcli/commands/dect_devices.py +787 -0
  179. wxcli/commands/device_configurations.py +77 -0
  180. wxcli/commands/device_dynamic_settings.py +320 -0
  181. wxcli/commands/device_settings.py +1489 -0
  182. wxcli/commands/devices.py +280 -0
  183. wxcli/commands/domains.py +142 -0
  184. wxcli/commands/ecm.py +178 -0
  185. wxcli/commands/emergency_services.py +827 -0
  186. wxcli/commands/events.py +83 -0
  187. wxcli/commands/external_voicemail.py +52 -0
  188. wxcli/commands/groups.py +206 -0
  189. wxcli/commands/guest_management.py +76 -0
  190. wxcli/commands/hds.py +340 -0
  191. wxcli/commands/hot_desk.py +67 -0
  192. wxcli/commands/hot_desking_members.py +118 -0
  193. wxcli/commands/hot_desking_portal.py +124 -0
  194. wxcli/commands/hunt_group.py +577 -0
  195. wxcli/commands/hybrid_clusters.py +71 -0
  196. wxcli/commands/hybrid_connectors.py +67 -0
  197. wxcli/commands/identity_org.py +159 -0
  198. wxcli/commands/init_playbook.py +155 -0
  199. wxcli/commands/licenses.py +106 -0
  200. wxcli/commands/live_monitoring.py +40 -0
  201. wxcli/commands/location_call_handling.py +577 -0
  202. wxcli/commands/location_schedules.py +341 -0
  203. wxcli/commands/location_settings.py +1297 -0
  204. wxcli/commands/location_voicemail.py +494 -0
  205. wxcli/commands/locations.py +336 -0
  206. wxcli/commands/meeting_captions.py +110 -0
  207. wxcli/commands/meeting_chats.py +63 -0
  208. wxcli/commands/meeting_invitees.py +234 -0
  209. wxcli/commands/meeting_messages.py +28 -0
  210. wxcli/commands/meeting_participants.py +278 -0
  211. wxcli/commands/meeting_polls.py +111 -0
  212. wxcli/commands/meeting_preferences.py +515 -0
  213. wxcli/commands/meeting_qa.py +78 -0
  214. wxcli/commands/meeting_qualities.py +43 -0
  215. wxcli/commands/meeting_reports.py +101 -0
  216. wxcli/commands/meeting_session_types.py +105 -0
  217. wxcli/commands/meeting_site.py +57 -0
  218. wxcli/commands/meeting_slido.py +44 -0
  219. wxcli/commands/meeting_summaries.py +92 -0
  220. wxcli/commands/meeting_tracking_codes.py +233 -0
  221. wxcli/commands/meeting_transcripts.py +230 -0
  222. wxcli/commands/meetings.py +1997 -0
  223. wxcli/commands/memberships.py +164 -0
  224. wxcli/commands/messages.py +213 -0
  225. wxcli/commands/mode_management.py +244 -0
  226. wxcli/commands/my_call_settings.py +3577 -0
  227. wxcli/commands/numbers.py +405 -0
  228. wxcli/commands/operating_modes.py +410 -0
  229. wxcli/commands/org_contacts.py +284 -0
  230. wxcli/commands/org_health_cli.py +55 -0
  231. wxcli/commands/org_settings.py +71 -0
  232. wxcli/commands/organizations.py +83 -0
  233. wxcli/commands/paging_group.py +257 -0
  234. wxcli/commands/partner_admins.py +141 -0
  235. wxcli/commands/partner_reports.py +188 -0
  236. wxcli/commands/partner_tags.py +216 -0
  237. wxcli/commands/people.py +279 -0
  238. wxcli/commands/person_call_settings.py +71 -0
  239. wxcli/commands/pstn.py +280 -0
  240. wxcli/commands/recording_report.py +154 -0
  241. wxcli/commands/report_templates.py +38 -0
  242. wxcli/commands/reports.py +144 -0
  243. wxcli/commands/resource_group_memberships.py +160 -0
  244. wxcli/commands/resource_groups.py +67 -0
  245. wxcli/commands/roles.py +63 -0
  246. wxcli/commands/room_tabs.py +160 -0
  247. wxcli/commands/rooms.py +225 -0
  248. wxcli/commands/scim_bulk.py +46 -0
  249. wxcli/commands/scim_groups.py +246 -0
  250. wxcli/commands/scim_schemas.py +82 -0
  251. wxcli/commands/scim_users.py +283 -0
  252. wxcli/commands/security_audit.py +54 -0
  253. wxcli/commands/service_apps.py +51 -0
  254. wxcli/commands/single_number_reach.py +209 -0
  255. wxcli/commands/team_memberships.py +155 -0
  256. wxcli/commands/teams.py +153 -0
  257. wxcli/commands/ucm_profile.py +42 -0
  258. wxcli/commands/update.py +219 -0
  259. wxcli/commands/user_settings.py +5116 -0
  260. wxcli/commands/video_mesh.py +1090 -0
  261. wxcli/commands/virtual_extensions.py +502 -0
  262. wxcli/commands/virtual_line_settings.py +2072 -0
  263. wxcli/commands/webhooks.py +176 -0
  264. wxcli/commands/wholesale_billing_reports.py +147 -0
  265. wxcli/commands/wholesale_provisioning.py +523 -0
  266. wxcli/commands/workspace_locations.py +339 -0
  267. wxcli/commands/workspace_metrics.py +103 -0
  268. wxcli/commands/workspace_personalization.py +70 -0
  269. wxcli/commands/workspace_settings.py +3347 -0
  270. wxcli/commands/workspaces.py +279 -0
  271. wxcli/commands/xapi.py +114 -0
  272. wxcli/config.py +139 -0
  273. wxcli/errors.py +77 -0
  274. wxcli/main.py +210 -0
  275. wxcli/migration/CLAUDE.md +106 -0
  276. wxcli/migration/__init__.py +1 -0
  277. wxcli/migration/advisory/CLAUDE.md +196 -0
  278. wxcli/migration/advisory/__init__.py +46 -0
  279. wxcli/migration/advisory/advisor.py +107 -0
  280. wxcli/migration/advisory/advisory_patterns.py +2552 -0
  281. wxcli/migration/advisory/recommendation_rules.py +835 -0
  282. wxcli/migration/cucm/CLAUDE.md +74 -0
  283. wxcli/migration/cucm/__init__.py +5 -0
  284. wxcli/migration/cucm/connection.py +265 -0
  285. wxcli/migration/cucm/discovery.py +225 -0
  286. wxcli/migration/cucm/extractors/__init__.py +1 -0
  287. wxcli/migration/cucm/extractors/announcements.py +92 -0
  288. wxcli/migration/cucm/extractors/base.py +90 -0
  289. wxcli/migration/cucm/extractors/device_profiles.py +98 -0
  290. wxcli/migration/cucm/extractors/devices.py +193 -0
  291. wxcli/migration/cucm/extractors/e911.py +103 -0
  292. wxcli/migration/cucm/extractors/features.py +309 -0
  293. wxcli/migration/cucm/extractors/helpers.py +73 -0
  294. wxcli/migration/cucm/extractors/informational.py +295 -0
  295. wxcli/migration/cucm/extractors/locations.py +221 -0
  296. wxcli/migration/cucm/extractors/moh.py +100 -0
  297. wxcli/migration/cucm/extractors/remote_destinations.py +96 -0
  298. wxcli/migration/cucm/extractors/routing.py +493 -0
  299. wxcli/migration/cucm/extractors/shared_lines.py +146 -0
  300. wxcli/migration/cucm/extractors/templates.py +229 -0
  301. wxcli/migration/cucm/extractors/tier4.py +173 -0
  302. wxcli/migration/cucm/extractors/users.py +240 -0
  303. wxcli/migration/cucm/extractors/voicemail.py +243 -0
  304. wxcli/migration/cucm/extractors/workspaces.py +88 -0
  305. wxcli/migration/cucm/unity_connection.py +304 -0
  306. wxcli/migration/execute/CLAUDE.md +372 -0
  307. wxcli/migration/execute/__init__.py +303 -0
  308. wxcli/migration/execute/batch.py +277 -0
  309. wxcli/migration/execute/dependency.py +483 -0
  310. wxcli/migration/execute/engine.py +964 -0
  311. wxcli/migration/execute/handlers.py +2232 -0
  312. wxcli/migration/execute/planner.py +1939 -0
  313. wxcli/migration/execute/runtime.py +571 -0
  314. wxcli/migration/export/CLAUDE.md +49 -0
  315. wxcli/migration/export/__init__.py +8 -0
  316. wxcli/migration/export/csv_export.py +126 -0
  317. wxcli/migration/export/deployment_plan.py +479 -0
  318. wxcli/migration/export/json_export.py +60 -0
  319. wxcli/migration/models.py +941 -0
  320. wxcli/migration/phone_models.py +50 -0
  321. wxcli/migration/preflight/CLAUDE.md +52 -0
  322. wxcli/migration/preflight/__init__.py +89 -0
  323. wxcli/migration/preflight/checks.py +911 -0
  324. wxcli/migration/preflight/runner.py +293 -0
  325. wxcli/migration/rate_limiter.py +144 -0
  326. wxcli/migration/report/CLAUDE.md +245 -0
  327. wxcli/migration/report/__init__.py +7 -0
  328. wxcli/migration/report/appendix.py +2437 -0
  329. wxcli/migration/report/assembler.py +183 -0
  330. wxcli/migration/report/charts.py +263 -0
  331. wxcli/migration/report/executive.py +776 -0
  332. wxcli/migration/report/explainer.py +704 -0
  333. wxcli/migration/report/helpers.py +74 -0
  334. wxcli/migration/report/ingest.py +152 -0
  335. wxcli/migration/report/notice_templates.py +192 -0
  336. wxcli/migration/report/score.py +375 -0
  337. wxcli/migration/report/styles.py +1052 -0
  338. wxcli/migration/report/user_diff.py +1052 -0
  339. wxcli/migration/report/user_notice.py +425 -0
  340. wxcli/migration/state.py +139 -0
  341. wxcli/migration/store.py +860 -0
  342. wxcli/migration/transform/CLAUDE.md +107 -0
  343. wxcli/migration/transform/__init__.py +12 -0
  344. wxcli/migration/transform/analysis_pipeline.py +453 -0
  345. wxcli/migration/transform/analyzers/__init__.py +142 -0
  346. wxcli/migration/transform/analyzers/css_permission.py +189 -0
  347. wxcli/migration/transform/analyzers/css_routing.py +335 -0
  348. wxcli/migration/transform/analyzers/device_compatibility.py +122 -0
  349. wxcli/migration/transform/analyzers/dn_ambiguity.py +131 -0
  350. wxcli/migration/transform/analyzers/duplicate_user.py +221 -0
  351. wxcli/migration/transform/analyzers/extension_conflict.py +173 -0
  352. wxcli/migration/transform/analyzers/feature_approximation.py +227 -0
  353. wxcli/migration/transform/analyzers/layout_overflow.py +174 -0
  354. wxcli/migration/transform/analyzers/location_ambiguity.py +184 -0
  355. wxcli/migration/transform/analyzers/missing_data.py +300 -0
  356. wxcli/migration/transform/analyzers/selective_call_handling.py +394 -0
  357. wxcli/migration/transform/analyzers/shared_line.py +170 -0
  358. wxcli/migration/transform/analyzers/voicemail_compatibility.py +267 -0
  359. wxcli/migration/transform/analyzers/workspace_license.py +212 -0
  360. wxcli/migration/transform/cross_reference.py +1136 -0
  361. wxcli/migration/transform/cucm_pattern.py +246 -0
  362. wxcli/migration/transform/decisions.py +301 -0
  363. wxcli/migration/transform/e164.py +145 -0
  364. wxcli/migration/transform/engine.py +256 -0
  365. wxcli/migration/transform/mappers/CLAUDE.md +123 -0
  366. wxcli/migration/transform/mappers/__init__.py +76 -0
  367. wxcli/migration/transform/mappers/announcement_mapper.py +122 -0
  368. wxcli/migration/transform/mappers/base.py +190 -0
  369. wxcli/migration/transform/mappers/button_template_mapper.py +186 -0
  370. wxcli/migration/transform/mappers/call_forwarding_mapper.py +228 -0
  371. wxcli/migration/transform/mappers/call_settings_mapper.py +144 -0
  372. wxcli/migration/transform/mappers/css_mapper.py +868 -0
  373. wxcli/migration/transform/mappers/dect_mapper.py +367 -0
  374. wxcli/migration/transform/mappers/device_layout_mapper.py +289 -0
  375. wxcli/migration/transform/mappers/device_mapper.py +227 -0
  376. wxcli/migration/transform/mappers/device_profile_mapper.py +190 -0
  377. wxcli/migration/transform/mappers/device_settings_mapper.py +464 -0
  378. wxcli/migration/transform/mappers/e911_mapper.py +129 -0
  379. wxcli/migration/transform/mappers/ecbn_mapper.py +263 -0
  380. wxcli/migration/transform/mappers/executive_assistant_mapper.py +156 -0
  381. wxcli/migration/transform/mappers/feature_mapper.py +1179 -0
  382. wxcli/migration/transform/mappers/line_mapper.py +310 -0
  383. wxcli/migration/transform/mappers/location_mapper.py +235 -0
  384. wxcli/migration/transform/mappers/moh_mapper.py +114 -0
  385. wxcli/migration/transform/mappers/monitoring_mapper.py +221 -0
  386. wxcli/migration/transform/mappers/receptionist_mapper.py +245 -0
  387. wxcli/migration/transform/mappers/routing_mapper.py +821 -0
  388. wxcli/migration/transform/mappers/snr_mapper.py +201 -0
  389. wxcli/migration/transform/mappers/softkey_mapper.py +205 -0
  390. wxcli/migration/transform/mappers/user_mapper.py +264 -0
  391. wxcli/migration/transform/mappers/voicemail_group_mapper.py +286 -0
  392. wxcli/migration/transform/mappers/voicemail_mapper.py +514 -0
  393. wxcli/migration/transform/mappers/workspace_mapper.py +464 -0
  394. wxcli/migration/transform/normalizers.py +2008 -0
  395. wxcli/migration/transform/pattern_converter.py +106 -0
  396. wxcli/migration/transform/pipeline.py +271 -0
  397. wxcli/migration/transform/rules.py +255 -0
  398. wxcli/org_health/CLAUDE.md +63 -0
  399. wxcli/org_health/__init__.py +7 -0
  400. wxcli/org_health/analyze.py +93 -0
  401. wxcli/org_health/checks.py +458 -0
  402. wxcli/org_health/collector.py +62 -0
  403. wxcli/org_health/models.py +78 -0
  404. wxcli/org_health/report.py +300 -0
  405. wxcli/output.py +101 -0
  406. wxcli-1.2.0.dist-info/METADATA +505 -0
  407. wxcli-1.2.0.dist-info/RECORD +413 -0
  408. wxcli-1.2.0.dist-info/WHEEL +5 -0
  409. wxcli-1.2.0.dist-info/entry_points.txt +2 -0
  410. wxcli-1.2.0.dist-info/licenses/LICENSE +191 -0
  411. wxcli-1.2.0.dist-info/scm_file_list.json +751 -0
  412. wxcli-1.2.0.dist-info/scm_version.json +8 -0
  413. wxcli-1.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1489 @@
1
+ import json
2
+ import typer
3
+ from wxcli.auth import get_api
4
+ from wxcli.errors import WebexError, handle_rest_error
5
+ from wxcli.output import print_table, print_json
6
+ from wxcli.config import get_org_id
7
+
8
+
9
+ app = typer.Typer(help="Manage Webex Calling device-settings.")
10
+
11
+
12
+ @app.command("list")
13
+ def cmd_list(
14
+ device_id: str = typer.Argument(help="deviceId"),
15
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
16
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
17
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
18
+ debug: bool = typer.Option(False, "--debug"),
19
+ ):
20
+ """Get Device Members."""
21
+ api = get_api(debug=debug)
22
+ url = f"https://webexapis.com/v1/telephony/config/devices/{device_id}/members"
23
+ params = {}
24
+ if limit > 0:
25
+ params["max"] = limit
26
+ if offset > 0:
27
+ params["start"] = offset
28
+ org_id = get_org_id()
29
+ if org_id is not None:
30
+ params["orgId"] = org_id
31
+ result = None
32
+ try:
33
+ result = api.session.rest_get(url, params=params)
34
+ except WebexError as e:
35
+ handle_rest_error(e)
36
+ result = result or []
37
+ items = result.get("members", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
38
+ if output == "json":
39
+ print_json(items)
40
+ else:
41
+ print_table(items, columns=[('ID', 'id'), ('First Name', 'firstName'), ('Last Name', 'lastName'), ('Extension', 'extension'), ('Line Type', 'lineType')], limit=limit)
42
+
43
+
44
+
45
+ @app.command("update")
46
+ def update(
47
+ device_id: str = typer.Argument(help="deviceId"),
48
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
49
+ debug: bool = typer.Option(False, "--debug"),
50
+ ):
51
+ """Update Members on the device\n\nExample --json-body:\n '{"members":[{"port":"...","id":"...","primaryOwner":"...","lineType":"...","lineWeight":"...","hotlineEnabled":"...","hotlineDestination":"...","allowCallDeclineEnabled":"..."}]}'."""
52
+ api = get_api(debug=debug)
53
+ url = f"https://webexapis.com/v1/telephony/config/devices/{device_id}/members"
54
+ params = {}
55
+ org_id = get_org_id()
56
+ if org_id is not None:
57
+ params["orgId"] = org_id
58
+ if json_body:
59
+ body = json.loads(json_body)
60
+ else:
61
+ body = {}
62
+ try:
63
+ result = api.session.rest_put(url, json=body, params=params)
64
+ except WebexError as e:
65
+ handle_rest_error(e)
66
+ typer.echo(f"Updated.")
67
+
68
+
69
+
70
+ @app.command("list-available-members")
71
+ def list_available_members(
72
+ device_id: str = typer.Argument(help="deviceId"),
73
+ member_name: str = typer.Option(None, "--member-name", help="Search (Contains) numbers based on member name."),
74
+ phone_number: str = typer.Option(None, "--phone-number", help="Search (Contains) based on number."),
75
+ location_id: str = typer.Option(None, "--location-id", help="Unique identifier for the location."),
76
+ extension: str = typer.Option(None, "--extension", help="Search (Contains) based on extension."),
77
+ usage_type: str = typer.Option(None, "--usage-type", help="Choices: DEVICE_OWNER, SHARED_LINE"),
78
+ order: str = typer.Option(None, "--order", help="Sort the list of available members on the device in ascendin"),
79
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
80
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
81
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
82
+ debug: bool = typer.Option(False, "--debug"),
83
+ ):
84
+ """Search Members."""
85
+ api = get_api(debug=debug)
86
+ url = f"https://webexapis.com/v1/telephony/config/devices/{device_id}/availableMembers"
87
+ params = {}
88
+ if member_name is not None:
89
+ params["memberName"] = member_name
90
+ if phone_number is not None:
91
+ params["phoneNumber"] = phone_number
92
+ if location_id is not None:
93
+ params["locationId"] = location_id
94
+ if extension is not None:
95
+ params["extension"] = extension
96
+ if usage_type is not None:
97
+ params["usageType"] = usage_type
98
+ if order is not None:
99
+ params["order"] = order
100
+ if limit > 0:
101
+ params["max"] = limit
102
+ if offset > 0:
103
+ params["start"] = offset
104
+ org_id = get_org_id()
105
+ if org_id is not None:
106
+ params["orgId"] = org_id
107
+ result = None
108
+ try:
109
+ result = api.session.rest_get(url, params=params)
110
+ except WebexError as e:
111
+ handle_rest_error(e)
112
+ result = result or []
113
+ items = result.get("members", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
114
+ if output == "json":
115
+ print_json(items)
116
+ else:
117
+ print_table(items, columns=[('ID', 'id'), ('First Name', 'firstName'), ('Last Name', 'lastName'), ('Phone Number', 'phoneNumber'), ('Member Type', 'memberType')], limit=limit)
118
+
119
+
120
+
121
+ @app.command("show")
122
+ def show(
123
+ device_id: str = typer.Argument(help="deviceId"),
124
+ member_name: str = typer.Option(None, "--member-name", help="Search (Contains) numbers based on member name."),
125
+ phone_number: str = typer.Option(None, "--phone-number", help="Search (Contains) based on number."),
126
+ location_id: str = typer.Option(None, "--location-id", help="Unique identifier for the location."),
127
+ extension: str = typer.Option(None, "--extension", help="Search (Contains) based on extension."),
128
+ usage_type: str = typer.Option(None, "--usage-type", help="Choices: DEVICE_OWNER, SHARED_LINE"),
129
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
130
+ debug: bool = typer.Option(False, "--debug"),
131
+ ):
132
+ """Get Count of Members."""
133
+ api = get_api(debug=debug)
134
+ url = f"https://webexapis.com/v1/telephony/config/devices/{device_id}/availableMembers/count"
135
+ params = {}
136
+ if member_name is not None:
137
+ params["memberName"] = member_name
138
+ if phone_number is not None:
139
+ params["phoneNumber"] = phone_number
140
+ if location_id is not None:
141
+ params["locationId"] = location_id
142
+ if extension is not None:
143
+ params["extension"] = extension
144
+ if usage_type is not None:
145
+ params["usageType"] = usage_type
146
+ org_id = get_org_id()
147
+ if org_id is not None:
148
+ params["orgId"] = org_id
149
+ try:
150
+ result = api.session.rest_get(url, params=params)
151
+ except WebexError as e:
152
+ handle_rest_error(e)
153
+ if output == "json":
154
+ print_json(result)
155
+ else:
156
+ if isinstance(result, dict):
157
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
158
+ elif isinstance(result, list):
159
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
160
+ else:
161
+ print_json(result)
162
+
163
+
164
+
165
+ @app.command("show-count-available-members")
166
+ def show_count_available_members(
167
+ member_name: str = typer.Option(None, "--member-name", help="Search (Contains) numbers based on member name."),
168
+ phone_number: str = typer.Option(None, "--phone-number", help="Search (Contains) based on number."),
169
+ location_id: str = typer.Option(None, "--location-id", help="Unique identifier for the location."),
170
+ extension: str = typer.Option(None, "--extension", help="Search (Contains) based on extension."),
171
+ usage_type: str = typer.Option(None, "--usage-type", help="Choices: DEVICE_OWNER, SHARED_LINE"),
172
+ exclude_virtual_line: str = typer.Option(None, "--exclude-virtual-line", help="If true, filters out virtual lines from the available member"),
173
+ device_location_id: str = typer.Option(None, "--device-location-id", help="Unique identifier for the device's location. When specified,"),
174
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
175
+ debug: bool = typer.Option(False, "--debug"),
176
+ ):
177
+ """Get Count of Available Members."""
178
+ api = get_api(debug=debug)
179
+ url = f"https://webexapis.com/v1/telephony/config/devices/availableMembers/count"
180
+ params = {}
181
+ if member_name is not None:
182
+ params["memberName"] = member_name
183
+ if phone_number is not None:
184
+ params["phoneNumber"] = phone_number
185
+ if location_id is not None:
186
+ params["locationId"] = location_id
187
+ if extension is not None:
188
+ params["extension"] = extension
189
+ if usage_type is not None:
190
+ params["usageType"] = usage_type
191
+ if exclude_virtual_line is not None:
192
+ params["excludeVirtualLine"] = exclude_virtual_line
193
+ if device_location_id is not None:
194
+ params["deviceLocationId"] = device_location_id
195
+ org_id = get_org_id()
196
+ if org_id is not None:
197
+ params["orgId"] = org_id
198
+ try:
199
+ result = api.session.rest_get(url, params=params)
200
+ except WebexError as e:
201
+ handle_rest_error(e)
202
+ if output == "json":
203
+ print_json(result)
204
+ else:
205
+ if isinstance(result, dict):
206
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
207
+ elif isinstance(result, list):
208
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
209
+ else:
210
+ print_json(result)
211
+
212
+
213
+
214
+ @app.command("apply-changes-for")
215
+ def apply_changes_for(
216
+ device_id: str = typer.Argument(help="deviceId"),
217
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
218
+ debug: bool = typer.Option(False, "--debug"),
219
+ ):
220
+ """Apply Changes for a specific device."""
221
+ api = get_api(debug=debug)
222
+ url = f"https://webexapis.com/v1/telephony/config/devices/{device_id}/actions/applyChanges/invoke"
223
+ params = {}
224
+ org_id = get_org_id()
225
+ if org_id is not None:
226
+ params["orgId"] = org_id
227
+ if json_body:
228
+ body = json.loads(json_body)
229
+ else:
230
+ body = {}
231
+ try:
232
+ result = api.session.rest_post(url, json=body, params=params)
233
+ except WebexError as e:
234
+ handle_rest_error(e)
235
+ print_json(result)
236
+
237
+
238
+
239
+ @app.command("show-settings-devices")
240
+ def show_settings_devices(
241
+ device_id: str = typer.Argument(help="deviceId"),
242
+ device_model: str = typer.Option(None, "--device-model", help="The model type of the device. The corresponding device model"),
243
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
244
+ debug: bool = typer.Option(False, "--debug"),
245
+ ):
246
+ """Get Device Settings."""
247
+ api = get_api(debug=debug)
248
+ url = f"https://webexapis.com/v1/telephony/config/devices/{device_id}/settings"
249
+ params = {}
250
+ if device_model is not None:
251
+ params["deviceModel"] = device_model
252
+ org_id = get_org_id()
253
+ if org_id is not None:
254
+ params["orgId"] = org_id
255
+ try:
256
+ result = api.session.rest_get(url, params=params)
257
+ except WebexError as e:
258
+ handle_rest_error(e)
259
+ if output == "json":
260
+ print_json(result)
261
+ else:
262
+ if isinstance(result, dict):
263
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
264
+ elif isinstance(result, list):
265
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
266
+ else:
267
+ print_json(result)
268
+
269
+
270
+
271
+ @app.command("update-settings-devices")
272
+ def update_settings_devices(
273
+ device_id: str = typer.Argument(help="deviceId"),
274
+ device_model: str = typer.Option(None, "--device-model", help="The model type of the device. The corresponding device model"),
275
+ custom_enabled: bool = typer.Option(None, "--custom-enabled/--no-custom-enabled", help="Indicates if customization is allowed at a device level. If"),
276
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
277
+ debug: bool = typer.Option(False, "--debug"),
278
+ ):
279
+ """Update device settings\n\nExample --json-body:\n '{"customizations":{"ata":{"audioCodecPriority":"...","ataDtmfMode":"...","ataDtmfMethod":"...","cdpEnabled":"...","lldpEnabled":"...","qosEnabled":"...","vlan":"...","webAccessEnabled":"..."},"mpp":{"pnacEnabled":"...","audioCodecPriority":"...","backlightTimer":"...","background":"...","cdpEnabled":"...","defaultLoggingLevel":"...","dndServicesEnabled":"...","acd":"..."},"wifi":{"audioCodecPriority":"...","ldap":"...","webAccess":"...","phoneSecurityPwd":"..."}},"customEnabled":true}'."""
280
+ api = get_api(debug=debug)
281
+ url = f"https://webexapis.com/v1/telephony/config/devices/{device_id}/settings"
282
+ params = {}
283
+ if device_model is not None:
284
+ params["deviceModel"] = device_model
285
+ org_id = get_org_id()
286
+ if org_id is not None:
287
+ params["orgId"] = org_id
288
+ if json_body:
289
+ body = json.loads(json_body)
290
+ else:
291
+ body = {}
292
+ if custom_enabled is not None:
293
+ body["customEnabled"] = custom_enabled
294
+ try:
295
+ result = api.session.rest_put(url, json=body, params=params)
296
+ except WebexError as e:
297
+ handle_rest_error(e)
298
+ typer.echo(f"Updated.")
299
+
300
+
301
+
302
+ @app.command("show-settings-devices-1")
303
+ def show_settings_devices_1(
304
+ location_id: str = typer.Argument(help="locationId"),
305
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
306
+ debug: bool = typer.Option(False, "--debug"),
307
+ ):
308
+ """Get Location Device Settings."""
309
+ api = get_api(debug=debug)
310
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/devices/settings"
311
+ params = {}
312
+ org_id = get_org_id()
313
+ if org_id is not None:
314
+ params["orgId"] = org_id
315
+ try:
316
+ result = api.session.rest_get(url, params=params)
317
+ except WebexError as e:
318
+ handle_rest_error(e)
319
+ if output == "json":
320
+ print_json(result)
321
+ else:
322
+ if isinstance(result, dict):
323
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
324
+ elif isinstance(result, list):
325
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
326
+ else:
327
+ print_json(result)
328
+
329
+
330
+
331
+ @app.command("show-devices")
332
+ def show_devices(
333
+ device_id: str = typer.Argument(help="deviceId"),
334
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
335
+ debug: bool = typer.Option(False, "--debug"),
336
+ ):
337
+ """Get Webex Calling Device Details."""
338
+ api = get_api(debug=debug)
339
+ url = f"https://webexapis.com/v1/telephony/config/devices/{device_id}"
340
+ params = {}
341
+ org_id = get_org_id()
342
+ if org_id is not None:
343
+ params["orgId"] = org_id
344
+ try:
345
+ result = api.session.rest_get(url, params=params)
346
+ except WebexError as e:
347
+ handle_rest_error(e)
348
+ if output == "json":
349
+ print_json(result)
350
+ else:
351
+ if isinstance(result, dict):
352
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
353
+ elif isinstance(result, list):
354
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
355
+ else:
356
+ print_json(result)
357
+
358
+
359
+
360
+ @app.command("update-devices-config")
361
+ def update_devices_config(
362
+ device_id: str = typer.Argument(help="deviceId"),
363
+ sip_password: str = typer.Option(None, "--sip-password", help="Password to be updated."),
364
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
365
+ debug: bool = typer.Option(False, "--debug"),
366
+ ):
367
+ """Update Third Party Device."""
368
+ api = get_api(debug=debug)
369
+ url = f"https://webexapis.com/v1/telephony/config/devices/{device_id}"
370
+ params = {}
371
+ org_id = get_org_id()
372
+ if org_id is not None:
373
+ params["orgId"] = org_id
374
+ if json_body:
375
+ body = json.loads(json_body)
376
+ else:
377
+ body = {}
378
+ if sip_password is not None:
379
+ body["sipPassword"] = sip_password
380
+ try:
381
+ result = api.session.rest_put(url, json=body, params=params)
382
+ except WebexError as e:
383
+ handle_rest_error(e)
384
+ typer.echo(f"Updated.")
385
+
386
+
387
+
388
+ @app.command("list-devices-people")
389
+ def list_devices_people(
390
+ person_id: str = typer.Argument(help="personId"),
391
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
392
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
393
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
394
+ debug: bool = typer.Option(False, "--debug"),
395
+ ):
396
+ """Get Person Devices."""
397
+ api = get_api(debug=debug)
398
+ url = f"https://webexapis.com/v1/telephony/config/people/{person_id}/devices"
399
+ params = {}
400
+ if limit > 0:
401
+ params["max"] = limit
402
+ if offset > 0:
403
+ params["start"] = offset
404
+ org_id = get_org_id()
405
+ if org_id is not None:
406
+ params["orgId"] = org_id
407
+ result = None
408
+ try:
409
+ result = api.session.rest_get(url, params=params)
410
+ except WebexError as e:
411
+ handle_rest_error(e)
412
+ result = result or []
413
+ items = result.get("devices", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
414
+ if output == "json":
415
+ print_json(items)
416
+ else:
417
+ print_table(items, columns=[('ID', 'id'), ('Model', 'model'), ('MAC', 'mac'), ('Type', 'type')], limit=limit)
418
+
419
+
420
+
421
+ @app.command("update-hoteling")
422
+ def update_hoteling(
423
+ person_id: str = typer.Argument(help="personId"),
424
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
425
+ debug: bool = typer.Option(False, "--debug"),
426
+ ):
427
+ """Modify Hoteling Settings for a Person's Primary Devices\n\nExample --json-body:\n '{"hoteling":{"enabled":true,"limitGuestUse":true,"guestHoursLimit":0}}'."""
428
+ api = get_api(debug=debug)
429
+ url = f"https://webexapis.com/v1/telephony/config/people/{person_id}/devices/settings/hoteling"
430
+ params = {}
431
+ org_id = get_org_id()
432
+ if org_id is not None:
433
+ params["orgId"] = org_id
434
+ if json_body:
435
+ body = json.loads(json_body)
436
+ else:
437
+ body = {}
438
+ try:
439
+ result = api.session.rest_put(url, json=body, params=params)
440
+ except WebexError as e:
441
+ handle_rest_error(e)
442
+ typer.echo(f"Updated.")
443
+
444
+
445
+
446
+ @app.command("list-devices-workspaces")
447
+ def list_devices_workspaces(
448
+ workspace_id: str = typer.Argument(help="workspaceId"),
449
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
450
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
451
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
452
+ debug: bool = typer.Option(False, "--debug"),
453
+ ):
454
+ """Get Workspace Devices."""
455
+ api = get_api(debug=debug)
456
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/devices"
457
+ params = {}
458
+ if limit > 0:
459
+ params["max"] = limit
460
+ if offset > 0:
461
+ params["start"] = offset
462
+ org_id = get_org_id()
463
+ if org_id is not None:
464
+ params["orgId"] = org_id
465
+ result = None
466
+ try:
467
+ result = api.session.rest_get(url, params=params)
468
+ except WebexError as e:
469
+ handle_rest_error(e)
470
+ result = result or []
471
+ items = result.get("devices", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
472
+ if output == "json":
473
+ print_json(items)
474
+ else:
475
+ print_table(items, columns=[('ID', 'id'), ('Model', 'model'), ('MAC', 'mac'), ('Type', 'type')], limit=limit)
476
+
477
+
478
+
479
+ @app.command("update-devices-workspaces")
480
+ def update_devices_workspaces(
481
+ workspace_id: str = typer.Argument(help="workspaceId"),
482
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Enable/Disable hoteling Host. Enabling the device for hoteli"),
483
+ limit_guest_use: bool = typer.Option(None, "--limit-guest-use/--no-limit-guest-use", help="Enable limiting the time a guest can use the device. The tim"),
484
+ guest_hours_limit: str = typer.Option(None, "--guest-hours-limit", help="Time Limit in hours until hoteling is enabled. Mandatory if"),
485
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
486
+ debug: bool = typer.Option(False, "--debug"),
487
+ ):
488
+ """Modify Workspace Devices."""
489
+ api = get_api(debug=debug)
490
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/devices"
491
+ params = {}
492
+ org_id = get_org_id()
493
+ if org_id is not None:
494
+ params["orgId"] = org_id
495
+ if json_body:
496
+ body = json.loads(json_body)
497
+ else:
498
+ body = {}
499
+ if enabled is not None:
500
+ body["enabled"] = enabled
501
+ if limit_guest_use is not None:
502
+ body["limitGuestUse"] = limit_guest_use
503
+ if guest_hours_limit is not None:
504
+ body["guestHoursLimit"] = guest_hours_limit
505
+ try:
506
+ result = api.session.rest_put(url, json=body, params=params)
507
+ except WebexError as e:
508
+ handle_rest_error(e)
509
+ typer.echo(f"Updated.")
510
+
511
+
512
+
513
+ @app.command("show-settings-devices-2")
514
+ def show_settings_devices_2(
515
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
516
+ debug: bool = typer.Option(False, "--debug"),
517
+ ):
518
+ """Read the device override settings for a organization."""
519
+ api = get_api(debug=debug)
520
+ url = f"https://webexapis.com/v1/telephony/config/devices/settings"
521
+ params = {}
522
+ org_id = get_org_id()
523
+ if org_id is not None:
524
+ params["orgId"] = org_id
525
+ try:
526
+ result = api.session.rest_get(url, params=params)
527
+ except WebexError as e:
528
+ handle_rest_error(e)
529
+ if output == "json":
530
+ print_json(result)
531
+ else:
532
+ if isinstance(result, dict):
533
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
534
+ elif isinstance(result, list):
535
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
536
+ else:
537
+ print_json(result)
538
+
539
+
540
+
541
+ @app.command("list-line-key-templates")
542
+ def list_line_key_templates(
543
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
544
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
545
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
546
+ debug: bool = typer.Option(False, "--debug"),
547
+ ):
548
+ """Read the list of Line Key Templates."""
549
+ api = get_api(debug=debug)
550
+ url = f"https://webexapis.com/v1/telephony/config/devices/lineKeyTemplates"
551
+ params = {}
552
+ if limit > 0:
553
+ params["max"] = limit
554
+ if offset > 0:
555
+ params["start"] = offset
556
+ org_id = get_org_id()
557
+ if org_id is not None:
558
+ params["orgId"] = org_id
559
+ result = None
560
+ try:
561
+ result = api.session.rest_get(url, params=params)
562
+ except WebexError as e:
563
+ handle_rest_error(e)
564
+ result = result or []
565
+ items = result.get("lineKeyTemplates", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
566
+ if output == "json":
567
+ print_json(items)
568
+ else:
569
+ print_table(items, columns=[('ID', 'id'), ('Template Name', 'templateName'), ('Device Model', 'deviceModel'), ('Display Name', 'modelDisplayName')], limit=limit)
570
+
571
+
572
+
573
+ @app.command("create")
574
+ def create(
575
+ template_name: str = typer.Option(None, "--template-name", help="(required) Name of the Line Key Template."),
576
+ device_model: str = typer.Option(None, "--device-model", help="(required) The model of the device for which the Line Key Template is a"),
577
+ user_reorder_enabled: bool = typer.Option(None, "--user-reorder-enabled/--no-user-reorder-enabled", help="User Customization Enabled."),
578
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
579
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
580
+ debug: bool = typer.Option(False, "--debug"),
581
+ ):
582
+ """Create a Line Key Template\n\nExample --json-body:\n '{"templateName":"...","deviceModel":"...","lineKeys":[{"lineKeyIndex":"...","lineKeyType":"...","sharedLineIndex":"...","lineKeyLabel":"...","lineKeyValue":"..."}],"userReorderEnabled":true}'."""
583
+ api = get_api(debug=debug)
584
+ url = f"https://webexapis.com/v1/telephony/config/devices/lineKeyTemplates"
585
+ params = {}
586
+ org_id = get_org_id()
587
+ if org_id is not None:
588
+ params["orgId"] = org_id
589
+ if json_body:
590
+ body = json.loads(json_body)
591
+ else:
592
+ body = {}
593
+ if template_name is not None:
594
+ body["templateName"] = template_name
595
+ if device_model is not None:
596
+ body["deviceModel"] = device_model
597
+ if user_reorder_enabled is not None:
598
+ body["userReorderEnabled"] = user_reorder_enabled
599
+ _missing = [f for f in ['templateName', 'deviceModel'] if f not in body or body[f] is None]
600
+ if _missing:
601
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
602
+ raise typer.Exit(1)
603
+ try:
604
+ result = api.session.rest_post(url, json=body, params=params)
605
+ except WebexError as e:
606
+ handle_rest_error(e)
607
+ if output == "json":
608
+ print_json(result)
609
+ elif isinstance(result, dict) and "id" in result:
610
+ typer.echo(f"Created: {result['id']}")
611
+ elif not result or result == {}:
612
+ typer.echo("Created.")
613
+ else:
614
+ print_json(result)
615
+
616
+
617
+
618
+ @app.command("show-line-key-templates")
619
+ def show_line_key_templates(
620
+ template_id: str = typer.Argument(help="templateId"),
621
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
622
+ debug: bool = typer.Option(False, "--debug"),
623
+ ):
624
+ """Get details of a Line Key Template."""
625
+ api = get_api(debug=debug)
626
+ url = f"https://webexapis.com/v1/telephony/config/devices/lineKeyTemplates/{template_id}"
627
+ params = {}
628
+ org_id = get_org_id()
629
+ if org_id is not None:
630
+ params["orgId"] = org_id
631
+ try:
632
+ result = api.session.rest_get(url, params=params)
633
+ except WebexError as e:
634
+ handle_rest_error(e)
635
+ if output == "json":
636
+ print_json(result)
637
+ else:
638
+ if isinstance(result, dict):
639
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
640
+ elif isinstance(result, list):
641
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
642
+ else:
643
+ print_json(result)
644
+
645
+
646
+
647
+ @app.command("update-line-key-templates")
648
+ def update_line_key_templates(
649
+ template_id: str = typer.Argument(help="templateId"),
650
+ user_reorder_enabled: bool = typer.Option(None, "--user-reorder-enabled/--no-user-reorder-enabled", help="Indicates whether the user can reorder the line keys."),
651
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
652
+ debug: bool = typer.Option(False, "--debug"),
653
+ ):
654
+ """Modify a Line Key Template\n\nExample --json-body:\n '{"lineKeys":[{"lineKeyIndex":"...","lineKeyType":"...","sharedLineIndex":"...","lineKeyLabel":"...","lineKeyValue":"..."}],"userReorderEnabled":true}'."""
655
+ api = get_api(debug=debug)
656
+ url = f"https://webexapis.com/v1/telephony/config/devices/lineKeyTemplates/{template_id}"
657
+ params = {}
658
+ org_id = get_org_id()
659
+ if org_id is not None:
660
+ params["orgId"] = org_id
661
+ if json_body:
662
+ body = json.loads(json_body)
663
+ else:
664
+ body = {}
665
+ if user_reorder_enabled is not None:
666
+ body["userReorderEnabled"] = user_reorder_enabled
667
+ try:
668
+ result = api.session.rest_put(url, json=body, params=params)
669
+ except WebexError as e:
670
+ handle_rest_error(e)
671
+ typer.echo(f"Updated.")
672
+
673
+
674
+
675
+ @app.command("delete")
676
+ def delete(
677
+ template_id: str = typer.Argument(help="templateId"),
678
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
679
+ debug: bool = typer.Option(False, "--debug"),
680
+ ):
681
+ """Delete a Line Key Template."""
682
+ if not force:
683
+ typer.confirm(f"Delete {template_id}?", abort=True)
684
+ api = get_api(debug=debug)
685
+ url = f"https://webexapis.com/v1/telephony/config/devices/lineKeyTemplates/{template_id}"
686
+ params = {}
687
+ org_id = get_org_id()
688
+ if org_id is not None:
689
+ params["orgId"] = org_id
690
+ try:
691
+ api.session.rest_delete(url, params=params)
692
+ except WebexError as e:
693
+ handle_rest_error(e)
694
+ typer.echo(f"Deleted: {template_id}")
695
+
696
+
697
+
698
+ @app.command("preview-apply-line")
699
+ def preview_apply_line(
700
+ action: str = typer.Option(None, "--action", help="Choices: APPLY_TEMPLATE, APPLY_DEFAULT_TEMPLATES"),
701
+ template_id: str = typer.Option(None, "--template-id", help="`templateId` is required for `APPLY_TEMPLATE` action."),
702
+ exclude_devices_with_custom_layout: str = typer.Option(None, "--exclude-devices-with-custom-layout", help="Indicates whether to exclude devices with custom layout."),
703
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
704
+ debug: bool = typer.Option(False, "--debug"),
705
+ ):
706
+ """Preview Apply Line Key Template\n\nExample --json-body:\n '{"action":"APPLY_TEMPLATE","templateId":"...","locationIds":["..."],"excludeDevicesWithCustomLayout":true,"includeDeviceTags":["..."],"excludeDeviceTags":["..."],"advisoryTypes":{"moreSharedAppearancesEnabled":true,"fewSharedAppearancesEnabled":true,"moreMonitorAppearancesEnabled":true,"moreCPEAppearancesEnabled":true,"moreModeManagementAppearancesEnabled":true}}'."""
707
+ api = get_api(debug=debug)
708
+ url = f"https://webexapis.com/v1/telephony/config/devices/actions/previewApplyLineKeyTemplate/invoke"
709
+ params = {}
710
+ org_id = get_org_id()
711
+ if org_id is not None:
712
+ params["orgId"] = org_id
713
+ if json_body:
714
+ body = json.loads(json_body)
715
+ else:
716
+ body = {}
717
+ if action is not None:
718
+ body["action"] = action
719
+ if template_id is not None:
720
+ body["templateId"] = template_id
721
+ if exclude_devices_with_custom_layout is not None:
722
+ body["excludeDevicesWithCustomLayout"] = exclude_devices_with_custom_layout
723
+ try:
724
+ result = api.session.rest_post(url, json=body, params=params)
725
+ except WebexError as e:
726
+ handle_rest_error(e)
727
+ print_json(result)
728
+
729
+
730
+
731
+ @app.command("list-apply-line-key-template")
732
+ def list_apply_line_key_template(
733
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
734
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
735
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
736
+ debug: bool = typer.Option(False, "--debug"),
737
+ ):
738
+ """Get List of Apply Line Key Template jobs."""
739
+ api = get_api(debug=debug)
740
+ url = f"https://webexapis.com/v1/telephony/config/jobs/devices/applyLineKeyTemplate"
741
+ params = {}
742
+ if limit > 0:
743
+ params["max"] = limit
744
+ if offset > 0:
745
+ params["start"] = offset
746
+ org_id = get_org_id()
747
+ if org_id is not None:
748
+ params["orgId"] = org_id
749
+ result = None
750
+ try:
751
+ result = api.session.rest_get(url, params=params)
752
+ except WebexError as e:
753
+ handle_rest_error(e)
754
+ result = result or []
755
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
756
+ if output == "json":
757
+ print_json(items)
758
+ else:
759
+ print_table(items, columns=[('ID', 'id'), ('Status', 'latestExecutionStatus')], limit=limit)
760
+
761
+
762
+
763
+ @app.command("create-apply-line-key-template")
764
+ def create_apply_line_key_template(
765
+ action: str = typer.Option(None, "--action", help="(required) Choices: APPLY_TEMPLATE, APPLY_DEFAULT_TEMPLATES"),
766
+ template_id: str = typer.Option(None, "--template-id", help="(required) `templateId` is required for `APPLY_TEMPLATE` action."),
767
+ exclude_devices_with_custom_layout: bool = typer.Option(None, "--exclude-devices-with-custom-layout/--no-exclude-devices-with-custom-layout", help="Indicates whether to exclude devices with custom layout."),
768
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
769
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
770
+ debug: bool = typer.Option(False, "--debug"),
771
+ ):
772
+ """Apply a Line Key Template\n\nExample --json-body:\n '{"action":"APPLY_TEMPLATE","templateId":"...","locationIds":["..."],"excludeDevicesWithCustomLayout":true,"includeDeviceTags":["..."],"excludeDeviceTags":["..."],"advisoryTypes":{"moreSharedAppearancesEnabled":true,"fewSharedAppearancesEnabled":true,"moreMonitorAppearancesEnabled":true,"moreCPEAppearancesEnabled":true,"moreModeManagementAppearancesEnabled":true}}'."""
773
+ api = get_api(debug=debug)
774
+ url = f"https://webexapis.com/v1/telephony/config/jobs/devices/applyLineKeyTemplate"
775
+ params = {}
776
+ org_id = get_org_id()
777
+ if org_id is not None:
778
+ params["orgId"] = org_id
779
+ if json_body:
780
+ body = json.loads(json_body)
781
+ else:
782
+ body = {}
783
+ if action is not None:
784
+ body["action"] = action
785
+ if template_id is not None:
786
+ body["templateId"] = template_id
787
+ if exclude_devices_with_custom_layout is not None:
788
+ body["excludeDevicesWithCustomLayout"] = exclude_devices_with_custom_layout
789
+ _missing = [f for f in ['action', 'templateId'] if f not in body or body[f] is None]
790
+ if _missing:
791
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
792
+ raise typer.Exit(1)
793
+ try:
794
+ result = api.session.rest_post(url, json=body, params=params)
795
+ except WebexError as e:
796
+ handle_rest_error(e)
797
+ if output == "json":
798
+ print_json(result)
799
+ elif isinstance(result, dict) and "id" in result:
800
+ typer.echo(f"Created: {result['id']}")
801
+ elif not result or result == {}:
802
+ typer.echo("Created.")
803
+ else:
804
+ print_json(result)
805
+
806
+
807
+
808
+ @app.command("show-apply-line-key-template")
809
+ def show_apply_line_key_template(
810
+ job_id: str = typer.Argument(help="jobId"),
811
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
812
+ debug: bool = typer.Option(False, "--debug"),
813
+ ):
814
+ """Get the job status of an Apply Line Key Template job."""
815
+ api = get_api(debug=debug)
816
+ url = f"https://webexapis.com/v1/telephony/config/jobs/devices/applyLineKeyTemplate/{job_id}"
817
+ params = {}
818
+ org_id = get_org_id()
819
+ if org_id is not None:
820
+ params["orgId"] = org_id
821
+ try:
822
+ result = api.session.rest_get(url, params=params)
823
+ except WebexError as e:
824
+ handle_rest_error(e)
825
+ if output == "json":
826
+ print_json(result)
827
+ else:
828
+ if isinstance(result, dict):
829
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
830
+ elif isinstance(result, list):
831
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
832
+ else:
833
+ print_json(result)
834
+
835
+
836
+
837
+ @app.command("show-errors")
838
+ def show_errors(
839
+ job_id: str = typer.Argument(help="jobId"),
840
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
841
+ debug: bool = typer.Option(False, "--debug"),
842
+ ):
843
+ """Get job errors for an Apply Line Key Template job."""
844
+ api = get_api(debug=debug)
845
+ url = f"https://webexapis.com/v1/telephony/config/jobs/devices/applyLineKeyTemplate/{job_id}/errors"
846
+ params = {}
847
+ org_id = get_org_id()
848
+ if org_id is not None:
849
+ params["orgId"] = org_id
850
+ try:
851
+ result = api.session.rest_get(url, params=params)
852
+ except WebexError as e:
853
+ handle_rest_error(e)
854
+ if output == "json":
855
+ print_json(result)
856
+ else:
857
+ if isinstance(result, dict):
858
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
859
+ elif isinstance(result, list):
860
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
861
+ else:
862
+ print_json(result)
863
+
864
+
865
+
866
+ @app.command("list-supported-devices-dects")
867
+ def list_supported_devices_dects(
868
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
869
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
870
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
871
+ debug: bool = typer.Option(False, "--debug"),
872
+ ):
873
+ """Read the DECT device type list - Deprecated."""
874
+ api = get_api(debug=debug)
875
+ url = f"https://webexapis.com/v1/telephony/config/devices/dects/supportedDevices"
876
+ params = {}
877
+ if limit > 0:
878
+ params["max"] = limit
879
+ if offset > 0:
880
+ params["start"] = offset
881
+ org_id = get_org_id()
882
+ if org_id is not None:
883
+ params["orgId"] = org_id
884
+ try:
885
+ if limit > 0:
886
+ result = api.session.rest_get(url, params=params)
887
+ result = result or {}
888
+ items = result.get("devices", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
889
+ else:
890
+ if "max" not in params:
891
+ params["max"] = 1000
892
+ items = list(api.session.follow_pagination(url=url, params=params, item_key="devices"))
893
+ except WebexError as e:
894
+ handle_rest_error(e)
895
+ if output == "json":
896
+ print_json(items)
897
+ else:
898
+ print_table(items, columns=[('Model', 'model'), ('Display Name', 'displayName'), ('Base Stations', 'numberOfBaseStations')], limit=limit)
899
+
900
+
901
+
902
+ @app.command("list-supported-devices-dect-networks")
903
+ def list_supported_devices_dect_networks(
904
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
905
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
906
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
907
+ debug: bool = typer.Option(False, "--debug"),
908
+ ):
909
+ """Read the DECT device type list."""
910
+ api = get_api(debug=debug)
911
+ url = f"https://webexapis.com/v1/telephony/config/devices/dectNetworks/supportedDevices"
912
+ params = {}
913
+ if limit > 0:
914
+ params["max"] = limit
915
+ if offset > 0:
916
+ params["start"] = offset
917
+ org_id = get_org_id()
918
+ if org_id is not None:
919
+ params["orgId"] = org_id
920
+ try:
921
+ if limit > 0:
922
+ result = api.session.rest_get(url, params=params)
923
+ result = result or {}
924
+ items = result.get("devices", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
925
+ else:
926
+ if "max" not in params:
927
+ params["max"] = 1000
928
+ items = list(api.session.follow_pagination(url=url, params=params, item_key="devices"))
929
+ except WebexError as e:
930
+ handle_rest_error(e)
931
+ if output == "json":
932
+ print_json(items)
933
+ else:
934
+ print_table(items, columns=[('Model', 'model'), ('Display Name', 'displayName'), ('Base Stations', 'numberOfBaseStations')], limit=limit)
935
+
936
+
937
+
938
+ @app.command("validate-a-list")
939
+ def validate_a_list(
940
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
941
+ debug: bool = typer.Option(False, "--debug"),
942
+ ):
943
+ """Validate a list of MAC address\n\nExample --json-body:\n '{"macs":["..."]}'."""
944
+ api = get_api(debug=debug)
945
+ url = f"https://webexapis.com/v1/telephony/config/devices/actions/validateMacs/invoke"
946
+ params = {}
947
+ org_id = get_org_id()
948
+ if org_id is not None:
949
+ params["orgId"] = org_id
950
+ if json_body:
951
+ body = json.loads(json_body)
952
+ else:
953
+ body = {}
954
+ try:
955
+ result = api.session.rest_post(url, json=body, params=params)
956
+ except WebexError as e:
957
+ handle_rest_error(e)
958
+ print_json(result)
959
+
960
+
961
+
962
+ @app.command("list-call-device-settings")
963
+ def list_call_device_settings(
964
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
965
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
966
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
967
+ debug: bool = typer.Option(False, "--debug"),
968
+ ):
969
+ """List Change Device Settings Jobs."""
970
+ api = get_api(debug=debug)
971
+ url = f"https://webexapis.com/v1/telephony/config/jobs/devices/callDeviceSettings"
972
+ params = {}
973
+ if limit > 0:
974
+ params["max"] = limit
975
+ if offset > 0:
976
+ params["start"] = offset
977
+ org_id = get_org_id()
978
+ if org_id is not None:
979
+ params["orgId"] = org_id
980
+ try:
981
+ if limit > 0:
982
+ result = api.session.rest_get(url, params=params)
983
+ result = result or {}
984
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
985
+ else:
986
+ items = list(api.session.follow_pagination(url=url, params=params, item_key="items"))
987
+ except WebexError as e:
988
+ handle_rest_error(e)
989
+ if output == "json":
990
+ print_json(items)
991
+ else:
992
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
993
+
994
+
995
+
996
+ @app.command("create-call-device-settings")
997
+ def create_call_device_settings(
998
+ location_id: str = typer.Option(None, "--location-id", help="Location within an organization where changes of device seti"),
999
+ location_customizations_enabled: bool = typer.Option(None, "--location-customizations-enabled/--no-location-customizations-enabled", help="Indicates if all the devices within this location will be cu"),
1000
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1001
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1002
+ debug: bool = typer.Option(False, "--debug"),
1003
+ ):
1004
+ """Change Device Settings Across Organization Or Location Job\n\nExample --json-body:\n '{"locationId":"...","locationCustomizationsEnabled":true,"customizations":{"ata":{"audioCodecPriority":"...","ataDtmfMode":"...","ataDtmfMethod":"...","cdpEnabled":"...","lldpEnabled":"...","qosEnabled":"...","vlan":"...","webAccessEnabled":"..."},"dect":{"audioCodecPriority":"...","cdpEnabled":"...","dect6825HandsetEmergencyNumber":"...","lldpEnabled":"...","multicast":"...","qosEnabled":"...","vlan":"...","webAccessEnabled":"..."},"mpp":{"pnacEnabled":"...","audioCodecPriority":"...","backlightTimer":"...","background":"...","cdpEnabled":"...","defaultLoggingLevel":"...","dndServicesEnabled":"...","acd":"..."},"wifi":{"audioCodecPriority":"...","ldap":"...","webAccess":"...","phoneSecurityPwd":"..."}}}'."""
1005
+ api = get_api(debug=debug)
1006
+ url = f"https://webexapis.com/v1/telephony/config/jobs/devices/callDeviceSettings"
1007
+ params = {}
1008
+ org_id = get_org_id()
1009
+ if org_id is not None:
1010
+ params["orgId"] = org_id
1011
+ if json_body:
1012
+ body = json.loads(json_body)
1013
+ else:
1014
+ body = {}
1015
+ if location_id is not None:
1016
+ body["locationId"] = location_id
1017
+ if location_customizations_enabled is not None:
1018
+ body["locationCustomizationsEnabled"] = location_customizations_enabled
1019
+ try:
1020
+ result = api.session.rest_post(url, json=body, params=params)
1021
+ except WebexError as e:
1022
+ handle_rest_error(e)
1023
+ if output == "json":
1024
+ print_json(result)
1025
+ elif isinstance(result, dict) and "id" in result:
1026
+ typer.echo(f"Created: {result['id']}")
1027
+ elif not result or result == {}:
1028
+ typer.echo("Created.")
1029
+ else:
1030
+ print_json(result)
1031
+
1032
+
1033
+
1034
+ @app.command("show-call-device-settings")
1035
+ def show_call_device_settings(
1036
+ job_id: str = typer.Argument(help="jobId"),
1037
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1038
+ debug: bool = typer.Option(False, "--debug"),
1039
+ ):
1040
+ """Get Change Device Settings Job Status."""
1041
+ api = get_api(debug=debug)
1042
+ url = f"https://webexapis.com/v1/telephony/config/jobs/devices/callDeviceSettings/{job_id}"
1043
+ try:
1044
+ result = api.session.rest_get(url)
1045
+ except WebexError as e:
1046
+ handle_rest_error(e)
1047
+ if output == "json":
1048
+ print_json(result)
1049
+ else:
1050
+ if isinstance(result, dict):
1051
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1052
+ elif isinstance(result, list):
1053
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1054
+ else:
1055
+ print_json(result)
1056
+
1057
+
1058
+
1059
+ @app.command("list-errors-call-device-settings")
1060
+ def list_errors_call_device_settings(
1061
+ job_id: str = typer.Argument(help="jobId"),
1062
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1063
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1064
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1065
+ debug: bool = typer.Option(False, "--debug"),
1066
+ ):
1067
+ """List Change Device Settings Job Errors."""
1068
+ api = get_api(debug=debug)
1069
+ url = f"https://webexapis.com/v1/telephony/config/jobs/devices/callDeviceSettings/{job_id}/errors"
1070
+ params = {}
1071
+ if limit > 0:
1072
+ params["max"] = limit
1073
+ if offset > 0:
1074
+ params["start"] = offset
1075
+ org_id = get_org_id()
1076
+ if org_id is not None:
1077
+ params["orgId"] = org_id
1078
+ try:
1079
+ if limit > 0:
1080
+ result = api.session.rest_get(url, params=params)
1081
+ result = result or {}
1082
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1083
+ else:
1084
+ items = list(api.session.follow_pagination(url=url, params=params, item_key="items"))
1085
+ except WebexError as e:
1086
+ handle_rest_error(e)
1087
+ if output == "json":
1088
+ print_json(items)
1089
+ else:
1090
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1091
+
1092
+
1093
+
1094
+ @app.command("list-layout")
1095
+ def list_layout(
1096
+ device_id: str = typer.Argument(help="deviceId"),
1097
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1098
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1099
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1100
+ debug: bool = typer.Option(False, "--debug"),
1101
+ ):
1102
+ """Get Device Layout by Device ID."""
1103
+ api = get_api(debug=debug)
1104
+ url = f"https://webexapis.com/v1/telephony/config/devices/{device_id}/layout"
1105
+ params = {}
1106
+ if limit > 0:
1107
+ params["max"] = limit
1108
+ if offset > 0:
1109
+ params["start"] = offset
1110
+ org_id = get_org_id()
1111
+ if org_id is not None:
1112
+ params["orgId"] = org_id
1113
+ result = None
1114
+ try:
1115
+ result = api.session.rest_get(url, params=params)
1116
+ except WebexError as e:
1117
+ handle_rest_error(e)
1118
+ result = result or []
1119
+ items = result.get("lineKeys", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1120
+ if output == "json":
1121
+ print_json(items)
1122
+ else:
1123
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1124
+
1125
+
1126
+
1127
+ @app.command("update-layout")
1128
+ def update_layout(
1129
+ device_id: str = typer.Argument(help="deviceId"),
1130
+ layout_mode: str = typer.Option(None, "--layout-mode", help="Choices: DEFAULT, CUSTOM"),
1131
+ user_reorder_enabled: bool = typer.Option(None, "--user-reorder-enabled/--no-user-reorder-enabled", help="If `true`, user customization is enabled."),
1132
+ kem_module_type: str = typer.Option(None, "--kem-module-type", help="Choices: KEM_14_KEYS, KEM_18_KEYS, KEM_20_KEYS"),
1133
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1134
+ debug: bool = typer.Option(False, "--debug"),
1135
+ ):
1136
+ """Modify Device Layout by Device ID\n\nExample --json-body:\n '{"layoutMode":"DEFAULT","lineKeys":[{"lineKeyIndex":"...","lineKeyType":"...","sharedLineIndex":"...","lineKeyLabel":"...","lineKeyValue":"..."}],"userReorderEnabled":true,"kemModuleType":"KEM_14_KEYS","kemKeys":[{"kemModuleIndex":"...","kemKeyIndex":"...","kemKeyType":"...","sharedLineIndex":"...","kemKeyLabel":"...","kemKeyValue":"..."}]}'."""
1137
+ api = get_api(debug=debug)
1138
+ url = f"https://webexapis.com/v1/telephony/config/devices/{device_id}/layout"
1139
+ params = {}
1140
+ org_id = get_org_id()
1141
+ if org_id is not None:
1142
+ params["orgId"] = org_id
1143
+ if json_body:
1144
+ body = json.loads(json_body)
1145
+ else:
1146
+ body = {}
1147
+ if layout_mode is not None:
1148
+ body["layoutMode"] = layout_mode
1149
+ if user_reorder_enabled is not None:
1150
+ body["userReorderEnabled"] = user_reorder_enabled
1151
+ if kem_module_type is not None:
1152
+ body["kemModuleType"] = kem_module_type
1153
+ try:
1154
+ result = api.session.rest_put(url, json=body, params=params)
1155
+ except WebexError as e:
1156
+ handle_rest_error(e)
1157
+ typer.echo(f"Updated.")
1158
+
1159
+
1160
+
1161
+ @app.command("list-rebuild-phones")
1162
+ def list_rebuild_phones(
1163
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1164
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1165
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1166
+ debug: bool = typer.Option(False, "--debug"),
1167
+ ):
1168
+ """List Rebuild Phones Jobs."""
1169
+ api = get_api(debug=debug)
1170
+ url = f"https://webexapis.com/v1/telephony/config/jobs/devices/rebuildPhones"
1171
+ params = {}
1172
+ if limit > 0:
1173
+ params["max"] = limit
1174
+ if offset > 0:
1175
+ params["start"] = offset
1176
+ org_id = get_org_id()
1177
+ if org_id is not None:
1178
+ params["orgId"] = org_id
1179
+ result = None
1180
+ try:
1181
+ result = api.session.rest_get(url, params=params)
1182
+ except WebexError as e:
1183
+ handle_rest_error(e)
1184
+ result = result or []
1185
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1186
+ if output == "json":
1187
+ print_json(items)
1188
+ else:
1189
+ print_table(items, columns=[('ID', 'id'), ('Status', 'latestExecutionStatus')], limit=limit)
1190
+
1191
+
1192
+
1193
+ @app.command("create-rebuild-phones")
1194
+ def create_rebuild_phones(
1195
+ location_id: str = typer.Option(None, "--location-id", help="(required) Unique identifier of the location."),
1196
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1197
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1198
+ debug: bool = typer.Option(False, "--debug"),
1199
+ ):
1200
+ """Rebuild Phones Configuration."""
1201
+ api = get_api(debug=debug)
1202
+ url = f"https://webexapis.com/v1/telephony/config/jobs/devices/rebuildPhones"
1203
+ params = {}
1204
+ org_id = get_org_id()
1205
+ if org_id is not None:
1206
+ params["orgId"] = org_id
1207
+ if json_body:
1208
+ body = json.loads(json_body)
1209
+ else:
1210
+ body = {}
1211
+ if location_id is not None:
1212
+ body["locationId"] = location_id
1213
+ _missing = [f for f in ['locationId'] if f not in body or body[f] is None]
1214
+ if _missing:
1215
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
1216
+ raise typer.Exit(1)
1217
+ try:
1218
+ result = api.session.rest_post(url, json=body, params=params)
1219
+ except WebexError as e:
1220
+ handle_rest_error(e)
1221
+ if output == "json":
1222
+ print_json(result)
1223
+ elif isinstance(result, dict) and "id" in result:
1224
+ typer.echo(f"Created: {result['id']}")
1225
+ elif not result or result == {}:
1226
+ typer.echo("Created.")
1227
+ else:
1228
+ print_json(result)
1229
+
1230
+
1231
+
1232
+ @app.command("show-rebuild-phones")
1233
+ def show_rebuild_phones(
1234
+ job_id: str = typer.Argument(help="jobId"),
1235
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1236
+ debug: bool = typer.Option(False, "--debug"),
1237
+ ):
1238
+ """Get the Job Status of a Rebuild Phones Job."""
1239
+ api = get_api(debug=debug)
1240
+ url = f"https://webexapis.com/v1/telephony/config/jobs/devices/rebuildPhones/{job_id}"
1241
+ params = {}
1242
+ org_id = get_org_id()
1243
+ if org_id is not None:
1244
+ params["orgId"] = org_id
1245
+ try:
1246
+ result = api.session.rest_get(url, params=params)
1247
+ except WebexError as e:
1248
+ handle_rest_error(e)
1249
+ if output == "json":
1250
+ print_json(result)
1251
+ else:
1252
+ if isinstance(result, dict):
1253
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1254
+ elif isinstance(result, list):
1255
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1256
+ else:
1257
+ print_json(result)
1258
+
1259
+
1260
+
1261
+ @app.command("list-errors-rebuild-phones")
1262
+ def list_errors_rebuild_phones(
1263
+ job_id: str = typer.Argument(help="jobId"),
1264
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1265
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1266
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1267
+ debug: bool = typer.Option(False, "--debug"),
1268
+ ):
1269
+ """Get Job Errors for a Rebuild Phones Job."""
1270
+ api = get_api(debug=debug)
1271
+ url = f"https://webexapis.com/v1/telephony/config/jobs/devices/rebuildPhones/{job_id}/errors"
1272
+ params = {}
1273
+ if limit > 0:
1274
+ params["max"] = limit
1275
+ if offset > 0:
1276
+ params["start"] = offset
1277
+ org_id = get_org_id()
1278
+ if org_id is not None:
1279
+ params["orgId"] = org_id
1280
+ result = None
1281
+ try:
1282
+ result = api.session.rest_get(url, params=params)
1283
+ except WebexError as e:
1284
+ handle_rest_error(e)
1285
+ result = result or []
1286
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1287
+ if output == "json":
1288
+ print_json(items)
1289
+ else:
1290
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1291
+
1292
+
1293
+
1294
+ @app.command("show-settings-devices-3")
1295
+ def show_settings_devices_3(
1296
+ person_id: str = typer.Argument(help="personId"),
1297
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1298
+ debug: bool = typer.Option(False, "--debug"),
1299
+ ):
1300
+ """Get Device Settings for a Person."""
1301
+ api = get_api(debug=debug)
1302
+ url = f"https://webexapis.com/v1/telephony/config/people/{person_id}/devices/settings"
1303
+ params = {}
1304
+ org_id = get_org_id()
1305
+ if org_id is not None:
1306
+ params["orgId"] = org_id
1307
+ try:
1308
+ result = api.session.rest_get(url, params=params)
1309
+ except WebexError as e:
1310
+ handle_rest_error(e)
1311
+ if output == "json":
1312
+ print_json(result)
1313
+ else:
1314
+ if isinstance(result, dict):
1315
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1316
+ elif isinstance(result, list):
1317
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1318
+ else:
1319
+ print_json(result)
1320
+
1321
+
1322
+
1323
+ @app.command("update-settings-devices-1")
1324
+ def update_settings_devices_1(
1325
+ person_id: str = typer.Argument(help="personId"),
1326
+ compression: str = typer.Option(None, "--compression", help="Choices: ON, OFF"),
1327
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1328
+ debug: bool = typer.Option(False, "--debug"),
1329
+ ):
1330
+ """Update Device Settings for a Person\n\nExample --json-body:\n '{"compression":"ON"}'."""
1331
+ api = get_api(debug=debug)
1332
+ url = f"https://webexapis.com/v1/telephony/config/people/{person_id}/devices/settings"
1333
+ params = {}
1334
+ org_id = get_org_id()
1335
+ if org_id is not None:
1336
+ params["orgId"] = org_id
1337
+ if json_body:
1338
+ body = json.loads(json_body)
1339
+ else:
1340
+ body = {}
1341
+ if compression is not None:
1342
+ body["compression"] = compression
1343
+ try:
1344
+ result = api.session.rest_put(url, json=body, params=params)
1345
+ except WebexError as e:
1346
+ handle_rest_error(e)
1347
+ typer.echo(f"Updated.")
1348
+
1349
+
1350
+
1351
+ @app.command("show-settings-devices-4")
1352
+ def show_settings_devices_4(
1353
+ workspace_id: str = typer.Argument(help="workspaceId"),
1354
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1355
+ debug: bool = typer.Option(False, "--debug"),
1356
+ ):
1357
+ """Get Device Settings for a Workspace."""
1358
+ api = get_api(debug=debug)
1359
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/devices/settings"
1360
+ params = {}
1361
+ org_id = get_org_id()
1362
+ if org_id is not None:
1363
+ params["orgId"] = org_id
1364
+ try:
1365
+ result = api.session.rest_get(url, params=params)
1366
+ except WebexError as e:
1367
+ handle_rest_error(e)
1368
+ if output == "json":
1369
+ print_json(result)
1370
+ else:
1371
+ if isinstance(result, dict):
1372
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1373
+ elif isinstance(result, list):
1374
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1375
+ else:
1376
+ print_json(result)
1377
+
1378
+
1379
+
1380
+ @app.command("update-settings-devices-2")
1381
+ def update_settings_devices_2(
1382
+ workspace_id: str = typer.Argument(help="workspaceId"),
1383
+ compression: str = typer.Option(None, "--compression", help="Choices: ON, OFF"),
1384
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1385
+ debug: bool = typer.Option(False, "--debug"),
1386
+ ):
1387
+ """Update Device Settings for a Workspace\n\nExample --json-body:\n '{"compression":"ON"}'."""
1388
+ api = get_api(debug=debug)
1389
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/devices/settings"
1390
+ params = {}
1391
+ org_id = get_org_id()
1392
+ if org_id is not None:
1393
+ params["orgId"] = org_id
1394
+ if json_body:
1395
+ body = json.loads(json_body)
1396
+ else:
1397
+ body = {}
1398
+ if compression is not None:
1399
+ body["compression"] = compression
1400
+ try:
1401
+ result = api.session.rest_put(url, json=body, params=params)
1402
+ except WebexError as e:
1403
+ handle_rest_error(e)
1404
+ typer.echo(f"Updated.")
1405
+
1406
+
1407
+
1408
+ @app.command("list-background-images")
1409
+ def list_background_images(
1410
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1411
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1412
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1413
+ debug: bool = typer.Option(False, "--debug"),
1414
+ ):
1415
+ """Read the List of Background Images."""
1416
+ api = get_api(debug=debug)
1417
+ url = f"https://webexapis.com/v1/telephony/config/devices/backgroundImages"
1418
+ params = {}
1419
+ if limit > 0:
1420
+ params["max"] = limit
1421
+ if offset > 0:
1422
+ params["start"] = offset
1423
+ org_id = get_org_id()
1424
+ if org_id is not None:
1425
+ params["orgId"] = org_id
1426
+ result = None
1427
+ try:
1428
+ result = api.session.rest_get(url, params=params)
1429
+ except WebexError as e:
1430
+ handle_rest_error(e)
1431
+ result = result or []
1432
+ items = result.get("backgroundImages", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1433
+ if output == "json":
1434
+ print_json(items)
1435
+ else:
1436
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1437
+
1438
+
1439
+
1440
+ @app.command("delete-background-images")
1441
+ def delete_background_images(
1442
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
1443
+ debug: bool = typer.Option(False, "--debug"),
1444
+ ):
1445
+ """Delete Device Background Images."""
1446
+ if not force:
1447
+ typer.confirm("Delete this resource?", abort=True)
1448
+ api = get_api(debug=debug)
1449
+ url = f"https://webexapis.com/v1/telephony/config/devices/backgroundImages"
1450
+ params = {}
1451
+ org_id = get_org_id()
1452
+ if org_id is not None:
1453
+ params["orgId"] = org_id
1454
+ try:
1455
+ api.session.rest_delete(url, params=params)
1456
+ except WebexError as e:
1457
+ handle_rest_error(e)
1458
+ typer.echo("Deleted.")
1459
+
1460
+
1461
+
1462
+ @app.command("show-count-devices")
1463
+ def show_count_devices(
1464
+ person_id: str = typer.Argument(help="personId"),
1465
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1466
+ debug: bool = typer.Option(False, "--debug"),
1467
+ ):
1468
+ """Get User Devices Count."""
1469
+ api = get_api(debug=debug)
1470
+ url = f"https://webexapis.com/v1/telephony/config/people/{person_id}/devices/count"
1471
+ params = {}
1472
+ org_id = get_org_id()
1473
+ if org_id is not None:
1474
+ params["orgId"] = org_id
1475
+ try:
1476
+ result = api.session.rest_get(url, params=params)
1477
+ except WebexError as e:
1478
+ handle_rest_error(e)
1479
+ if output == "json":
1480
+ print_json(result)
1481
+ else:
1482
+ if isinstance(result, dict):
1483
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1484
+ elif isinstance(result, list):
1485
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1486
+ else:
1487
+ print_json(result)
1488
+
1489
+