xaal.lib 0.7.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- xaal_lib-0.7.2/PKG-INFO +131 -0
- xaal_lib-0.7.2/README.rst +113 -0
- xaal_lib-0.7.2/pyproject.toml +11 -0
- xaal_lib-0.7.2/setup.cfg +4 -0
- xaal_lib-0.7.2/tests/test_bindings.py +56 -0
- xaal_lib-0.7.2/tests/test_cbor.py +62 -0
- xaal_lib-0.7.2/tests/test_device.py +195 -0
- xaal_lib-0.7.2/tests/test_engine.py +97 -0
- xaal_lib-0.7.2/tests/test_message.py +217 -0
- xaal_lib-0.7.2/tests/test_tools.py +53 -0
- xaal_lib-0.7.2/xaal/__init__.py +1 -0
- xaal_lib-0.7.2/xaal/lib/__init__.py +23 -0
- xaal_lib-0.7.2/xaal/lib/__main__.py +2 -0
- xaal_lib-0.7.2/xaal/lib/aioengine.py +388 -0
- xaal_lib-0.7.2/xaal/lib/aiohelpers.py +38 -0
- xaal_lib-0.7.2/xaal/lib/aionetwork.py +74 -0
- xaal_lib-0.7.2/xaal/lib/bindings.py +98 -0
- xaal_lib-0.7.2/xaal/lib/cbor.py +62 -0
- xaal_lib-0.7.2/xaal/lib/config.py +53 -0
- xaal_lib-0.7.2/xaal/lib/core.py +308 -0
- xaal_lib-0.7.2/xaal/lib/devices.py +285 -0
- xaal_lib-0.7.2/xaal/lib/engine.py +231 -0
- xaal_lib-0.7.2/xaal/lib/exceptions.py +20 -0
- xaal_lib-0.7.2/xaal/lib/helpers.py +80 -0
- xaal_lib-0.7.2/xaal/lib/messages.py +336 -0
- xaal_lib-0.7.2/xaal/lib/network.py +100 -0
- xaal_lib-0.7.2/xaal/lib/test.py +140 -0
- xaal_lib-0.7.2/xaal/lib/tools.py +130 -0
- xaal_lib-0.7.2/xaal.lib.egg-info/PKG-INFO +131 -0
- xaal_lib-0.7.2/xaal.lib.egg-info/SOURCES.txt +31 -0
- xaal_lib-0.7.2/xaal.lib.egg-info/dependency_links.txt +1 -0
- xaal_lib-0.7.2/xaal.lib.egg-info/requires.txt +7 -0
- xaal_lib-0.7.2/xaal.lib.egg-info/top_level.txt +1 -0
xaal_lib-0.7.2/PKG-INFO
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: xaal.lib
|
|
3
|
+
Version: 0.7.2
|
|
4
|
+
Summary: xaal.lib is the official Python stack of xAAL protocol dedicated to home automation systems
|
|
5
|
+
Author-email: Jerome Kerdreux <Jerome.Kerdreux@imt-atlantique.fr>
|
|
6
|
+
License: GPL License
|
|
7
|
+
Keywords: xaal,home-automation
|
|
8
|
+
Classifier: Programming Language :: Python
|
|
9
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
10
|
+
Description-Content-Type: text/x-rst
|
|
11
|
+
Requires-Dist: cbor2==5.4.2
|
|
12
|
+
Requires-Dist: pysodium
|
|
13
|
+
Requires-Dist: configobj
|
|
14
|
+
Requires-Dist: coloredlogs
|
|
15
|
+
Requires-Dist: decorator
|
|
16
|
+
Requires-Dist: tabulate
|
|
17
|
+
Requires-Dist: aioconsole
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
xaal.lib
|
|
21
|
+
========
|
|
22
|
+
**xaal.lib** is the official Python stack to develop home-automation devices and gateways
|
|
23
|
+
with the xAAL protocol. For a full description of the protocol check out
|
|
24
|
+
http://recherche.imt-atlantique.fr/xaal/
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
Dependencies
|
|
28
|
+
~~~~~~~~~~~~
|
|
29
|
+
xaal.lib depends on :
|
|
30
|
+
* cbor2
|
|
31
|
+
* pysodium
|
|
32
|
+
* configobj
|
|
33
|
+
* coloredlogs
|
|
34
|
+
* decorator
|
|
35
|
+
* tabulate
|
|
36
|
+
* aioconsole
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
Install
|
|
40
|
+
~~~~~~~
|
|
41
|
+
Please refer to the official `full documentation to install the lib in a virtualenv
|
|
42
|
+
<https://redmine.telecom-bretagne.eu/svn/xaal/code/Python/branches/0.7/README.html>`_
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
Usage
|
|
46
|
+
~~~~~
|
|
47
|
+
The main goal of xaal.lib is to provide an API to easily develop devices & gateways.
|
|
48
|
+
**xaal.lib.Engine send / receive / parse to|from xAAL Bus**.
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
To receive / parse / display incoming xAAL messages, you can simply try something like
|
|
52
|
+
this:
|
|
53
|
+
|
|
54
|
+
.. code-block:: python
|
|
55
|
+
|
|
56
|
+
from xaal.lib import Engine
|
|
57
|
+
|
|
58
|
+
def display(msg):
|
|
59
|
+
print(msg)
|
|
60
|
+
|
|
61
|
+
eng = Engine()
|
|
62
|
+
eng.subscribe(display)
|
|
63
|
+
eng.run()
|
|
64
|
+
|
|
65
|
+
The Engine will call the display function every time it receive a xAAL message.
|
|
66
|
+
|
|
67
|
+
Let's take a look at a simple lamp device :
|
|
68
|
+
|
|
69
|
+
.. code-block:: python
|
|
70
|
+
|
|
71
|
+
from xaal.lib import Device,Engine,tools
|
|
72
|
+
|
|
73
|
+
# create and configure the lamp device, with a random address
|
|
74
|
+
dev = Device("lamp.basic", tools.get_random_uuid())
|
|
75
|
+
dev.product_id = 'Dummy Lamp'
|
|
76
|
+
dev.url = 'http://www.acme.org'
|
|
77
|
+
dev.info = 'My fake lamp'
|
|
78
|
+
|
|
79
|
+
# add an xAAL attribute 'light'
|
|
80
|
+
light = dev.new_attribute('light')
|
|
81
|
+
|
|
82
|
+
# declare two device methods ON & OFF
|
|
83
|
+
def on():
|
|
84
|
+
light.value = True
|
|
85
|
+
|
|
86
|
+
def off():
|
|
87
|
+
light.value = False
|
|
88
|
+
|
|
89
|
+
dev.add_method('turn_on',on)
|
|
90
|
+
dev.add_method('turn_off',off)
|
|
91
|
+
|
|
92
|
+
# last step, create an engine and register the lamp
|
|
93
|
+
eng = Engine()
|
|
94
|
+
eng.add_device(dev)
|
|
95
|
+
eng.run()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
FAQ
|
|
99
|
+
~~~
|
|
100
|
+
The core engine run forever so how can I use it in webserver, GUI or to develop device
|
|
101
|
+
with IO. The whole API is absolutely not thread safe, so **don't use threads** unless you
|
|
102
|
+
exactly know what's going on. Anyways, you have several options to fix this issue:
|
|
103
|
+
|
|
104
|
+
* You can use you own loop and periodically call *eng.loop()*
|
|
105
|
+
for example, you can do something like this:
|
|
106
|
+
|
|
107
|
+
.. code:: python
|
|
108
|
+
|
|
109
|
+
while 1:
|
|
110
|
+
do_some_stuff()
|
|
111
|
+
eng.loop()
|
|
112
|
+
|
|
113
|
+
* You can use a engine timer, to perform some stuff.
|
|
114
|
+
|
|
115
|
+
.. code:: python
|
|
116
|
+
|
|
117
|
+
def read_io():
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
# call the read_io function every 10 sec
|
|
121
|
+
eng.add_timer(read_io,10)
|
|
122
|
+
eng.run()
|
|
123
|
+
|
|
124
|
+
* Use the **AsyncEngine**. Python version > 3.8 provides async programming with **asyncio** package.
|
|
125
|
+
*AsyncEngine* use the same API as *Engine*, but it is a **asynchronous** engine. You can use
|
|
126
|
+
*coroutines* in device methods, timers functions and callbacks. It provides additionals features
|
|
127
|
+
like the *on_start* and *on_stop* callbacks.
|
|
128
|
+
|
|
129
|
+
* Use an alternate coroutine lib, you can use **gevent** or **greenlet** for example. Look at
|
|
130
|
+
apps/rest for a simple greenlet example.
|
|
131
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
|
|
2
|
+
xaal.lib
|
|
3
|
+
========
|
|
4
|
+
**xaal.lib** is the official Python stack to develop home-automation devices and gateways
|
|
5
|
+
with the xAAL protocol. For a full description of the protocol check out
|
|
6
|
+
http://recherche.imt-atlantique.fr/xaal/
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
Dependencies
|
|
10
|
+
~~~~~~~~~~~~
|
|
11
|
+
xaal.lib depends on :
|
|
12
|
+
* cbor2
|
|
13
|
+
* pysodium
|
|
14
|
+
* configobj
|
|
15
|
+
* coloredlogs
|
|
16
|
+
* decorator
|
|
17
|
+
* tabulate
|
|
18
|
+
* aioconsole
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
Install
|
|
22
|
+
~~~~~~~
|
|
23
|
+
Please refer to the official `full documentation to install the lib in a virtualenv
|
|
24
|
+
<https://redmine.telecom-bretagne.eu/svn/xaal/code/Python/branches/0.7/README.html>`_
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
Usage
|
|
28
|
+
~~~~~
|
|
29
|
+
The main goal of xaal.lib is to provide an API to easily develop devices & gateways.
|
|
30
|
+
**xaal.lib.Engine send / receive / parse to|from xAAL Bus**.
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
To receive / parse / display incoming xAAL messages, you can simply try something like
|
|
34
|
+
this:
|
|
35
|
+
|
|
36
|
+
.. code-block:: python
|
|
37
|
+
|
|
38
|
+
from xaal.lib import Engine
|
|
39
|
+
|
|
40
|
+
def display(msg):
|
|
41
|
+
print(msg)
|
|
42
|
+
|
|
43
|
+
eng = Engine()
|
|
44
|
+
eng.subscribe(display)
|
|
45
|
+
eng.run()
|
|
46
|
+
|
|
47
|
+
The Engine will call the display function every time it receive a xAAL message.
|
|
48
|
+
|
|
49
|
+
Let's take a look at a simple lamp device :
|
|
50
|
+
|
|
51
|
+
.. code-block:: python
|
|
52
|
+
|
|
53
|
+
from xaal.lib import Device,Engine,tools
|
|
54
|
+
|
|
55
|
+
# create and configure the lamp device, with a random address
|
|
56
|
+
dev = Device("lamp.basic", tools.get_random_uuid())
|
|
57
|
+
dev.product_id = 'Dummy Lamp'
|
|
58
|
+
dev.url = 'http://www.acme.org'
|
|
59
|
+
dev.info = 'My fake lamp'
|
|
60
|
+
|
|
61
|
+
# add an xAAL attribute 'light'
|
|
62
|
+
light = dev.new_attribute('light')
|
|
63
|
+
|
|
64
|
+
# declare two device methods ON & OFF
|
|
65
|
+
def on():
|
|
66
|
+
light.value = True
|
|
67
|
+
|
|
68
|
+
def off():
|
|
69
|
+
light.value = False
|
|
70
|
+
|
|
71
|
+
dev.add_method('turn_on',on)
|
|
72
|
+
dev.add_method('turn_off',off)
|
|
73
|
+
|
|
74
|
+
# last step, create an engine and register the lamp
|
|
75
|
+
eng = Engine()
|
|
76
|
+
eng.add_device(dev)
|
|
77
|
+
eng.run()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
FAQ
|
|
81
|
+
~~~
|
|
82
|
+
The core engine run forever so how can I use it in webserver, GUI or to develop device
|
|
83
|
+
with IO. The whole API is absolutely not thread safe, so **don't use threads** unless you
|
|
84
|
+
exactly know what's going on. Anyways, you have several options to fix this issue:
|
|
85
|
+
|
|
86
|
+
* You can use you own loop and periodically call *eng.loop()*
|
|
87
|
+
for example, you can do something like this:
|
|
88
|
+
|
|
89
|
+
.. code:: python
|
|
90
|
+
|
|
91
|
+
while 1:
|
|
92
|
+
do_some_stuff()
|
|
93
|
+
eng.loop()
|
|
94
|
+
|
|
95
|
+
* You can use a engine timer, to perform some stuff.
|
|
96
|
+
|
|
97
|
+
.. code:: python
|
|
98
|
+
|
|
99
|
+
def read_io():
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
# call the read_io function every 10 sec
|
|
103
|
+
eng.add_timer(read_io,10)
|
|
104
|
+
eng.run()
|
|
105
|
+
|
|
106
|
+
* Use the **AsyncEngine**. Python version > 3.8 provides async programming with **asyncio** package.
|
|
107
|
+
*AsyncEngine* use the same API as *Engine*, but it is a **asynchronous** engine. You can use
|
|
108
|
+
*coroutines* in device methods, timers functions and callbacks. It provides additionals features
|
|
109
|
+
like the *on_start* and *on_stop* callbacks.
|
|
110
|
+
|
|
111
|
+
* Use an alternate coroutine lib, you can use **gevent** or **greenlet** for example. Look at
|
|
112
|
+
apps/rest for a simple greenlet example.
|
|
113
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "xaal.lib"
|
|
3
|
+
version = "0.7.2"
|
|
4
|
+
description = "xaal.lib is the official Python stack of xAAL protocol dedicated to home automation systems"
|
|
5
|
+
readme = "README.rst"
|
|
6
|
+
authors = [ { name = "Jerome Kerdreux", email = "Jerome.Kerdreux@imt-atlantique.fr" } ]
|
|
7
|
+
license = { text = "GPL License"}
|
|
8
|
+
classifiers = ['Programming Language :: Python', 'Topic :: Software Development :: Libraries :: Python Modules']
|
|
9
|
+
keywords = ['xaal', 'home-automation']
|
|
10
|
+
dependencies = ['cbor2==5.4.2', 'pysodium', 'configobj', 'coloredlogs', 'decorator', 'tabulate', 'aioconsole']
|
|
11
|
+
|
xaal_lib-0.7.2/setup.cfg
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from xaal.lib import bindings
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TestUUID(unittest.TestCase):
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_add_sub(self):
|
|
9
|
+
uuid = bindings.UUID("12345678-1234-1234-1234-123456789012")
|
|
10
|
+
uuid += 1
|
|
11
|
+
self.assertEqual(uuid,bindings.UUID("12345678-1234-1234-1234-123456789013"))
|
|
12
|
+
uuid -= 1
|
|
13
|
+
self.assertEqual(uuid,bindings.UUID("12345678-1234-1234-1234-123456789012"))
|
|
14
|
+
|
|
15
|
+
def test_random(self):
|
|
16
|
+
uuid = bindings.UUID.random()
|
|
17
|
+
self.assertIsInstance(uuid,bindings.UUID)
|
|
18
|
+
|
|
19
|
+
uuid = bindings.UUID.random_base()
|
|
20
|
+
self.assertIsInstance(uuid,bindings.UUID)
|
|
21
|
+
|
|
22
|
+
with self.assertRaises(bindings.UUIDError):
|
|
23
|
+
uuid = bindings.UUID.random_base(0)
|
|
24
|
+
|
|
25
|
+
def test_get_set(self):
|
|
26
|
+
uuid1 = bindings.UUID("12345678-1234-1234-1234-123456789012")
|
|
27
|
+
data = uuid1.get()
|
|
28
|
+
uuid2 = bindings.UUID.random()
|
|
29
|
+
uuid2.set(data)
|
|
30
|
+
self.assertEqual(uuid1,uuid2)
|
|
31
|
+
|
|
32
|
+
def test_str(self):
|
|
33
|
+
uuid = bindings.UUID("12345678-1234-1234-1234-123456789012")
|
|
34
|
+
self.assertEqual(str(uuid),"12345678-1234-1234-1234-123456789012")
|
|
35
|
+
self.assertEqual(uuid.str,"12345678-1234-1234-1234-123456789012")
|
|
36
|
+
|
|
37
|
+
def test_bytes(self):
|
|
38
|
+
uuid = bindings.UUID("12345678-1234-1234-1234-123456789012")
|
|
39
|
+
self.assertEqual(uuid.bytes,b'\x124Vx\x124\x124\x124\x124Vx\x90\x12')
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TestURL(unittest.TestCase):
|
|
43
|
+
|
|
44
|
+
def test_url(self):
|
|
45
|
+
url = bindings.URL("http://bar.com")
|
|
46
|
+
self.assertEqual(str(url), "http://bar.com")
|
|
47
|
+
url.set("http://foo.com")
|
|
48
|
+
print(url)
|
|
49
|
+
self.assertEqual(str(url), "http://foo.com")
|
|
50
|
+
self.assertEqual(url.str, "http://foo.com")
|
|
51
|
+
self.assertEqual(url.get(), "http://foo.com")
|
|
52
|
+
self.assertEqual(url.bytes, "http://foo.com")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
if __name__ == '__main__':
|
|
56
|
+
unittest.main()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from xaal.lib import cbor
|
|
3
|
+
from xaal.lib import bindings
|
|
4
|
+
import binascii
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestCbor(unittest.TestCase):
|
|
8
|
+
def test_encode(self):
|
|
9
|
+
value = cbor.dumps(42)
|
|
10
|
+
self.assertEqual(value, b"\x18*") # 0x18 0x2a
|
|
11
|
+
|
|
12
|
+
def test_decode(self):
|
|
13
|
+
value = cbor.loads(b"\x18*")
|
|
14
|
+
self.assertEqual(value, 42)
|
|
15
|
+
|
|
16
|
+
def test_list(self):
|
|
17
|
+
value = []
|
|
18
|
+
value.append(7)
|
|
19
|
+
value.append(1234567890)
|
|
20
|
+
data = cbor.dumps(value)
|
|
21
|
+
print(data)
|
|
22
|
+
self.assertEqual(data, binascii.unhexlify(b"82071a499602d2"))
|
|
23
|
+
|
|
24
|
+
def test_uuid(self):
|
|
25
|
+
uuid = bindings.UUID.random()
|
|
26
|
+
cbor_value = cbor.dumps(uuid)
|
|
27
|
+
value = cbor.loads(cbor_value)
|
|
28
|
+
self.assertEqual(value, uuid)
|
|
29
|
+
self.assertEqual(type(value), bindings.UUID)
|
|
30
|
+
|
|
31
|
+
def test_url(self):
|
|
32
|
+
url = bindings.URL("http://www.example.com")
|
|
33
|
+
cbor_value = cbor.dumps(url)
|
|
34
|
+
value = cbor.loads(cbor_value)
|
|
35
|
+
self.assertEqual(value, url)
|
|
36
|
+
self.assertEqual(type(value), bindings.URL)
|
|
37
|
+
|
|
38
|
+
def test_cleanup(self):
|
|
39
|
+
data = [
|
|
40
|
+
7,
|
|
41
|
+
"hello",
|
|
42
|
+
bindings.UUID("12345678-1234-1234-1234-123456789012"),
|
|
43
|
+
bindings.URL("http://www.example.com"),
|
|
44
|
+
{"hello": "world"},
|
|
45
|
+
]
|
|
46
|
+
cbor_value = cbor.dumps(data)
|
|
47
|
+
value = cbor.loads(cbor_value)
|
|
48
|
+
cbor.cleanup(value)
|
|
49
|
+
self.assertEqual(
|
|
50
|
+
value,
|
|
51
|
+
[
|
|
52
|
+
7,
|
|
53
|
+
"hello",
|
|
54
|
+
"12345678-1234-1234-1234-123456789012",
|
|
55
|
+
"http://www.example.com",
|
|
56
|
+
{"hello": "world"},
|
|
57
|
+
],
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
unittest.main()
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from xaal.lib import tools
|
|
3
|
+
from xaal.lib import Device,DeviceError,Attribute,Engine
|
|
4
|
+
from xaal.lib.devices import Attributes
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def new_dev() -> Device:
|
|
8
|
+
addr = tools.get_random_uuid()
|
|
9
|
+
dev = Device("light.basic",addr=addr)
|
|
10
|
+
return dev
|
|
11
|
+
|
|
12
|
+
class TestAttribute(unittest.TestCase):
|
|
13
|
+
|
|
14
|
+
def test_default(self):
|
|
15
|
+
attr = Attribute("foo",dev=new_dev(),default=1)
|
|
16
|
+
self.assertEqual(attr.value,1)
|
|
17
|
+
attr.value = 12
|
|
18
|
+
self.assertEqual(attr.value,12)
|
|
19
|
+
|
|
20
|
+
def test_with_engine(self):
|
|
21
|
+
dev = new_dev()
|
|
22
|
+
dev.engine = Engine()
|
|
23
|
+
attr = Attribute("foo",dev=dev,default=1,)
|
|
24
|
+
attr.value = 12
|
|
25
|
+
self.assertEqual(attr.value,12)
|
|
26
|
+
|
|
27
|
+
class TestAttributes(unittest.TestCase):
|
|
28
|
+
|
|
29
|
+
def test_default(self):
|
|
30
|
+
dev = new_dev()
|
|
31
|
+
attr = Attribute("foo",dev=dev,default=1)
|
|
32
|
+
attrs = Attributes()
|
|
33
|
+
attrs.append(attr)
|
|
34
|
+
self.assertEqual(attrs[0].value,1)
|
|
35
|
+
self.assertEqual(attrs["foo"],1)
|
|
36
|
+
|
|
37
|
+
with self.assertRaises(KeyError):
|
|
38
|
+
attrs["bar"]
|
|
39
|
+
|
|
40
|
+
attrs[0] = Attribute("bar",dev=dev,default=2)
|
|
41
|
+
self.assertEqual(attrs[0].value,2)
|
|
42
|
+
|
|
43
|
+
attrs["bar"] = 12
|
|
44
|
+
self.assertEqual(attrs["bar"],12)
|
|
45
|
+
|
|
46
|
+
with self.assertRaises(KeyError):
|
|
47
|
+
attrs["nop"]=12
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class TestDevice(unittest.TestCase):
|
|
51
|
+
|
|
52
|
+
def test_init(self):
|
|
53
|
+
dev_type="light.basic"
|
|
54
|
+
addr = tools.get_random_uuid()
|
|
55
|
+
dev = Device(dev_type=dev_type,addr=addr)
|
|
56
|
+
self.assertEqual(dev.dev_type, dev_type)
|
|
57
|
+
self.assertEqual(dev.address, addr)
|
|
58
|
+
|
|
59
|
+
def test_address(self):
|
|
60
|
+
dev = new_dev()
|
|
61
|
+
# address
|
|
62
|
+
addr = tools.get_random_uuid()
|
|
63
|
+
dev.address = addr
|
|
64
|
+
self.assertEqual(dev.address, addr)
|
|
65
|
+
# none address
|
|
66
|
+
dev.address = None
|
|
67
|
+
self.assertEqual(dev.address, None)
|
|
68
|
+
# invalid address
|
|
69
|
+
with self.assertRaises(DeviceError):
|
|
70
|
+
dev.address = "foo"
|
|
71
|
+
|
|
72
|
+
def test_dev_type(self):
|
|
73
|
+
dev = new_dev()
|
|
74
|
+
dev.dev_type = "foo.basic"
|
|
75
|
+
self.assertEqual(dev.dev_type, "foo.basic")
|
|
76
|
+
with self.assertRaises(DeviceError):
|
|
77
|
+
dev.dev_type = "foo"
|
|
78
|
+
|
|
79
|
+
def test_version(self):
|
|
80
|
+
dev = new_dev()
|
|
81
|
+
# version
|
|
82
|
+
dev.version = 12
|
|
83
|
+
self.assertEqual(dev.version, '12')
|
|
84
|
+
dev.version = None
|
|
85
|
+
self.assertEqual(dev.version, None)
|
|
86
|
+
|
|
87
|
+
def test_url(self):
|
|
88
|
+
dev = new_dev()
|
|
89
|
+
dev.url = "http://foo.bar"
|
|
90
|
+
self.assertEqual(dev.url, "http://foo.bar")
|
|
91
|
+
dev.url = None
|
|
92
|
+
self.assertEqual(dev.url, None)
|
|
93
|
+
|
|
94
|
+
def test_attributes(self):
|
|
95
|
+
dev = new_dev()
|
|
96
|
+
attr = Attribute("foo",dev=dev,default=1)
|
|
97
|
+
# add
|
|
98
|
+
dev.add_attribute(attr)
|
|
99
|
+
self.assertEqual(len(dev.attributes),1)
|
|
100
|
+
self.assertEqual(dev.attributes[0],attr)
|
|
101
|
+
# del
|
|
102
|
+
dev.del_attribute(attr)
|
|
103
|
+
self.assertEqual(len(dev.attributes),0)
|
|
104
|
+
# new
|
|
105
|
+
dev.new_attribute("foo",default=2)
|
|
106
|
+
self.assertEqual(len(dev.attributes),1)
|
|
107
|
+
self.assertEqual(dev.attributes[0].value,2)
|
|
108
|
+
# get
|
|
109
|
+
self.assertEqual(dev.get_attribute("foo").value,2)
|
|
110
|
+
self.assertEqual(dev.get_attribute("bar"),None)
|
|
111
|
+
|
|
112
|
+
# set
|
|
113
|
+
attr = Attribute("bar",dev=dev,default=3)
|
|
114
|
+
dev.attributes =Attributes([attr,])
|
|
115
|
+
self.assertEqual(len(dev.attributes),1)
|
|
116
|
+
self.assertEqual(dev.attributes[0].value,3)
|
|
117
|
+
# only accepts Attributes not list
|
|
118
|
+
with self.assertRaises(DeviceError):
|
|
119
|
+
dev.attributes = [attr,]
|
|
120
|
+
|
|
121
|
+
def test_get_attributes(self):
|
|
122
|
+
dev = new_dev()
|
|
123
|
+
dev.new_attribute("foo",default=1)
|
|
124
|
+
# _get_attributes
|
|
125
|
+
self.assertEqual(dev._get_attributes()["foo"],1)
|
|
126
|
+
data = dev._get_attributes(["foo","bar"])
|
|
127
|
+
self.assertEqual(len(data),1)
|
|
128
|
+
self.assertEqual(data["foo"],1)
|
|
129
|
+
|
|
130
|
+
def test_get_description(self):
|
|
131
|
+
dev = new_dev()
|
|
132
|
+
|
|
133
|
+
dev.vendor_id = 0x1234
|
|
134
|
+
dev.product_id = 0x1234
|
|
135
|
+
dev.version = '2f'
|
|
136
|
+
dev.url = "http://foo.bar"
|
|
137
|
+
dev.schema = "http://schemas.foo.bar/schema.json"
|
|
138
|
+
dev.info = "FooBar"
|
|
139
|
+
dev.hw_id = 0xf12
|
|
140
|
+
group = tools.get_random_uuid()
|
|
141
|
+
dev.group_id = group
|
|
142
|
+
|
|
143
|
+
dev.unsupported_methods = ["foo_func"]
|
|
144
|
+
dev.unsupported_attributes = ["foo_attr"]
|
|
145
|
+
dev.unsupported_notifications = ["foo_notif"]
|
|
146
|
+
|
|
147
|
+
data = dev._get_description()
|
|
148
|
+
self.assertEqual(data["vendor_id"], 0x1234)
|
|
149
|
+
self.assertEqual(data["product_id"], 0x1234)
|
|
150
|
+
self.assertEqual(data["version"], '2f')
|
|
151
|
+
self.assertEqual(data["url"], "http://foo.bar")
|
|
152
|
+
self.assertEqual(data["schema"], "http://schemas.foo.bar/schema.json")
|
|
153
|
+
self.assertEqual(data["info"], "FooBar")
|
|
154
|
+
self.assertEqual(data["hw_id"],0xf12)
|
|
155
|
+
self.assertEqual(data["group_id"],group)
|
|
156
|
+
|
|
157
|
+
self.assertEqual(data["unsupported_methods"],["foo_func"])
|
|
158
|
+
self.assertEqual(data["unsupported_notifications"],["foo_notif"])
|
|
159
|
+
self.assertEqual(data["unsupported_attributes"],["foo_attr"])
|
|
160
|
+
|
|
161
|
+
def test_methods(self):
|
|
162
|
+
dev = new_dev()
|
|
163
|
+
# device has two methods by default
|
|
164
|
+
self.assertEqual(len(dev.methods),2)
|
|
165
|
+
self.assertEqual(len(dev.get_methods()),2)
|
|
166
|
+
def func():pass
|
|
167
|
+
dev.add_method("foo",func)
|
|
168
|
+
self.assertEqual(dev.methods["foo"],func)
|
|
169
|
+
|
|
170
|
+
def test_dump(self):
|
|
171
|
+
dev = new_dev()
|
|
172
|
+
dev.info = 'FooBar'
|
|
173
|
+
dev.new_attribute("foo",default=1)
|
|
174
|
+
dev.dump()
|
|
175
|
+
|
|
176
|
+
def test_alive(self):
|
|
177
|
+
import time
|
|
178
|
+
now = time.time()
|
|
179
|
+
dev = new_dev()
|
|
180
|
+
dev.alive_period = 2
|
|
181
|
+
dev.update_alive()
|
|
182
|
+
self.assertTrue(dev.next_alive > now)
|
|
183
|
+
self.assertTrue(dev.next_alive < now+3)
|
|
184
|
+
self.assertEqual(dev.get_timeout(),4)
|
|
185
|
+
|
|
186
|
+
def test_send_notification(self):
|
|
187
|
+
# not really a test here, just to check if the method is called
|
|
188
|
+
# check engine unittest for futher tests
|
|
189
|
+
dev = new_dev()
|
|
190
|
+
dev.engine = Engine()
|
|
191
|
+
dev.send_notification("foo")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
if __name__ == '__main__':
|
|
195
|
+
unittest.main()
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import unittest
|
|
3
|
+
from xaal.lib import Engine, Device, MessageType, CallbackError
|
|
4
|
+
from xaal.lib import engine, tools
|
|
5
|
+
from xaal.lib.messages import Message
|
|
6
|
+
|
|
7
|
+
TEST_PORT = 6666
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def new_engine():
|
|
11
|
+
engine = Engine(port=TEST_PORT)
|
|
12
|
+
return engine
|
|
13
|
+
|
|
14
|
+
class TestEngine(unittest.TestCase):
|
|
15
|
+
|
|
16
|
+
def test_devices(self):
|
|
17
|
+
dev = Device("test.basic", tools.get_random_uuid())
|
|
18
|
+
eng = Engine(port=TEST_PORT)
|
|
19
|
+
eng.add_device(dev)
|
|
20
|
+
self.assertEqual(eng.devices,[dev,])
|
|
21
|
+
eng.remove_device(dev)
|
|
22
|
+
self.assertEqual(eng.devices,[])
|
|
23
|
+
eng.add_devices([dev,])
|
|
24
|
+
self.assertEqual(eng.devices,[dev,])
|
|
25
|
+
|
|
26
|
+
def test_start_stop(self):
|
|
27
|
+
eng = Engine(port=TEST_PORT)
|
|
28
|
+
dev = Device("test.basic", tools.get_random_uuid())
|
|
29
|
+
eng.add_device(dev)
|
|
30
|
+
eng.start()
|
|
31
|
+
eng.start() # second start
|
|
32
|
+
self.assertEqual(engine.EngineState.started, eng.state)
|
|
33
|
+
eng.loop()
|
|
34
|
+
eng.stop()
|
|
35
|
+
self.assertEqual(engine.EngineState.halted, eng.state)
|
|
36
|
+
|
|
37
|
+
def test_timer(self):
|
|
38
|
+
eng = Engine(port=TEST_PORT)
|
|
39
|
+
t0 = time.time()
|
|
40
|
+
def _exit():
|
|
41
|
+
eng.shutdown()
|
|
42
|
+
eng.add_timer(_exit, 1, 1)
|
|
43
|
+
eng.run()
|
|
44
|
+
t = time.time() - t0
|
|
45
|
+
self.assertTrue(t > 1)
|
|
46
|
+
self.assertTrue(t < 2)
|
|
47
|
+
|
|
48
|
+
def test_timer_error(self):
|
|
49
|
+
eng = Engine(port=TEST_PORT)
|
|
50
|
+
eng.start()
|
|
51
|
+
def _error():
|
|
52
|
+
raise CallbackError(500,"test error")
|
|
53
|
+
eng.add_timer(_error,0,1)
|
|
54
|
+
eng.loop()
|
|
55
|
+
eng.stop()
|
|
56
|
+
self.assertEqual(engine.EngineState.halted, eng.state)
|
|
57
|
+
|
|
58
|
+
def test_run_action(self):
|
|
59
|
+
target = Device("test.basic", tools.get_random_uuid())
|
|
60
|
+
|
|
61
|
+
def action_1():
|
|
62
|
+
return "action_1"
|
|
63
|
+
|
|
64
|
+
def action_2(_value=None):
|
|
65
|
+
return "action_%s" % _value
|
|
66
|
+
|
|
67
|
+
def action_3():
|
|
68
|
+
raise Exception
|
|
69
|
+
|
|
70
|
+
target.add_method("action_1", action_1)
|
|
71
|
+
target.add_method("action_2", action_2)
|
|
72
|
+
target.add_method("action_3", action_3)
|
|
73
|
+
|
|
74
|
+
msg = Message()
|
|
75
|
+
msg.msg_type = MessageType.REQUEST
|
|
76
|
+
msg.targets = [target.address]
|
|
77
|
+
# simple test method
|
|
78
|
+
msg.action = "action_1"
|
|
79
|
+
result = engine.run_action(msg, target)
|
|
80
|
+
self.assertEqual(result, "action_1")
|
|
81
|
+
# test with value
|
|
82
|
+
msg.action = "action_2"
|
|
83
|
+
msg.body = {"value": "2"}
|
|
84
|
+
result = engine.run_action(msg, target)
|
|
85
|
+
self.assertEqual(result, "action_2")
|
|
86
|
+
# Exception in method
|
|
87
|
+
msg.action = "action_3"
|
|
88
|
+
with self.assertRaises(engine.XAALError):
|
|
89
|
+
result = engine.run_action(msg, target)
|
|
90
|
+
# unknown method
|
|
91
|
+
msg.action = "missing"
|
|
92
|
+
with self.assertRaises(engine.XAALError):
|
|
93
|
+
result = engine.run_action(msg, target)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
unittest.main()
|