twist-innovation-api 0.0.1__tar.gz → 0.0.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.
Files changed (21) hide show
  1. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/PKG-INFO +54 -25
  2. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/README.md +53 -24
  3. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/setup.py +2 -3
  4. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/twist/TwistAPI.py +21 -20
  5. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/twist/TwistCbShutter.py +1 -1
  6. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/twist/TwistDevice.py +2 -2
  7. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/twist/TwistLight.py +12 -12
  8. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/twist/TwistLouvre.py +13 -13
  9. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/twist/TwistModel.py +8 -8
  10. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/twist/TwistRgb.py +12 -12
  11. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/twist/TwistSensor.py +1 -1
  12. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/twist_innovation_api.egg-info/PKG-INFO +54 -25
  13. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/LICENSE +0 -0
  14. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/pyproject.toml +0 -0
  15. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/setup.cfg +0 -0
  16. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/twist/TwistTypes.py +0 -0
  17. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/twist/Variants.py +0 -0
  18. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/twist/__init__.py +0 -0
  19. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/twist_innovation_api.egg-info/SOURCES.txt +0 -0
  20. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/twist_innovation_api.egg-info/dependency_links.txt +0 -0
  21. {twist_innovation_api-0.0.1 → twist_innovation_api-0.0.2}/twist_innovation_api.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: twist-innovation-api
3
- Version: 0.0.1
3
+ Version: 0.0.2
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,75 @@ 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
- from typing import Callable
83
+ import yaml
84
+ from typing import Callable, Awaitable
85
+ import aiomqtt
87
86
 
88
- # Initialize the mqtt broker here and define the functions needed
87
+ from twist import TwistAPI, TwistModel
89
88
 
90
- # Create a callback function
91
- callback_f: Callable[[str, str], None] | None = None
89
+ # Load configuration
90
+ with open("config.yaml", 'r') as file:
91
+ config = yaml.safe_load(file)
92
92
 
93
- # Callback function when a Mqtt message is received from the broker and forward it to the twist API
94
- def on_message(client, userdata, msg):
95
- if callback_f is not None:
96
- callback_f(msg.topic, msg.payload.decode())
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
- # Publish message to the broker
99
- def mqtt_publish(topic, payload):
98
+ async def _noop(topic: str, payload: str):
100
99
  pass
101
100
 
102
- # Subscribe to a topic from the broker
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
- # Callback function when a model received an update
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
- twist_api = TwistAPI(8, on_model_update)
113
- twist_api.add_mqtt(mqtt_publish, mqtt_subscribe)
114
- twist_model_list: list[TwistModel] = await twist_api.search_models()
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
- while True:
117
- await asyncio.sleep(1)
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, on_model_update)
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
+ print(f"{type(model)} has Model id: {model.model_id}, Device id: {model.parent_device.twist_id}")
143
+
144
+ while True:
145
+ await asyncio.sleep(1)
118
146
 
119
147
 
120
148
  if __name__ == "__main__":
121
149
  asyncio.run(main())
150
+
122
151
  ```
123
152
 
124
153
  ## License
@@ -56,46 +56,75 @@ twine upload dist/*
56
56
  Import the package in your Python scripts:
57
57
 
58
58
  ```python
59
- from twist import TwistAPI, TwistModel
60
-
61
- # Other includes for this example
62
59
  import asyncio
63
- from typing import Callable
60
+ import yaml
61
+ from typing import Callable, Awaitable
62
+ import aiomqtt
64
63
 
65
- # Initialize the mqtt broker here and define the functions needed
64
+ from twist import TwistAPI, TwistModel
66
65
 
67
- # Create a callback function
68
- callback_f: Callable[[str, str], None] | None = None
66
+ # Load configuration
67
+ with open("config.yaml", 'r') as file:
68
+ config = yaml.safe_load(file)
69
69
 
70
- # Callback function when a Mqtt message is received from the broker and forward it to the twist API
71
- def on_message(client, userdata, msg):
72
- if callback_f is not None:
73
- callback_f(msg.topic, msg.payload.decode())
70
+ mqtt_broker = config["mqtt_broker"]
71
+ mqtt_user = config["mqtt_user"]
72
+ mqtt_pass = config["mqtt_pass"]
73
+ mqtt_port = config["mqtt_port"]
74
74
 
