Compare commits

..

No commits in common. "cf3c16c66a670bba89b71444d02a669ec9876c0d" and "49a4ecc07f68182c32d59f0c41ede21085e0101d" have entirely different histories.

10 changed files with 196 additions and 272 deletions

View File

@ -20,14 +20,6 @@ mitel\_ommclient2.messages.getppdev module
:undoc-members: :undoc-members:
:show-inheritance: :show-inheritance:
mitel\_ommclient2.messages.getppuser module
-------------------------------------------
.. automodule:: mitel_ommclient2.messages.getppuser
:members:
:undoc-members:
:show-inheritance:
mitel\_ommclient2.messages.open module mitel\_ommclient2.messages.open module
-------------------------------------- --------------------------------------

View File

@ -15,7 +15,7 @@ to establish a transport to the API.
import mitel_ommclient2 import mitel_ommclient2
conn = mitel_ommclient2.connection.Connection("omm.local") conn = mitel_ommclient2..connection.Connection("omm.local")
To actually connect to the OMM, you need to call :func:`mitel_ommclient2.connection.Connection.connect`. To actually connect to the OMM, you need to call :func:`mitel_ommclient2.connection.Connection.connect`.
@ -45,8 +45,8 @@ You hand over a Request object and receive a response object.
.. code-block:: python .. code-block:: python
>>> m = mitel_ommclient2.messages.Ping() >>> request = mitel_ommclient2.messages.Ping()
>>> r = conn.request(m) >>> r = conn.request(request)
>>> r.name >>> r.name
'PingResp' 'PingResp'

View File

@ -5,13 +5,14 @@ The API consists of three main message types: request, response and event. They
are represented by :class:`mitel_ommclient2.messages.Request`, :class:`mitel_ommclient2.messages.Response` are represented by :class:`mitel_ommclient2.messages.Request`, :class:`mitel_ommclient2.messages.Response`
and events aren't supported yet. and events aren't supported yet.
There are several subclasses for each messages type, which provide a conveinient There are several subclasses for each messages type, but they are just just overlays
interface to message content using attributes. to provide a conveinient interface to message content using attributes.
For each message you can access each field directly as class attributes. Each message provides three attributes that define the central interfaces:
There are two special attributes:
* name: returns the message name * name
* childs: allowes you to access childs by the child name as class attributes * attrs
* childs
Using messages Using messages
-------------- --------------
@ -26,20 +27,27 @@ and hand it over to :func:`mitel_ommclient2.client.OMMClient2.request` or
my_time = int(time.time()) my_time = int(time.time())
m = mitel_ommclient2.messages.Ping() request = mitel_ommclient2.messages.Ping(timeStamp=my_time)
m.timeStamp = my_time r = c.request(request)
r = c.request(m)
ping = r.timeStamp - my_time ping = r.timeStamp - my_time
A more complex example Crafting your own messages
---------------------- --------------------------
This demonstrates how to access message childs. It some cases your message class isn't implemented yet. Then you can craft the
message yourself.
.. code-block:: python .. code-block:: python
m = messages.GetAccount() import time
m.id = id
r = self.connection.request(m) my_time = int(time.time())
return r.childs.account[0]
request = mitel_ommclient2.messages.Request("Ping")
request.attrs = {
"timeStamp": my_time,
}
r = c.request(request)
ping = r.attrs["timeStamp"] - my_time

View File

