"""
Devices represent cloud-registered Webex RoomOS devices and Webex Calling phones.
"""
import builtins
from collections.abc import Generator
from dataclasses import dataclass
from datetime import datetime
from typing import Any, Optional
from pydantic import Field, field_validator, model_validator
from ..api_child import ApiChild
from ..base import ApiModel, enum_str
from ..base import SafeEnum as Enum
from ..common import DevicePlatform, MaintenanceMode
from ..rest import RestSession
from ..telephony.devices import DeviceManagedBy
from ..telephony.jobs import DeviceSettingsJobsApi
__all__ = ['DevicesApi', 'Device', 'TagOp', 'ActivationCodeResponse', 'ProductType', 'ConnectionStatus', 'Lifecycle']
[docs]
class ProductType(str, Enum):
phone = 'phone'
roomdesk = 'roomdesk'
[docs]
class ConnectionStatus(str, Enum):
connected = 'connected'
disconnected = 'disconnected'
connected_with_issues = 'connected_with_issues'
offline_expired = 'offline_expired'
activating = 'activating'
unknown = 'unknown'
offline_deep_sleep = 'offline_deep_sleep'
pending = 'pending'
[docs]
class Lifecycle(str, Enum):
unknown = 'UNKNOWN'
active = 'ACTIVE'
end_of_sale = 'END_OF_SALE'
end_of_maintenance = 'END_OF_MAINTENANCE'
end_of_service = 'END_OF_SERVICE'
upcoming_end_of_support = 'UPCOMING_END_OF_SUPPORT'
end_of_support = 'END_OF_SUPPORT'
[docs]
class Device(ApiModel):
#: A unique identifier for the device. May be null.
device_id: Optional[str] = Field(None, alias='id')
#: A unique identifier for the device specifically for use with Webex Calling APIs. May be null.
calling_device_id: Optional[str] = None
#: A unique identifier for the device specifically for use with Webex Devices APIs. May be null.
webex_device_id: Optional[str] = None
#: A persistent unique identifier for the device. Org based and persistent across federation migrations.
p_device_id: Optional[str] = Field(None, alias='device_id')
#: A friendly name for the device.
display_name: Optional[str] = None
#: The workspace associated with the device.
workspace_id: Optional[str] = None
#: The person associated with the device.
person_id: Optional[str] = None
#: The organization associated with the device
org_id: Optional[str] = None
#: The capabilities of the device.
capabilities: Optional[list[str]] = None
#: The permissions the user has for this device. For example, xapi means this user is entitled to using the xapi
#: against this device.
permissions: Optional[list[str]] = None
#: The connection status of the device.
connection_status: Optional[ConnectionStatus] = None
#: The product name. A display friendly version of the device's model.
product: Optional[str] = None
#: The product type.
product_type: ProductType | None = Field(None, alias='type')
#: Tags assigned to the device.
tags: Optional[list[str]] = None
#: The current IP address of the device.
ip: Optional[str] = None
#: The current network connectivity for the device.
active_interface: Optional[str] = None
#: The unique address for the network adapter.
mac: Optional[str] = None
#: The primary SIP address to dial this device.
primary_sip_url: Optional[str] = None
#: All SIP addresses to dial this device.
sip_urls: Optional[list[Any]] = None
error_codes: Optional[list[Any]] = None
#: Serial number for the device.
serial: Optional[str] = None
#: The operating system name data and version tag.
software: Optional[str] = None
#: The upgrade channel the device is assigned to.
upgrade_channel: Optional[str] = None
#: The date and time that the device was registered, in ISO8601 format.
created: Optional[datetime] = None
#: The location associated with the device.
location_id: Optional[str] = None
#: The workspace location associated with the device.
workspace_location_id: Optional[str] = None
#: The date and time that the device was first seen, in ISO8601 format.
first_seen: Optional[datetime] = None
#: The date and time that the device was last seen, in ISO8601 format.
last_seen: Optional[datetime] = None
#: Entity managing the device configuration.
managed_by: Optional[DeviceManagedBy] = None
#: Manufacturer of the device
#: only for 3rd party devices
manufacturer: Optional[str] = None
#: The Line/Port identifies a device endpoint in standalone mode or a SIP URI public identity in IMS mode
#: only for 3rd party devices
line_port: Optional[str] = None
#: Contains the body of the HTTP response received following the request to the Console API
#: Not set if the response has no body
#: only for 3rd party devices
outbound_proxy: Optional[str] = None
#: SIP authentication user ame for the owner of the device
#: only for 3rd party devices
sip_user_name: Optional[str] = None
#: The device platform.
device_platform: Optional[DevicePlatform] = None
#: TODO: undocumented, WXCAPIBULK-719
lifecycle: Optional[Lifecycle] = None
planned_maintenance: Optional[MaintenanceMode] = None
@model_validator(mode='before')
@classmethod
def pop_place_id(cls, values):
"""
:meta private:
"""
values.pop('placeId', None)
return values
@field_validator('mac')
@classmethod
def mac_no_colon(cls, v: str) -> str:
"""
:meta private:
"""
return v.replace(':', '')
[docs]
class TagOp(str, Enum):
add = 'add'
remove = 'remove'
replace = 'replace' # type: ignore[assignment]
[docs]
class ActivationCodeResponse(ApiModel):
#: The activation code.
code: str
#: The date and time the activation code expires.
expiry_time: datetime
[docs]
@dataclass(init=False, repr=False)
class DevicesApi(ApiChild, base='devices'):
"""
Devices
Devices represent cloud-registered Webex RoomOS devices and Webex Calling phones. Devices may be associated with
`Workspaces
<https://developer.webex.com/docs/api/v1/workspaces>`_.
The following scopes are required for performing the specified actions:
* Searching and viewing details for devices requires an auth token with the `spark:devices_read` scope.
* Updating or deleting your devices requires an auth token with the `spark:devices_write` scope.
* Viewing the list of all devices in an organization requires an administrator auth token with the
`spark-admin:devices_read` scope.
* Adding, updating, or deleting all devices in an organization requires an administrator auth token with the
`spark-admin:devices_write` scope.
* Generating an activation code requires an auth token with the `spark-admin:devices_write` scope, and one of the
`identity:placeonetimepassword_create` or `identity:one_time_password` scopes.
These APIs cannot be used with Cisco 98xx devices that are not yet Webex
Aware. Use Webex Control Hub to manage these devices.
"""
#: device jobs Api
settings_jobs: DeviceSettingsJobsApi
[docs]
def __init__(self, *, session: RestSession):
super().__init__(session=session)
self.settings_jobs = DeviceSettingsJobsApi(session=session)
[docs]
def list(
self,
person_id: str = None,
workspace_id: str = None,
location_id: str = None,
workspace_location_id: str = None,
display_name: str = None,
product: str = None,
product_type: ProductType = None,
tag: str = None,
connection_status: ConnectionStatus = None,
serial: str = None,
software: str = None,
upgrade_channel: str = None,
error_code: str = None,
capability: str = None,
permission: str = None,
mac: str = None,
device_platform: DevicePlatform = None,
planned_maintenance: MaintenanceMode = None,
org_id: str = None,
**params,
) -> Generator[Device, None, None]:
"""
List Devices
Lists all active Webex devices associated with the authenticated user, such as devices activated in personal
mode. Administrators can list all devices within an organization.
:param person_id: List devices by person ID.
:type person_id: str
:param workspace_id: List devices by workspace ID.
:type workspace_id: str
:param location_id: List devices by location ID.
:type location_id: str
:param workspace_location_id: List devices by workspace location ID. Deprecated, prefer `location_id`.
:type workspace_location_id: str
:param display_name: List devices with this display name.
:type display_name: str
:param product: List devices with this product name.
:type product: str
:param product_type: List devices with this type. Possible values: roomdesk, phone, accessory, webexgo, unknown
:type product_type: str
:param tag: List devices which have a tag. Searching for multiple tags (logical AND) can be done by comma
:type tag: str
separating the tag values or adding several tag parameters.
:param connection_status: List devices with this connection statu
:type connection_status: str
:param serial: List devices with this serial number.
:type serial: str
:param software: List devices with this software version.
:type software: str
:param upgrade_channel: List devices with this upgrade channel.
:type upgrade_channel: str
:param error_code: List devices with this error code.
:type error_code: str
:param capability: List devices with this capability. For example: xapi
:type capability: str
:param permission: List devices with this permission.
:type permission: str
:param mac: List devices with this MAC address.
:type mac: str
:param device_platform: List devices with this device platform.
:type device_platform: DevicePlatform
:param org_id: List devices in this organization. Only admin users of another organization (such as partners)
may use this parameter.
:param planned_maintenance:
List devices with this planned maintenance.
:type planned_maintenance: MaintenanceMode
:type org_id: str
:return: Generator yielding :class:`Device` instances
"""
if display_name is not None:
params['displayName'] = display_name
if person_id is not None:
params['personId'] = person_id
if workspace_id is not None:
params['workspaceId'] = workspace_id
if org_id is not None:
params['orgId'] = org_id
if connection_status is not None:
params['connectionStatus'] = connection_status
if product is not None:
params['product'] = enum_str(product)
if product_type is not None:
params['type'] = enum_str(product_type)
if serial is not None:
params['serial'] = serial
if tag is not None:
params['tag'] = tag
if software is not None:
params['software'] = software
if upgrade_channel is not None:
params['upgradeChannel'] = upgrade_channel
if error_code is not None:
params['errorCode'] = error_code
if capability is not None:
params['capability'] = enum_str(capability)
if permission is not None:
params['permission'] = permission
if location_id is not None:
params['locationId'] = location_id
if workspace_location_id is not None:
params['workspaceLocationId'] = workspace_location_id
if mac is not None:
params['mac'] = mac
if device_platform is not None:
params['devicePlatform'] = enum_str(device_platform)
if planned_maintenance is not None:
params['plannedMaintenance'] = enum_str(planned_maintenance)
url = self.ep()
return self.session.follow_pagination(url=url, model=Device, params=params, item_key='items')
[docs]
def details(self, device_id: str, org_id: str = None) -> Device:
"""
Get Device Details
Shows details for a device, by ID.
Specify the device ID in the deviceId parameter in the URI.
:param device_id: A unique identifier for the device.
:type device_id: str
:param org_id:
:type org_id: str
:return: Device details
:rtype: Device
"""
url = self.ep(device_id)
params = org_id and {'orgId': org_id} or None
data = self.get(url=url, params=params)
return Device.model_validate(data)
[docs]
def delete(self, device_id: str, org_id: str = None):
"""
Delete a Device
Deletes a device, by ID.
Specify the device ID in the deviceId parameter in the URI.
:param device_id: A unique identifier for the device.
:type device_id: str
:param org_id:
:type org_id: str
"""
url = self.ep(device_id)
params = org_id and {'orgId': org_id} or None
super().delete(url=url, params=params)
[docs]
def activation_code(
self, workspace_id: str = None, person_id: str = None, model: str = None, org_id: str = None
) -> ActivationCodeResponse:
"""
Create a Device Activation Code
Generate an activation code for a device in a specific workspace by `workspaceId` or for a person by
`personId`. This requires an auth token with the `spark-admin:devices_write` scope, and either
`identity:placeonetimepassword_create` (allows creating activation codes for workspaces only) or
`identity:one_time_password` (allows creating activation codes for workspaces or persons).
* Adding a device to a workspace with calling type `none` or `thirdPartySipCalling` will reset the workspace
calling type to `freeCalling`.
* Either `workspaceId` or `personId` should be provided. If both are supplied, the request will be invalid.
* If no `model` is supplied, the `code` returned will only be accepted on RoomOS devices.
* If your device is a phone, you must provide the `model` as a field. You can get the `model` from the
`supported devices
<https://developer.webex.com/docs/api/v1/device-call-settings/read-the-list-of-supported-devices>`_ API.
Adding a device to a person with a Webex Calling Standard license will disable
Webex Calling across their Webex mobile, tablet, desktop, and browser
applications.
When adding devices to a Webex Calling Professional licensed person or workspace, wait for each API call to
finish before starting the next. This prevents race conditions that can cause errors when assigning primary
versus secondary device status.
:param workspace_id: The ID of the workspace where the device will be activated.
:type workspace_id: str
:param person_id: The ID of the person who will own the device once activated.
:type person_id: str
:param model: The model of the device being created.
:type model: str
:param org_id: The organization associated with the activation code generated.
:type org_id: str
:rtype: ActivationCodeResponse
"""
params = org_id and {'orgId': org_id} or None
body = {}
if workspace_id is not None:
body['workspaceId'] = workspace_id
if person_id is not None:
body['personId'] = person_id
if model is not None:
body['model'] = model
url = self.ep('activationCode')
data = self.post(url=url, json=body, params=params)
return ActivationCodeResponse.model_validate(data)
[docs]
def create_by_mac_address(
self,
mac: str,
workspace_id: str = None,
person_id: str = None,
model: str = None,
password: str = None,
org_id: str = None,
) -> Optional[Device]:
"""
Create a Device by MAC Address
CCreate a phone by its MAC address in a specific workspace or for a person.
Specify the `mac`, `model` and either `workspaceId` or `personId`.
* You can get the `model` from the `supported devices
<https://developer.webex.com/docs/api/v1/device-call-settings/read-the-list-of-supported-devices>`_ API.
* Either `workspaceId` or `personId` should be provided. If both are supplied, the request will be invalid.
* The `password` field is only required for third party devices. You can obtain the required third party phone
configuration from `here
<https://developer.webex.com/docs/api/v1/beta-device-call-settings-with-third-party-device-support/get-third
-party-device>`_.
Adding a device to a person with a Webex Calling Standard license will disable
Webex Calling across their Webex mobile, tablet, desktop, and browser
applications.
When adding devices to a Webex Calling Professional licensed person or workspace, wait for each API call to
finish before starting the next. This prevents race conditions that can cause errors when assigning primary
versus secondary device status.
:param mac: The MAC address of the device being created.
:type mac: str
:param workspace_id: The ID of the workspace where the device will be activated.
:type workspace_id: str
:param person_id: The ID of the person who will own the device once activated.
:type person_id: str
:param model: The model of the device being created.
:type model: str
:param password: SIP password to be configured for the phone, only required with third party devices.
:type password: str
:param org_id: The organization associated with the device.
:type org_id: str
:return: created device information
:rtype: Device
"""
params = org_id and {'orgId': org_id} or None
body = {'mac': mac}
if workspace_id is not None:
body['workspaceId'] = workspace_id
if person_id is not None:
body['personId'] = person_id
if model is not None:
body['model'] = model
if password is not None:
body['password'] = password
url = self.ep()
data = super().post(url=url, json=body, params=params)
if not data:
return None
return Device.model_validate(data)