75
- # Publish message to the broker
76
- def mqtt_publish(topic, payload):
75
+ async def _noop(topic: str, payload: str):
77
76
  pass
78
77
 
79
- # Subscribe to a topic from the broker
80
- def mqtt_subscribe(topic, callback):
81
- global callback_f
82
- callback_f = callback
78
+ callback_f :Callable[[str, str], Awaitable[None]] = _noop
83
79
 
84
- # Callback function when a model received an update
85
- def on_model_update(model: TwistModel):
80
+
81
+ async def on_model_update(model: TwistModel):
86
82
  model.print_context()
87
83
 
84
+
88
85
  async def main():
89
- twist_api = TwistAPI(8, on_model_update)
90
- twist_api.add_mqtt(mqtt_publish, mqtt_subscribe)
91
- twist_model_list: list[TwistModel] = await twist_api.search_models()
86
+ global callback_f
87
+
88
+ async with aiomqtt.Client(
89
+ hostname=mqtt_broker,
90
+ port=mqtt_port,
91
+ username=mqtt_user,
92
+ password=mqtt_pass
93
+ ) as mqtt_client:
92
94
 
93
- while True:
94
- await asyncio.sleep(1)
95
+ # Async publish method
96
+ async def mqtt_publish(topic: str, payload: str):
97
+ await mqtt_client.publish(topic, payload)
98
+
99
+ # Async subscribe method
100
+ async def mqtt_subscribe(topic: str, callback: Callable[[str, str], None]):
101
+ global callback_f
102
+ callback_f = callback
103
+
104
+ async def listen():
105
+ await mqtt_client.subscribe(topic)
106
+ async for message in mqtt_client.messages:
107
+ assert callback_f is not None
108
+ await callback_f(message.topic.value, message.payload.decode())
109
+
110
+ asyncio.create_task(listen())
111
+
112
+ # Initialize Twist API with async methods
113
+ twist_api = TwistAPI(8, on_model_update)
114
+ await twist_api.add_mqtt(mqtt_publish, mqtt_subscribe)
115
+
116
+ twist_model_list: list[TwistModel] = await twist_api.search_models()
117
+
118
+ for model in twist_model_list:
119
+ print(f"{type(model)} has Model id: {model.model_id}, Device id: {model.parent_device.twist_id}")
120
+
121
+ while True:
122
+ await asyncio.sleep(1)
95
123
 
96
124
 
97
125
  if __name__ == "__main__":
98
126
  asyncio.run(main())