@ -1,7 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from .connection import Connection from .connection import Connection
from . import exceptions
from . import messages from . import messages
class OMMClient2: class OMMClient2:
@ -44,11 +43,7 @@ class OMMClient2:
self.connection.connect() self.connection.connect()
# Login # Login
m = messages.Open() r = self.connection.request(messages.Open(self._username, self._password, UserDeviceSyncClient=self._ommsync))
m.username = self._username
m.password = self._password
m.UserDeviceSyncClient = self._ommsync
r = self.connection.request(m)
r.raise_on_error() r.raise_on_error()
def get_account(self, id): def get_account(self, id):
@ -58,88 +53,23 @@ class OMMClient2:
:param id: User id :param id: User id
""" """
m = messages.GetAccount() r = self.connection.request(messages.GetAccount(id))
m.id = id
r = self.connection.request(m)
r.raise_on_error() r.raise_on_error()
if r.childs.account is None: if r.account is None:
return None return None
return r.childs.account[0] return r.account[0]
def get_device(self, ppn): def get_pp_dev(self, ppn):
""" """
Get PP device Get PP device
:param ppn: Device id :param id: Device id
""" """
r = self.connection.request(messages.GetPPDev(ppn))
m = messages.GetPPDev()
m.ppn = ppn
r = self.connection.request(m)
r.raise_on_error() r.raise_on_error()
if r.childs.pp is None: if r.pp is None:
return None return None
return r.childs.pp[0] return r.pp[0]
def get_devices(self):
"""
Get all PP devices
"""
next_ppn = 0
while True:
m = messages.GetPPDev()
m.ppn = next_ppn
m.maxRecords = 20
r = self.connection.request(m)
try:
r.raise_on_error()
except exceptions.ENoEnt:
# No more devices to fetch
break
# Output all found devices
for pp in r.childs.pp:
yield pp
# Determine next possible ppn
next_ppn = int(pp["ppn"]) + 1
def get_user(self, uid):
"""
Get PP user
:param uid: User id
"""
m = messages.GetPPUser()
m.uid = uid
r = self.connection.request(m)
r.raise_on_error()
if r.childs.user is None:
return None
return r.childs.user[0]
def get_users(self):
"""
Get all PP users
"""
next_uid = 0
while True:
m = messages.GetPPUser()
m.uid = next_uid
m.maxRecords = 20
r = self.connection.request(m)
try:
r.raise_on_error()
except exceptions.ENoEnt:
# No more devices to fetch
break
# Output all found devices
for user in r.childs.user:
yield user
# Determine next possible ppn
next_uid = int(user["uid"]) + 1
def ping(self): def ping(self):
""" """

View File

