twist-innovation-api 0.0.1__py3-none-any.whl → 0.0.3__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.
- twist/TwistAPI.py +18 -21
- twist/TwistCbShutter.py +4 -1
- twist/TwistDevice.py +2 -3
- twist/TwistLight.py +16 -12
- twist/TwistLouvre.py +18 -14
- twist/TwistModel.py +28 -8
- twist/TwistRgb.py +19 -14
- twist/TwistSensor.py +4 -1
- {twist_innovation_api-0.0.1.dist-info → twist_innovation_api-0.0.3.dist-info}/METADATA +55 -25
- twist_innovation_api-0.0.3.dist-info/RECORD +16 -0
- twist_innovation_api-0.0.1.dist-info/RECORD +0 -16
- {twist_innovation_api-0.0.1.dist-info → twist_innovation_api-0.0.3.dist-info}/WHEEL +0 -0
- {twist_innovation_api-0.0.1.dist-info → twist_innovation_api-0.0.3.dist-info}/licenses/LICENSE +0 -0
- {twist_innovation_api-0.0.1.dist-info → twist_innovation_api-0.0.3.dist-info}/top_level.txt +0 -0
twist/TwistAPI.py
CHANGED
|
@@ -13,18 +13,17 @@
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
import asyncio
|
|
16
|
-
from typing import Callable
|
|
17
|
-
import random
|
|
18
16
|
import json
|
|
17
|
+
import random
|
|
18
|
+
|
|
19
|
+
from .TwistDevice import TwistDevice, TwistModel
|
|
19
20
|
from .TwistTypes import DeviceVariant
|
|
20
|
-
from .TwistDevice import TwistModel, TwistDevice
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class TwistAPI:
|
|
24
|
-
def __init__(self, installation_id: int
|
|
24
|
+
def __init__(self, installation_id: int):
|
|
25
25
|
self._ext_publish = None
|
|
26
26
|
self._subscribe = None
|
|
27
|
-
self._callback = model_update
|
|
28
27
|
|
|
29
28
|
self.function_map = {
|
|
30
29
|
"context": self._context_msg,
|
|
@@ -36,14 +35,14 @@ class TwistAPI:
|
|
|
36
35
|
# TODO: this should come from the API
|
|
37
36
|
self.installation_id = installation_id
|
|
38
37
|
|
|
39
|
-
def add_mqtt(self, publisher, subscriber):
|
|
38
|
+
async def add_mqtt(self, publisher, subscriber):
|
|
40
39
|
self._ext_publish = publisher
|
|
41
40
|
self._subscribe = subscriber
|
|
42
41
|
|
|
43
|
-
self._subscribe(f"v2/{self.installation_id}/rx/#", self._on_message_received)
|
|
42
|
+
await self._subscribe(f"v2/{self.installation_id}/rx/#", self._on_message_received)
|
|
44
43
|
|
|
45
44
|
async def search_models(self):
|
|
46
|
-
self.getboard(0xffffffff)
|
|
45
|
+
await self.getboard(0xffffffff)
|
|
47
46
|
await asyncio.sleep(3)
|
|
48
47
|
|
|
49
48
|
model_list: list[TwistModel] = list()
|
|
@@ -53,7 +52,7 @@ class TwistAPI:
|
|
|
53
52
|
|
|
54
53
|
return model_list
|
|
55
54
|
|
|
56
|
-
def _publish(self, twist_id, opcode, payload: dict | None = None, model_id=None):
|
|
55
|
+
async def _publish(self, twist_id, opcode, payload: dict | None = None, model_id=None):
|
|
57
56
|
if self._ext_publish is not None:
|
|
58
57
|
topic = f"v2/{self.installation_id}/tx/{twist_id}/{opcode}"
|
|
59
58
|
if payload is None:
|
|
@@ -62,13 +61,13 @@ class TwistAPI:
|
|
|
62
61
|
if model_id is not None:
|
|
63
62
|
topic = f"{topic}/{model_id}"
|
|
64
63
|
|
|
65
|
-
self._ext_publish(topic, json.dumps(payload))
|
|
64
|
+
await self._ext_publish(topic, json.dumps(payload))
|
|
66
65
|
|
|
67
|
-
def getboard(self, twist_id):
|
|
68
|
-
|
|
66
|
+
async def getboard(self, twist_id):
|
|
67
|
+
await self._publish(twist_id, "getboard")
|
|
69
68
|
|
|
70
|
-
def activate_event(self, model: TwistModel, data: json):
|
|
71
|
-
self._publish(model.parent_device.twist_id, "activate_event", data, model.model_id)
|
|
69
|
+
async def activate_event(self, model: TwistModel, data: json):
|
|
70
|
+
await self._publish(model.parent_device.twist_id, "activate_event", data, model.model_id)
|
|
72
71
|
|
|
73
72
|
def _parse_topic(self, topic):
|
|
74
73
|
tpc_delim = topic.split('/')
|
|
@@ -83,19 +82,17 @@ class TwistAPI:
|
|
|
83
82
|
"model_id": model_id
|
|
84
83
|
}
|
|
85
84
|
|
|
86
|
-
def _on_message_received(self, topic, payload, qos=None):
|
|
85
|
+
async def _on_message_received(self, topic, payload, qos=None):
|
|
87
86
|
data = self._parse_topic(topic)
|
|
88
87
|
if any(dev.twist_id == data["twist_id"] for dev in self.device_list) or data["opcode"] == "getboard":
|
|
89
88
|
if data["opcode"] in self.function_map:
|
|
90
|
-
self.function_map[data["opcode"]](data["twist_id"], payload, data["model_id"])
|
|
89
|
+
await self.function_map[data["opcode"]](data["twist_id"], payload, data["model_id"])
|
|
91
90
|
|
|
92
|
-
def _context_msg(self, twist_id, payload, model_id):
|
|
91
|
+
async def _context_msg(self, twist_id, payload, model_id):
|
|
93
92
|
device = next((d for d in self.device_list if d.twist_id == twist_id), None)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
self._callback(model)
|
|
93
|
+
await device.context_msg(model_id, payload)
|
|
97
94
|
|
|
98
|
-
def _get_board(self, twist_id, payload, model_id=None):
|
|
95
|
+
async def _get_board(self, twist_id, payload, model_id=None):
|
|
99
96
|
data = json.loads(payload)
|
|
100
97
|
if not any(dev.twist_id == twist_id for dev in self.device_list):
|
|
101
98
|
self.device_list.append(TwistDevice(twist_id, data["h"], DeviceVariant(data["v"]), self))
|
twist/TwistCbShutter.py
CHANGED
|
@@ -23,8 +23,11 @@ class TwistCbShutter(TwistModel):
|
|
|
23
23
|
def __init__(self, model_id: int, parent_device: TwistDevice):
|
|
24
24
|
super().__init__(model_id, parent_device)
|
|
25
25
|
|
|
26
|
-
def context_msg(self, payload: str):
|
|
26
|
+
async def context_msg(self, payload: str):
|
|
27
27
|
self.parse_general_context(payload)
|
|
28
28
|
|
|
29
|
+
if self._update_callback is not None:
|
|
30
|
+
await self._update_callback(self)
|
|
31
|
+
|
|
29
32
|
def print_context(self):
|
|
30
33
|
print(f"Cb Shutter Device: {self.parent_device.twist_id}, Model: {self.model_id},")
|
twist/TwistDevice.py
CHANGED
|
@@ -37,6 +37,5 @@ class TwistDevice:
|
|
|
37
37
|
self.model_list.append(model(model_id, self))
|
|
38
38
|
model_id += 1
|
|
39
39
|
|
|
40
|
-
def context_msg(self, model_id: int, payload: str):
|
|
41
|
-
self.model_list[model_id].context_msg(payload)
|
|
42
|
-
return self.model_list[model_id]
|
|
40
|
+
async def context_msg(self, model_id: int, payload: str):
|
|
41
|
+
await self.model_list[model_id].context_msg(payload)
|
twist/TwistLight.py
CHANGED
|
@@ -34,22 +34,23 @@ class TwistLight(TwistModel):
|
|
|
34
34
|
self.operating_time = 0
|
|
35
35
|
self.current = 0
|
|
36
36
|
|
|
37
|
-
def turn_on(self):
|
|
38
|
-
self._activate_event(TwistLight.EventIndexes.SET)
|
|
37
|
+
async def turn_on(self):
|
|
38
|
+
await self._activate_event(TwistLight.EventIndexes.SET)
|
|
39
39
|
|
|
40
|
-
def turn_off(self):
|
|
41
|
-
self._activate_event(TwistLight.EventIndexes.CLEAR)
|
|
40
|
+
async def turn_off(self):
|
|
41
|
+
await self._activate_event(TwistLight.EventIndexes.CLEAR)
|
|
42
42
|
|
|
43
|
-
def toggle(self):
|
|
44
|
-
self._activate_event(TwistLight.EventIndexes.TOGGLE)
|
|
43
|
+
async def toggle(self):
|
|
44
|
+
await self._activate_event(TwistLight.EventIndexes.TOGGLE)
|
|
45
45
|
|
|
46
|
-
def set_value(self, value: int, fading_time: int | None = None):
|
|
46
|
+
async def set_value(self, value: int, fading_time: int | None = None):
|
|
47
|
+
value = value * 655.35
|
|
47
48
|
if fading_time is None:
|
|
48
|
-
self._activate_event(TwistLight.EventIndexes.VALUE, value)
|
|
49
|
+
await self._activate_event(TwistLight.EventIndexes.VALUE, value)
|
|
49
50
|
else:
|
|
50
|
-
self._activate_event(TwistLight.EventIndexes.VALUE_FADING, value, fading_time)
|
|
51
|
+
await self._activate_event(TwistLight.EventIndexes.VALUE_FADING, value, fading_time)
|
|
51
52
|
|
|
52
|
-
def _activate_event(self, index: TwistLight.EventIndexes, value: int | None = None, fading_time: int | None = None):
|
|
53
|
+
async def _activate_event(self, index: TwistLight.EventIndexes, value: int | None = None, fading_time: int | None = None):
|
|
53
54
|
data = {
|
|
54
55
|
"i": index.value
|
|
55
56
|
}
|
|
@@ -61,9 +62,9 @@ class TwistLight(TwistModel):
|
|
|
61
62
|
else:
|
|
62
63
|
data["vl"] = [value, fading_time]
|
|
63
64
|
|
|
64
|
-
self.parent_device.api.activate_event(self, data)
|
|
65
|
+
await self.parent_device.api.activate_event(self, data)
|
|
65
66
|
|
|
66
|
-
def context_msg(self, payload: str):
|
|
67
|
+
async def context_msg(self, payload: str):
|
|
67
68
|
data = self.parse_general_context(payload)
|
|
68
69
|
|
|
69
70
|
for ctx in data["cl"]:
|
|
@@ -79,6 +80,9 @@ class TwistLight(TwistModel):
|
|
|
79
80
|
elif index == 7:
|
|
80
81
|
self.current = value[0]
|
|
81
82
|
|
|
83
|
+
if self._update_callback is not None:
|
|
84
|
+
await self._update_callback(self)
|
|
85
|
+
|
|
82
86
|
def print_context(self):
|
|
83
87
|
print(
|
|
84
88
|
f"Light Device: {self.parent_device.twist_id}, Model: {self.model_id}, Actual: {self.actual_state}, "
|
twist/TwistLouvre.py
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
# https://www.gnu.org/licenses/gpl-3.0.html
|
|
13
13
|
|
|
14
14
|
from __future__ import annotations
|
|
15
|
+
|
|
15
16
|
from typing import TYPE_CHECKING
|
|
16
17
|
|
|
17
18
|
if TYPE_CHECKING:
|
|
@@ -34,7 +35,7 @@ class TwistLouvre(TwistModel):
|
|
|
34
35
|
self.operating_time = 0
|
|
35
36
|
self.current = 0
|
|
36
37
|
|
|
37
|
-
def context_msg(self, payload: str):
|
|
38
|
+
async def context_msg(self, payload: str):
|
|
38
39
|
data = self.parse_general_context(payload)
|
|
39
40
|
|
|
40
41
|
for ctx in data["cl"]:
|
|
@@ -50,26 +51,29 @@ class TwistLouvre(TwistModel):
|
|
|
50
51
|
elif index == 7:
|
|
51
52
|
self.current = value[0]
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
if self._update_callback is not None:
|
|
55
|
+
await self._update_callback(self)
|
|
56
|
+
|
|
57
|
+
async def open(self):
|
|
58
|
+
await self._activate_event(TwistLouvre.EventIndexes.OPEN)
|
|
55
59
|
|
|
56
|
-
def stop(self):
|
|
57
|
-
self._activate_event(TwistLouvre.EventIndexes.STOP)
|
|
60
|
+
async def stop(self):
|
|
61
|
+
await self._activate_event(TwistLouvre.EventIndexes.STOP)
|
|
58
62
|
|
|
59
|
-
def close(self):
|
|
60
|
-
self._activate_event(TwistLouvre.EventIndexes.CLOSE)
|
|
63
|
+
async def close(self):
|
|
64
|
+
await self._activate_event(TwistLouvre.EventIndexes.CLOSE)
|
|
61
65
|
|
|
62
|
-
def toggle(self):
|
|
63
|
-
self._activate_event(TwistLouvre.EventIndexes.TOGGLE)
|
|
66
|
+
async def toggle(self):
|
|
67
|
+
await self._activate_event(TwistLouvre.EventIndexes.TOGGLE)
|
|
64
68
|
|
|
65
|
-
def set_value(self, value: int, fading_time: int | None = None):
|
|
69
|
+
async def set_value(self, value: int, fading_time: int | None = None):
|
|
66
70
|
if fading_time is not None:
|
|
67
71
|
raise NotImplementedError("Fading time can't be used in this model")
|
|
68
72
|
else:
|
|
69
|
-
self._activate_event(TwistLouvre.EventIndexes.VALUE, value)
|
|
73
|
+
await self._activate_event(TwistLouvre.EventIndexes.VALUE, int(value * 655.35))
|
|
70
74
|
|
|
71
|
-
def _activate_event(self, index: TwistLouvre.EventIndexes, value: int | None = None,
|
|
72
|
-
|
|
75
|
+
async def _activate_event(self, index: TwistLouvre.EventIndexes, value: int | None = None,
|
|
76
|
+
fading_time: int | None = None):
|
|
73
77
|
data = {
|
|
74
78
|
"i": index.value
|
|
75
79
|
}
|
|
@@ -81,7 +85,7 @@ class TwistLouvre(TwistModel):
|
|
|
81
85
|
else:
|
|
82
86
|
data["vl"] = [value, fading_time]
|
|
83
87
|
|
|
84
|
-
self.parent_device.api.activate_event(self, data)
|
|
88
|
+
await self.parent_device.api.activate_event(self, data)
|
|
85
89
|
|
|
86
90
|
def print_context(self):
|
|
87
91
|
print(
|
twist/TwistModel.py
CHANGED
|
@@ -12,12 +12,15 @@
|
|
|
12
12
|
# https://www.gnu.org/licenses/gpl-3.0.html
|
|
13
13
|
|
|
14
14
|
from __future__ import annotations
|
|
15
|
+
|
|
15
16
|
from typing import TYPE_CHECKING
|
|
16
17
|
|
|
17
18
|
if TYPE_CHECKING:
|
|
18
19
|
from TwistDevice import TwistDevice
|
|
19
20
|
|
|
20
21
|
import json
|
|
22
|
+
|
|
23
|
+
from typing import Callable, Awaitable
|
|
21
24
|
from .TwistTypes import ContextErrors
|
|
22
25
|
|
|
23
26
|
|
|
@@ -34,9 +37,13 @@ class TwistModel():
|
|
|
34
37
|
"prio": None
|
|
35
38
|
}
|
|
36
39
|
|
|
40
|
+
self._update_callback: Callable[[TwistModel], Awaitable[None]] | None = None
|
|
41
|
+
|
|
42
|
+
|
|
37
43
|
def print_context(self):
|
|
38
44
|
print("function is not supported")
|
|
39
45
|
|
|
46
|
+
|
|
40
47
|
def parse_general_context(self, payload: str):
|
|
41
48
|
data = json.loads(payload)
|
|
42
49
|
|
|
@@ -54,29 +61,42 @@ class TwistModel():
|
|
|
54
61
|
self.errors["prio"] = value
|
|
55
62
|
return data
|
|
56
63
|
|
|
64
|
+
|
|
57
65
|
def _get_value_from_context(self, ctx: dict):
|
|
58
66
|
return ctx["i"], ctx["vl"]
|
|
59
67
|
|
|
60
|
-
|
|
68
|
+
|
|
69
|
+
async def context_msg(self, payload):
|
|
61
70
|
raise NotImplementedError("Function not supported for this model")
|
|
62
71
|
|
|
63
|
-
|
|
72
|
+
|
|
73
|
+
async def turn_on(self):
|
|
64
74
|
raise NotImplementedError("Function not supported for this model")
|
|
65
75
|
|
|
66
|
-
|
|
76
|
+
|
|
77
|
+
async def turn_off(self):
|
|
67
78
|
raise NotImplementedError("Function not supported for this model")
|
|
68
79
|
|
|
69
|
-
|
|
80
|
+
|
|
81
|
+
async def open(self):
|
|
70
82
|
raise NotImplementedError("Function not supported for this model")
|
|
71
83
|
|
|
72
|
-
|
|
84
|
+
|
|
85
|
+
async def stop(self):
|
|
73
86
|
raise NotImplementedError("Function not supported for this model")
|
|
74
87
|
|
|
75
|
-
|
|
88
|
+
|
|
89
|
+
async def close(self):
|
|
76
90
|
raise NotImplementedError("Function not supported for this model")
|
|
77
91
|
|
|
78
|
-
|
|
92
|
+
|
|
93
|
+
async def toggle(self):
|
|
79
94
|
raise NotImplementedError("Function not supported for this model")
|
|
80
95
|
|
|
81
|
-
|
|
96
|
+
|
|
97
|
+
async def set_value(self, value: int | list[int, int] | list[int, int, int], fading_time: int | None = None):
|
|
82
98
|
raise NotImplementedError("Function not supported for this model")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def register_update_cb(self, cb: Callable[[TwistModel], Awaitable[None]]):
|
|
102
|
+
self._update_callback = cb
|
twist/TwistRgb.py
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
# https://www.gnu.org/licenses/gpl-3.0.html
|
|
13
13
|
|
|
14
14
|
from __future__ import annotations
|
|
15
|
+
|
|
15
16
|
from typing import TYPE_CHECKING
|
|
16
17
|
|
|
17
18
|
if TYPE_CHECKING:
|
|
@@ -42,23 +43,23 @@ class TwistRgb(TwistModel):
|
|
|
42
43
|
|
|
43
44
|
self.operating_time = 0
|
|
44
45
|
|
|
45
|
-
def turn_on(self):
|
|
46
|
-
self._activate_event(TwistRgb.EventIndexes.SET)
|
|
46
|
+
async def turn_on(self):
|
|
47
|
+
await self._activate_event(TwistRgb.EventIndexes.SET)
|
|
47
48
|
|
|
48
|
-
def turn_off(self):
|
|
49
|
-
self._activate_event(TwistRgb.EventIndexes.CLEAR)
|
|
49
|
+
async def turn_off(self):
|
|
50
|
+
await self._activate_event(TwistRgb.EventIndexes.CLEAR)
|
|
50
51
|
|
|
51
|
-
def toggle(self):
|
|
52
|
-
self._activate_event(TwistRgb.EventIndexes.TOGGLE)
|
|
52
|
+
async def toggle(self):
|
|
53
|
+
await self._activate_event(TwistRgb.EventIndexes.TOGGLE)
|
|
53
54
|
|
|
54
|
-
def set_value(self, value: list[int, int, int], fading_time: int | None = None):
|
|
55
|
+
async def set_value(self, value: list[int, int, int], fading_time: int | None = None):
|
|
55
56
|
if fading_time is None:
|
|
56
|
-
self._activate_event(TwistRgb.EventIndexes.VALUE, value)
|
|
57
|
+
await self._activate_event(TwistRgb.EventIndexes.VALUE, value)
|
|
57
58
|
else:
|
|
58
|
-
self._activate_event(TwistRgb.EventIndexes.VALUE_FADING, value, fading_time)
|
|
59
|
+
await self._activate_event(TwistRgb.EventIndexes.VALUE_FADING, value, fading_time)
|
|
59
60
|
|
|
60
|
-
def _activate_event(self, index: TwistRgb.EventIndexes, value:
|
|
61
|
-
|
|
61
|
+
async def _activate_event(self, index: TwistRgb.EventIndexes, value: tuple[int, int, int] | None = None,
|
|
62
|
+
fading_time: int | None = None):
|
|
62
63
|
data = {
|
|
63
64
|
"i": index.value
|
|
64
65
|
}
|
|
@@ -66,14 +67,15 @@ class TwistRgb(TwistModel):
|
|
|
66
67
|
if value is None:
|
|
67
68
|
data["vl"] = []
|
|
68
69
|
elif fading_time is None:
|
|
69
|
-
data["vl"] = value
|
|
70
|
+
data["vl"] = [int(v / 655.35) for v in value]
|
|
70
71
|
else:
|
|
72
|
+
value = list(value)
|
|
71
73
|
value.append(fading_time)
|
|
72
74
|
data["vl"] = value
|
|
73
75
|
|
|
74
|
-
self.parent_device.api.activate_event(self, data)
|
|
76
|
+
await self.parent_device.api.activate_event(self, data)
|
|
75
77
|
|
|
76
|
-
def context_msg(self, payload: str):
|
|
78
|
+
async def context_msg(self, payload: str):
|
|
77
79
|
data = self.parse_general_context(payload)
|
|
78
80
|
|
|
79
81
|
for ctx in data["cl"]:
|
|
@@ -91,6 +93,9 @@ class TwistRgb(TwistModel):
|
|
|
91
93
|
if index == 6:
|
|
92
94
|
self.operating_time = value[0]
|
|
93
95
|
|
|
96
|
+
if self._update_callback is not None:
|
|
97
|
+
await self._update_callback(self)
|
|
98
|
+
|
|
94
99
|
def print_context(self):
|
|
95
100
|
print(f"Rgb Device: {self.parent_device.twist_id}, Model: {self.model_id}, "
|
|
96
101
|
f"Actual: h{self.actual_h} s{self.actual_s} v{self.actual_v}")
|
twist/TwistSensor.py
CHANGED
|
@@ -24,8 +24,11 @@ class TwistSensor(TwistModel):
|
|
|
24
24
|
def __init__(self, model_id: int, parent_device: TwistDevice):
|
|
25
25
|
super().__init__(model_id, parent_device)
|
|
26
26
|
|
|
27
|
-
def context_msg(self, payload: str):
|
|
27
|
+
async def context_msg(self, payload: str):
|
|
28
28
|
self.parse_general_context(payload)
|
|
29
29
|
|
|
30
|
+
if self._update_callback is not None:
|
|
31
|
+
await self._update_callback(self)
|
|
32
|
+
|
|
30
33
|
def print_context(self):
|
|
31
34
|
print(f"Sensor Device: {self.parent_device.twist_id}, Model: {self.model_id}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: twist-innovation-api
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: Python library to talk to the twist-innovation api
|
|
5
5
|
Home-page: https://github.com/twist-innovation/twist-innovation-api
|
|
6
6
|
Author: Sibrecht Goudsmedt
|
|
@@ -79,46 +79,76 @@ twine upload dist/*
|
|
|
79
79
|
Import the package in your Python scripts:
|
|
80
80
|
|
|
81
81
|
```python
|
|
82
|
-
from twist import TwistAPI, TwistModel
|
|
83
|
-
|
|
84
|
-
# Other includes for this example
|
|
85
82
|
import asyncio
|
|
86
|
-
|
|
83
|
+
import yaml
|
|
84
|
+
from typing import Callable, Awaitable
|
|
85
|
+
import aiomqtt
|
|
87
86
|
|
|
88
|
-
|
|
87
|
+
from twist import TwistAPI, TwistModel
|
|
89
88
|
|
|
90
|
-
#
|
|
91
|
-
|
|
89
|
+
# Load configuration
|
|
90
|
+
with open("config.yaml", 'r') as file:
|
|
91
|
+
config = yaml.safe_load(file)
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
mqtt_broker = config["mqtt_broker"]
|
|
94
|
+
mqtt_user = config["mqtt_user"]
|
|
95
|
+
mqtt_pass = config["mqtt_pass"]
|
|
96
|
+
mqtt_port = config["mqtt_port"]
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
def mqtt_publish(topic, payload):
|
|
98
|
+
async def _noop(topic: str, payload: str):
|
|
100
99
|
pass
|
|
101
100
|
|
|
102
|
-
|
|
103
|
-
def mqtt_subscribe(topic, callback):
|
|
104
|
-
global callback_f
|
|
105
|
-
callback_f = callback
|
|
101
|
+
callback_f :Callable[[str, str], Awaitable[None]] = _noop
|
|
106
102
|
|
|
107
|
-
|
|
108
|
-
def on_model_update(model: TwistModel):
|
|
103
|
+
|
|
104
|
+
async def on_model_update(model: TwistModel):
|
|
109
105
|
model.print_context()
|
|
110
106
|
|
|
107
|
+
|
|
111
108
|
async def main():
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
109
|
+
global callback_f
|
|
110
|
+
|
|
111
|
+
async with aiomqtt.Client(
|
|
112
|
+
hostname=mqtt_broker,
|
|
113
|
+
port=mqtt_port,
|
|
114
|
+
username=mqtt_user,
|
|
115
|
+
password=mqtt_pass
|
|
116
|
+
) as mqtt_client:
|
|
115
117
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
+
# Async publish method
|
|
119
|
+
async def mqtt_publish(topic: str, payload: str):
|
|
120
|
+
await mqtt_client.publish(topic, payload)
|
|
121
|
+
|
|
122
|
+
# Async subscribe method
|
|
123
|
+
async def mqtt_subscribe(topic: str, callback: Callable[[str, str], None]):
|
|
124
|
+
global callback_f
|
|
125
|
+
callback_f = callback
|
|
126
|
+
|
|
127
|
+
async def listen():
|
|
128
|
+
await mqtt_client.subscribe(topic)
|
|
129
|
+
async for message in mqtt_client.messages:
|
|
130
|
+
assert callback_f is not None
|
|
131
|
+
await callback_f(message.topic.value, message.payload.decode())
|
|
132
|
+
|
|
133
|
+
asyncio.create_task(listen())
|
|
134
|
+
|
|
135
|
+
# Initialize Twist API with async methods
|
|
136
|
+
twist_api = TwistAPI(8)
|
|
137
|
+
await twist_api.add_mqtt(mqtt_publish, mqtt_subscribe)
|
|
138
|
+
|
|
139
|
+
twist_model_list: list[TwistModel] = await twist_api.search_models()
|
|
140
|
+
|
|
141
|
+
for model in twist_model_list:
|
|
142
|
+
model.register_update_cb(on_model_update)
|
|
143
|
+
print(f"{type(model)} has Model id: {model.model_id}, Device id: {model.parent_device.twist_id}")
|
|
144
|
+
|
|
145
|
+
while True:
|
|
146
|
+
await asyncio.sleep(1)
|
|
118
147
|
|
|
119
148
|
|
|
120
149
|
if __name__ == "__main__":
|
|
121
150
|
asyncio.run(main())
|
|
151
|
+
|
|
122
152
|
```
|
|
123
153
|
|
|
124
154
|
## License
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
twist/TwistAPI.py,sha256=LBLBGgwY66lVBNSto4VjPEpzj9kmDzXTCwpW9f3OSic,3579
|
|
2
|
+
twist/TwistCbShutter.py,sha256=mWQa4ro30uI8qm0J1rkR8M0KP7IwxlCRfWt0wSyCOLA,1199
|
|
3
|
+
twist/TwistDevice.py,sha256=PoPKnNrbiy1j55MyZJW4nDg9OXtGZI7CgHlxcDEQhqs,1380
|
|
4
|
+
twist/TwistLight.py,sha256=6f-gPfP6v7BgaRJUFt7Q3r990y2L0812d76K_33KSTw,3230
|
|
5
|
+
twist/TwistLouvre.py,sha256=f2ocRjLkf3qC45hbcHdEOboA2mbkaxLaOa15tvkHOHY,3320
|
|
6
|
+
twist/TwistModel.py,sha256=SQHpYmpdV7PfZij69-TIHDa_77QabamZ71f9ZUeyvOk,3179
|
|
7
|
+
twist/TwistRgb.py,sha256=V7YuaqJaLTH3cL8CsQ3xIztfVKfqdMMJ7uyh9UZQDwA,3572
|
|
8
|
+
twist/TwistSensor.py,sha256=6eYrwtZFFFOHmoHS68JHmsyi6FVVzoQy19KooGU0vMI,1192
|
|
9
|
+
twist/TwistTypes.py,sha256=wsp-lnkaU70wNdoKWOWcnsq89vBheBYmM31ZXW3FMRw,2166
|
|
10
|
+
twist/Variants.py,sha256=SFAZ7bSmTRomPspOfU1qNWEW-htRuyhTKuyGKK9Qh54,1369
|
|
11
|
+
twist/__init__.py,sha256=gf8GHc9utiG0vD8GP3IfzLFHAG-ev1TXBIm0XUHxy38,201
|
|
12
|
+
twist_innovation_api-0.0.3.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
13
|
+
twist_innovation_api-0.0.3.dist-info/METADATA,sha256=aquMRxNsC_ZBZJLCrulRkXUxDfTyG4mxub0WDOgmBXo,4335
|
|
14
|
+
twist_innovation_api-0.0.3.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
15
|
+
twist_innovation_api-0.0.3.dist-info/top_level.txt,sha256=mkoeBkRPFodjnd-3UDf9YndjTQ6Sriz-EKSI4nQ7brs,6
|
|
16
|
+
twist_innovation_api-0.0.3.dist-info/RECORD,,
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
twist/TwistAPI.py,sha256=pHUXI-ToF3zda9Q2gQzlHg73G0q7MWwgXGqz23PmRyY,3645
|
|
2
|
-
twist/TwistCbShutter.py,sha256=fOZ2ELxmsuH7seLmxvZmSpuSIzn6D6kKyIxtVYU0ZUI,1100
|
|
3
|
-
twist/TwistDevice.py,sha256=dByOL6v3k36Dz-QMsbgZQCr8JdxxxyOWhfn4G1Xzpaw,1409
|
|
4
|
-
twist/TwistLight.py,sha256=H1guYXWgZYCV5b-pNnQalAp5-_aI6N6db5aEoJhEoEs,3034
|
|
5
|
-
twist/TwistLouvre.py,sha256=ewYLO7nXFSEq7XtitXJW0VlLRv3_-iwsg8BhA5bOUMw,3128
|
|
6
|
-
twist/TwistModel.py,sha256=MFN3Bs0g3k_FKwTL40c3iHsy4mqXpUvLQpSfGybwaGY,2877
|
|
7
|
-
twist/TwistRgb.py,sha256=ui4DiKJ91TRTXQz3s6J_Wl-Ue7tKIGnY1FuMTZP_RFs,3340
|
|
8
|
-
twist/TwistSensor.py,sha256=AvuhlUejthAdQXsNA6MAS6NyKwpvLs4UiEa3P1mwqOE,1093
|
|
9
|
-
twist/TwistTypes.py,sha256=wsp-lnkaU70wNdoKWOWcnsq89vBheBYmM31ZXW3FMRw,2166
|
|
10
|
-
twist/Variants.py,sha256=SFAZ7bSmTRomPspOfU1qNWEW-htRuyhTKuyGKK9Qh54,1369
|
|
11
|
-
twist/__init__.py,sha256=gf8GHc9utiG0vD8GP3IfzLFHAG-ev1TXBIm0XUHxy38,201
|
|
12
|
-
twist_innovation_api-0.0.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
13
|
-
twist_innovation_api-0.0.1.dist-info/METADATA,sha256=2BIzSO_NZ2Dygs_hUdVg5PfeUcX3riO1tr4txoerIxY,3499
|
|
14
|
-
twist_innovation_api-0.0.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
15
|
-
twist_innovation_api-0.0.1.dist-info/top_level.txt,sha256=mkoeBkRPFodjnd-3UDf9YndjTQ6Sriz-EKSI4nQ7brs,6
|
|
16
|
-
twist_innovation_api-0.0.1.dist-info/RECORD,,
|
|
File without changes
|
{twist_innovation_api-0.0.1.dist-info → twist_innovation_api-0.0.3.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|