127
+
99
128
  ```
100
129
 
101
130
  ## License
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="twist-innovation-api",
5
- version="0.0.1",
5
+ version="0.0.2",
6
6
  packages=find_packages(),
7
7
  install_requires=[
8
8
  # "requests" # For REST API
@@ -19,5 +19,4 @@ setup(
19
19
  "Operating System :: OS Independent",
20
20
  ],
21
21
  python_requires=">=3.7",
22
- )
23
-
22
+ )
@@ -13,18 +13,19 @@
13
13
 
14
14
 
15
15
  import asyncio
16
- from typing import Callable
17
- import random
18
16
  import json
17
+ import random
18
+ from typing import Callable, Awaitable
19
+
20
+ from .TwistDevice import TwistDevice, TwistModel
19
21
  from .TwistTypes import DeviceVariant
20
- from .TwistDevice import TwistModel, TwistDevice
21
22
 
22
23
 
23
24
  class TwistAPI:
24
- def __init__(self, installation_id: int, model_update: Callable[[TwistModel], None]):
25
+ def __init__(self, installation_id: int, model_update: Callable[[TwistModel], Awaitable[None]]):
25
26
  self._ext_publish = None
26
27
  self._subscribe = None
27
- self._callback = model_update
28
+ self._callback: Callable[[TwistModel], Awaitable[None]] = model_update
28
29
 
29
30
  self.function_map = {
30
31
  "context": self._context_msg,
@@ -36,14 +37,14 @@ class TwistAPI:
36
37
  # TODO: this should come from the API
37
38
  self.installation_id = installation_id
38
39
 
39
- def add_mqtt(self, publisher, subscriber):
40
+ async def add_mqtt(self, publisher, subscriber):
40
41
  self._ext_publish = publisher
41
42
  self._subscribe = subscriber
42
43
 
43
- self._subscribe(f"v2/{self.installation_id}/rx/#", self._on_message_received)
44
+ await self._subscribe(f"v2/{self.installation_id}/rx/#", self._on_message_received)
44
45
 
45
46
  async def search_models(self):
46
- self.getboard(0xffffffff)
47
+ await self.getboard(0xffffffff)
47
48
  await asyncio.sleep(3)
48
49
 
49
50
  model_list: list[TwistModel] = list()
@@ -53,7 +54,7 @@ class TwistAPI:
53
54
 
54
55
  return model_list
55
56
 
56
- def _publish(self, twist_id, opcode, payload: dict | None = None, model_id=None):
57
+ async def _publish(self, twist_id, opcode, payload: dict | None = None, model_id=None):
57
58
  if self._ext_publish is not None:
58
59
  topic = f"v2/{self.installation_id}/tx/{twist_id}/{opcode}"
59
60
  if payload is None:
@@ -62,13 +63,13 @@ class TwistAPI:
62
63
  if model_id is not None:
63
64
  topic = f"{topic}/{model_id}"
64
65
 
65
- self._ext_publish(topic, json.dumps(payload))
66
+ await self._ext_publish(topic, json.dumps(payload))
66
67
 
67
- def getboard(self, twist_id):
68
- self._publish(twist_id, "getboard")
68
+ async def getboard(self, twist_id):
69
+ await self._publish(twist_id, "getboard")
69
70
 
70
- def activate_event(self, model: TwistModel, data: json):
71
- self._publish(model.parent_device.twist_id, "activate_event", data, model.model_id)
71
+ async def activate_event(self, model: TwistModel, data: json):
72
+ await self._publish(model.parent_device.twist_id, "activate_event", data, model.model_id)
72
73
 
73
74
  def _parse_topic(self, topic):
74
75
  tpc_delim = topic.split('/')
@@ -83,19 +84,19 @@ class TwistAPI:
83
84
  "model_id": model_id
84
85
  }
85
86
 
86
- def _on_message_received(self, topic, payload, qos=None):
87
+ async def _on_message_received(self, topic, payload, qos=None):
87
88
  data = self._parse_topic(topic)
88
89
  if any(dev.twist_id == data["twist_id"] for dev in self.device_list) or data["opcode"] == "getboard":
89
90
  if data["opcode"] in self.function_map:
90
- self.function_map[data["opcode"]](data["twist_id"], payload, data["model_id"])
91
+ await self.function_map[data["opcode"]](data["twist_id"], payload, data["model_id"])
91
92
 
92
- def _context_msg(self, twist_id, payload, model_id):
93
+ async def _context_msg(self, twist_id, payload, model_id):
93
94
  device = next((d for d in self.device_list if d.twist_id == twist_id), None)
94
- model = device.context_msg(model_id, payload)
95
+ model = await device.context_msg(model_id, payload)
95
96
 
96
- self._callback(model)
97
+ await self._callback(model)
97
98
 
98
- def _get_board(self, twist_id, payload, model_id=None):
99
+ async def _get_board(self, twist_id, payload, model_id=None):
99
100
  data = json.loads(payload)
100
101
  if not any(dev.twist_id == twist_id for dev in self.device_list):
101
102
  self.device_list.append(TwistDevice(twist_id, data["h"], DeviceVariant(data["v"]), self))
@@ -23,7 +23,7 @@ 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
29
  def print_context(self):
@@ -37,6 +37,6 @@ 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)
40
+ async def context_msg(self, model_id: int, payload: str):
41
+ await self.model_list[model_id].context_msg(payload)
42
42
  return self.model_list[model_id]
@@ -34,22 +34,22 @@ 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
47
  if fading_time is None:
48
- self._activate_event(TwistLight.EventIndexes.VALUE, value)
48
+ await self._activate_event(TwistLight.EventIndexes.VALUE, value)
49
49
  else:
50
- self._activate_event(TwistLight.EventIndexes.VALUE_FADING, value, fading_time)
50
+ await self._activate_event(TwistLight.EventIndexes.VALUE_FADING, value, fading_time)
51
51
 
52
- def _activate_event(self, index: TwistLight.EventIndexes, value: int | None = None, fading_time: int | None = None):
52
+ async def _activate_event(self, index: TwistLight.EventIndexes, value: int | None = None, fading_time: int | None = None):
53
53
  data = {
54
54
  "i": index.value
55
55
  }
@@ -61,9 +61,9 @@ class TwistLight(TwistModel):
61
61
  else:
62
62
  data["vl"] = [value, fading_time]
63
63
 
64
- self.parent_device.api.activate_event(self, data)
64
+ await self.parent_device.api.activate_event(self, data)
65
65
 
66
- def context_msg(self, payload: str):
66
+ async def context_msg(self, payload: str):
67
67
  data = self.parse_general_context(payload)
68
68
 
69
69
  for ctx in data["cl"]:
@@ -34,7 +34,7 @@ class TwistLouvre(TwistModel):
34
34
  self.operating_time = 0
35
35
  self.current = 0
36
36
 
37
- def context_msg(self, payload: str):
37
+ async def context_msg(self, payload: str):
38
38
  data = self.parse_general_context(payload)
39
39
 
40
40
  for ctx in data["cl"]:
@@ -50,25 +50,25 @@ class TwistLouvre(TwistModel):
50
50
  elif index == 7:
51
51
  self.current = value[0]
52
52
 
53
- def open(self):
54
- self._activate_event(TwistLouvre.EventIndexes.OPEN)
53
+ async def open(self):
54
+ await self._activate_event(TwistLouvre.EventIndexes.OPEN)
55
55
 
56
- def stop(self):
57
- self._activate_event(TwistLouvre.EventIndexes.STOP)
56
+ async def stop(self):
57
+ await self._activate_event(TwistLouvre.EventIndexes.STOP)
58
58
 
59
- def close(self):
60
- self._activate_event(TwistLouvre.EventIndexes.CLOSE)
59
+ async def close(self):
60
+ await self._activate_event(TwistLouvre.EventIndexes.CLOSE)
61
61
 
62
- def toggle(self):
63
- self._activate_event(TwistLouvre.EventIndexes.TOGGLE)
62
+ async def toggle(self):
63
+ await self._activate_event(TwistLouvre.EventIndexes.TOGGLE)
64
64
 
65
- def set_value(self, value: int, fading_time: int | None = None):
65
+ async def set_value(self, value: int, fading_time: int | None = None):
66
66
  if fading_time is not None:
67
67
  raise NotImplementedError("Fading time can't be used in this model")
68
68
  else:
69
- self._activate_event(TwistLouvre.EventIndexes.VALUE, value)
69
+ await self._activate_event(TwistLouvre.EventIndexes.VALUE, value)
70
70
 
71
- def _activate_event(self, index: TwistLouvre.EventIndexes, value: int | None = None,
71
+ async def _activate_event(self, index: TwistLouvre.EventIndexes, value: int | None = None,
72
72
  fading_time: int | None = None):
73
73
  data = {
74
74
  "i": index.value
@@ -81,7 +81,7 @@ class TwistLouvre(TwistModel):
81
81
  else:
82
82
  data["vl"] = [value, fading_time]
83
83
 
84
- self.parent_device.api.activate_event(self, data)
84
+ await self.parent_device.api.activate_event(self, data)
85
85
 
86
86
  def print_context(self):
87
87
  print(
@@ -57,26 +57,26 @@ class TwistModel():
57
57
  def _get_value_from_context(self, ctx: dict):
58
58
  return ctx["i"], ctx["vl"]
59
59
 
60
- def context_msg(self, payload):
60
+ async def context_msg(self, payload):
61
61
  raise NotImplementedError("Function not supported for this model")
62
62
 
63
- def turn_on(self):
63
+ async def turn_on(self):
64
64
  raise NotImplementedError("Function not supported for this model")
65
65
 
66
- def turn_off(self):
66
+ async def turn_off(self):
67
67
  raise NotImplementedError("Function not supported for this model")
68
68
 
69
- def open(self):
69
+ async def open(self):
70
70
  raise NotImplementedError("Function not supported for this model")
71
71
 
72
- def stop(self):
72
+ async def stop(self):
73
73
  raise NotImplementedError("Function not supported for this model")
74
74
 
75
- def close(self):
75
+ async def close(self):
76
76
  raise NotImplementedError("Function not supported for this model")
77
77
 
78
- def toggle(self):
78
+ async def toggle(self):
79
79
  raise NotImplementedError("Function not supported for this model")
80
80
 
81
- def set_value(self, value: int | list[int, int] | list[int, int, int], fading_time: int | None = None):
81
+ async def set_value(self, value: int | list[int, int] | list[int, int, int], fading_time: int | None = None):
82
82
  raise NotImplementedError("Function not supported for this model")
@@ -42,22 +42,22 @@ class TwistRgb(TwistModel):
42
42
 
43
43
  self.operating_time = 0
44
44
 
45
- def turn_on(self):
46
- self._activate_event(TwistRgb.EventIndexes.SET)
45
+ async def turn_on(self):
46
+ await self._activate_event(TwistRgb.EventIndexes.SET)
47
47
 
48
- def turn_off(self):
49
- self._activate_event(TwistRgb.EventIndexes.CLEAR)
48
+ async def turn_off(self):
49
+ await self._activate_event(TwistRgb.EventIndexes.CLEAR)
50
50
 
51
- def toggle(self):
52
- self._activate_event(TwistRgb.EventIndexes.TOGGLE)
51
+ async def toggle(self):
52
+ await self._activate_event(TwistRgb.EventIndexes.TOGGLE)
53
53
 
54
- def set_value(self, value: list[int, int, int], fading_time: int | None = None):
54
+ async def set_value(self, value: list[int, int, int], fading_time: int | None = None):
55
55
  if fading_time is None:
56
- self._activate_event(TwistRgb.EventIndexes.VALUE, value)
56
+ await self._activate_event(TwistRgb.EventIndexes.VALUE, value)
57
57
  else:
58
- self._activate_event(TwistRgb.EventIndexes.VALUE_FADING, value, fading_time)
58
+ await self._activate_event(TwistRgb.EventIndexes.VALUE_FADING, value, fading_time)
59
59
 
60
- def _activate_event(self, index: TwistRgb.EventIndexes, value: list[int, int, int] | None = None,
60
+ async def _activate_event(self, index: TwistRgb.EventIndexes, value: list[int, int, int] | None = None,
61
61
  fading_time: int | None = None):
62
62
  data = {
63
63
  "i": index.value
@@ -71,9 +71,9 @@ class TwistRgb(TwistModel):
71
71
  value.append(fading_time)
72
72
  data["vl"] = value
73
73
 
74
- self.parent_device.api.activate_event(self, data)
74
+ await self.parent_device.api.activate_event(self, data)
75
75
 
76
- def context_msg(self, payload: str):
76
+ async def context_msg(self, payload: str):
77
77
  data = self.parse_general_context(payload)
78
78
 
79
79
  for ctx in data["cl"]:
@@ -24,7 +24,7 @@ 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
30
  def print_context(self):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: twist-innovation-api
3
- Version: 0.0.1
3
+ Version: 0.0.2
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,75 @@ 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
- from typing import Callable
83
+ import yaml
84
+ from typing import Callable, Awaitable
85
+ import aiomqtt
87
86
 
88
- # Initialize the mqtt broker here and define the functions needed
87
+ from twist import TwistAPI, TwistModel
89
88
 
90
- # Create a callback function
91
- callback_f: Callable[[str, str], None] | None = None
89
+ # Load configuration
90
+ with open("config.yaml", 'r') as file:
91
+ config = yaml.safe_load(file)
92
92
 
93
- # Callback function when a Mqtt message is received from the broker and forward it to the twist API
94
- def on_message(client, userdata, msg):
95
- if callback_f is not None:
96
- callback_f(msg.topic, msg.payload.decode())
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
- # Publish message to the broker
99
- def mqtt_publish(topic, payload):
98
+ async def _noop(topic: str, payload: str):
100
99
  pass
101
100
 
102
- # Subscribe to a topic from the broker
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
- # Callback function when a model received an update
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
- twist_api = TwistAPI(8, on_model_update)
113
- twist_api.add_mqtt(mqtt_publish, mqtt_subscribe)
114
- twist_model_list: list[TwistModel] = await twist_api.search_models()
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
- while True:
117
- await asyncio.sleep(1)
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, on_model_update)
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
+ print(f"{type(model)} has Model id: {model.model_id}, Device id: {model.parent_device.twist_id}")
143
+
144
+ while True:
145
+ await asyncio.sleep(1)
118
146
 
119
147
 
120
148
  if __name__ == "__main__":
121
149
  asyncio.run(main())
150
+
122
151
  ```
123
152
 
124
153
  ## License