@ -5,98 +5,45 @@ from xml.dom.minidom import getDOMImplementation, parseString
from ..exceptions import exception_classes, OMResponseException from ..exceptions import exception_classes, OMResponseException
class Message: class Request:
""" """
Base message class Request message class
:param name: Name of the message
:param seq: Unique sequence number to associate responses
Usage::
>>> req = Request("Ping")
"""
def __init__(self, name, seq=None):
self.name = name
self.attrs = {}
self.childs = {}
if seq is not None:
self.attrs["seq"] = seq
@property
def seq(self):
return self.attrs.get("seq")
@seq.setter
def seq(self, seq):
self.attrs["seq"] = seq
class Response:
"""
Response message class
:param name: Name of the message :param name: Name of the message
:param attrs: Message attributes :param attrs: Message attributes
:param childs: Message children :param childs: Message children
""" """
def __init__(self, name, attrs={}, childs={}):
# Fields defined by the base type class
BASE_FIELDS = {}
# Fields defined by subclasses
FIELDS = {}
# Child types
CHILDS = {}
# Fields dicts consist of the field name as name and the field type as value
# Use None if the field type is unknown, any type is allowed then
class Childs:
"""
Contains message childs
"""
CHILDS = {}
def __init__(self, child_types, child_dict):
self.CHILDS = child_types
self._child_dict = child_dict
def __getattr__(self, name):
if name in self.CHILDS.keys():
return self._child_dict.get(name)
else:
raise AttributeError()
def __setattr__(self, name, value):
if name in self.CHILDS.keys():
if self.CHILDS[name] is not None and type(value) != self.CHILDS[name]:
raise TypeError()
self._child_dict[name] = value
else:
object.__setattr__(self, name, value)
def __init__(self, name=None, attrs={}, childs={}):
self.name = name self.name = name
if not self.name: self.attrs = attrs
self.name = self.__class__.__name__ self.childs = childs
self._attrs = attrs
self._childs = childs
self.childs = self.Childs(self.CHILDS, self._childs)
def __getattr__(self, name):
fields = self.FIELDS | self.BASE_FIELDS
if name in fields.keys():
return self._attrs.get(name)
else:
raise AttributeError()
def __setattr__(self, name, value):
fields = self.FIELDS | self.BASE_FIELDS
if name in fields.keys():
if fields[name] is not None and type(value) != fields[name]:
raise TypeError()
self._attrs[name] = value
else:
object.__setattr__(self, name, value)
class Request(Message):
"""
Request message type class
"""
BASE_FIELDS = {
"seq": int,
}
class Response(Message):
"""
Response message type class
"""
BASE_FIELDS = {
"seq": int,
"errCode": None,
"info": None,
"bad": None,
"maxLen": None,
}
def raise_on_error(self): def raise_on_error(self):
""" """
@ -115,21 +62,28 @@ class Response(Message):
if self.errCode is not None: if self.errCode is not None:
raise exception_classes.get(self.errCode, OMResponseException)(response=self) raise exception_classes.get(self.errCode, OMResponseException)(response=self)
@property
def seq(self):
return int(self.attrs.get("seq"))
REQUEST_TYPES = {} @property
RESPONSE_TYPES = {} def errCode(self):
return self.attrs.get("errCode")
def request_type(c): @property
REQUEST_TYPES[c.__name__] = c def info(self):
return c return self.attrs.get("info")
def response_type(c): @property
RESPONSE_TYPES[c.__name__] = c def bad(self):
return c return self.attrs.get("bad")
@property
def maxLen(self):
return self.attrs.get("maxLen")
from .getaccount import GetAccount, GetAccountResp from .getaccount import GetAccount, GetAccountResp
from .getppdev import GetPPDev, GetPPDevResp from .getppdev import GetPPDev, GetPPDevResp
from .getppuser import GetPPUser, GetPPUserResp
from .open import Open, OpenResp from .open import Open, OpenResp
from .ping import Ping, PingResp from .ping import Ping, PingResp
@ -141,11 +95,11 @@ def construct(request):
message = impl.createDocument(None, request.name, None) message = impl.createDocument(None, request.name, None)
root = message.documentElement root = message.documentElement
for k, v in request._attrs.items(): for k, v in request.attrs.items():
root.setAttribute(str(k), str(v)) root.setAttribute(str(k), str(v))
for k, v in request._childs.items(): for k, v in request.childs.items():
child = message.createElement(k) child = message.createElement(k)
if v is not None: if v is not None:
for c_k, c_v in v.items(): for c_k, c_v in v.items():
@ -153,6 +107,17 @@ def construct(request):
root.appendChild(child) root.appendChild(child)
return root.toxml() return root.toxml()
def _response_type_by_name(name):
response_types = [
GetAccountResp,
GetPPDevResp,
PingResp,
]
response_types_dict = {r.__name__: r for r in response_types}
return response_types_dict.get(name, Response)
def parse(message): def parse(message):
message = parseString(message) message = parseString(message)
root = message.documentElement root = message.documentElement
@ -161,14 +126,8 @@ def parse(message):
attrs = {} attrs = {}
childs = {} childs = {}
response_type = RESPONSE_TYPES.get(name)
fields = response_type.FIELDS | response_type.BASE_FIELDS
for i in range(0, root.attributes.length): for i in range(0, root.attributes.length):
item = root.attributes.item(i) item = root.attributes.item(i)
if fields.get(item.name) is not None:
attrs[item.name] = fields[item.name](item.value)
else:
attrs[item.name] = item.value attrs[item.name] = item.value
child = root.firstChild child = root.firstChild
@ -187,4 +146,4 @@ def parse(message):
child = child.nextSibling child = child.nextSibling
return response_type(name, attrs, childs) return _response_type_by_name(name)(name, attrs, childs)

View File

@ -1,18 +1,27 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from . import Request, Response, request_type, response_type from . import Request, Response
@request_type
class GetAccount(Request): class GetAccount(Request):
FIELDS = { def __init__(self, id, maxRecords=None, **kwargs):
"id": int, super().__init__("GetAccount", **kwargs)
"maxRecords": int,
} self.attrs["id"] = id
if maxRecords is not None:
self.attrs["maxRecords"] = maxRecords
@property
def id(self):
return self.attrs.get("id")
@property
def maxRecords(self):
return self.attrs.get("maxRecords")
@response_type
class GetAccountResp(Response): class GetAccountResp(Response):
CHILDS = { @property
"account": None, def account(self):
} return self.childs.get("account")

View File

@ -1,18 +1,27 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from . import Request, Response, request_type, response_type from . import Request, Response
@request_type
class GetPPDev(Request): class GetPPDev(Request):
FIELDS = { def __init__(self, ppn, maxRecords=None, **kwargs):
"ppn": int, super().__init__("GetPPDev", **kwargs)
"maxRecords": int,
} self.attrs["ppn"] = ppn
if maxRecords is not None:
self.attrs["maxRecords"] = maxRecords
@property
def ppn(self):
return self.attrs.get("ppn")
@property
def maxRecords(self):
return self.attrs.get("maxRecords")
@response_type
class GetPPDevResp(Response): class GetPPDevResp(Response):
CHILDS = { @property
"pp": None, def pp(self):
} return self.childs.get("pp")

View File

@ -1,18 +0,0 @@
#!/usr/bin/env python3
from . import Request, Response, request_type, response_type
@request_type
class GetPPUser(Request):
FIELDS = {
"uid": int,
"maxRecords": int,
}
@response_type
class GetPPUserResp(Response):
CHILDS = {
"user": None,
}

View File

@ -1,23 +1,55 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from . import Request, Response, request_type, response_type from . import Request, Response
@request_type
class Open(Request): class Open(Request):
FIELDS = { """
"username": None, Authenticate Message
"password": None,
"UserDeviceSyncClient": None,
}
Needs to be the first message on a new connection.
:param username: Username
:param password: Password
:param UserDeviceSyncClient: If True login as OMM-Sync client. Some operations in OMM-Sync mode might lead to destroy DECT paring.
"""
def __init__(self, username, password, UserDeviceSyncClient=False, **kwargs):
super().__init__("Open", **kwargs)
self.attrs["username"] = username
self.attrs["password"] = password
if UserDeviceSyncClient:
self.attrs["UserDeviceSyncClient"] = "true"
@property
def username(self):
return self.attrs.get("username")
@property
def password(self):
return self.attrs.get("password")
@property
def UserDeviceSyncClient(self):
return self.attrs.get("UserDeviceSyncClient")
@response_type
class OpenResp(Response): class OpenResp(Response):
FIELDS = { @property
"protocolVersion": None, def protocolVersion(self):
"minPPSwVersion1": None, return self.attrs.get("protocolVersion")
"minPPSwVersion2": None,
"ommStbState": None, @property
"publicKey": None, def minPPSwVersion1(self):
} return self.attrs.get("minPPSwVersion1")
@property
def minPPSwVersion2(self):
return self.attrs.get("minPPSwVersion2")
@property
def ommStbState(self):
return self.attrs.get("ommStbState")
@property
def publicKey(self):
return self.attrs.get("publicKey")

View File

@ -1,17 +1,20 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from . import Request, Response, request_type, response_type from . import Request, Response
@request_type
class Ping(Request): class Ping(Request):
FIELDS = { def __init__(self, timeStamp=None, **kwargs):
"timeStamp": int, super().__init__("Ping", **kwargs)
}
if timeStamp is not None:
self.attrs["timeStamp"] = timeStamp
@property
def timeStamp(self):
return self.attrs.get("timeStamp")
@response_type
class PingResp(Response): class PingResp(Response):
FIELDS = { @property
"timeStamp": int, def timeStamp(self):
} return self.attrs.get("timeStamp")