twist-innovation-api 0.0.5__tar.gz → 0.0.7__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 (24) hide show
  1. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/PKG-INFO +11 -2
  2. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/setup.py +1 -1
  3. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist/TwistAPI.py +11 -13
  4. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist/TwistModel.py +5 -14
  5. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist/TwistRgb.py +32 -12
  6. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist_innovation_api.egg-info/PKG-INFO +12 -3
  7. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/LICENSE +0 -0
  8. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/README.md +0 -0
  9. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/pyproject.toml +0 -0
  10. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/setup.cfg +0 -0
  11. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist/BackendAdapter.py +0 -0
  12. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist/TwistBinarySensor.py +0 -0
  13. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist/TwistButton.py +0 -0
  14. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist/TwistDevice.py +0 -0
  15. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist/TwistGarage.py +0 -0
  16. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist/TwistLight.py +0 -0
  17. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist/TwistRelay.py +0 -0
  18. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist/TwistSensor.py +0 -0
  19. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist/TwistShutter.py +0 -0
  20. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist/TwistTypes.py +0 -0
  21. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist/__init__.py +0 -0
  22. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist_innovation_api.egg-info/SOURCES.txt +0 -0
  23. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist_innovation_api.egg-info/dependency_links.txt +0 -0
  24. {twist_innovation_api-0.0.5 → twist_innovation_api-0.0.7}/twist_innovation_api.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: twist_innovation_api
3
- Version: 0.0.5
3
+ Version: 0.0.7
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
@@ -11,6 +11,15 @@ Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.7
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
+ Dynamic: author
15
+ Dynamic: author-email
16
+ Dynamic: classifier
17
+ Dynamic: description
18
+ Dynamic: description-content-type
19
+ Dynamic: home-page
20
+ Dynamic: license-file
21
+ Dynamic: requires-python
22
+ Dynamic: summary
14
23
 
15
24
  # Twist Innovation API
16
25
 
@@ -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.5",
5
+ version="0.0.7",
6
6
  packages=find_packages(),
7
7
  install_requires=[
8
8
  # "requests" # For REST API
@@ -43,7 +43,8 @@ class TwistAPI:
43
43
  self.installation_id: Optional[int] = None
44
44
 
45
45
  # Backend adapter for fetching device info
46
- self._backend_adapter = BackendAdapter(backend_url, api_key, installation_uuid)
46
+ self._backend_adapter = BackendAdapter(
47
+ backend_url, api_key, installation_uuid)
47
48
 
48
49
  async def add_mqtt(self, publisher, subscriber):
49
50
  self._ext_publish = publisher
@@ -55,15 +56,6 @@ class TwistAPI:
55
56
  """
56
57
  Get models from backend
57
58
 
58
- Returns:
59
- List of TwistModel objects
60
- """
61
- return await self._get_models_from_backend()
62
-
63
- async def _get_models_from_backend(self):
64
- """
65
- Fetch devices from backend and create model list
66
-
67
59
  Returns:
68
60
  List of TwistModel objects
69
61
  """
@@ -90,7 +82,8 @@ class TwistAPI:
90
82
  infos.sort(key=lambda x: x.model_id)
91
83
 
92
84
  # Create device if it doesn't exist
93
- device = next((d for d in self.device_list if d.twist_id == device_id), None)
85
+ device = next(
86
+ (d for d in self.device_list if d.twist_id == device_id), None)
94
87
  if device is None:
95
88
  device = TwistDevice(device_id, 0, self)
96
89
  self.device_list.append(device)
@@ -102,7 +95,8 @@ class TwistAPI:
102
95
  # Get product info if available using deviceAllocationModelKey
103
96
  product_info = None
104
97
  if info.device_allocation_model_key:
105
- product_info = products.get(info.device_allocation_model_key)
98
+ product_info = products.get(
99
+ info.device_allocation_model_key)
106
100
 
107
101
  if product_info:
108
102
  model.product_name = product_info.product_name
@@ -137,6 +131,9 @@ class TwistAPI:
137
131
  async def activate_event(self, model: TwistModel, data: json):
138
132
  await self._publish(model.parent_device.twist_id, "activate_event", data, model.model_id)
139
133
 
134
+ async def request_context(self, model: TwistModel):
135
+ await self._publish(model.parent_device.twist_id, "context", model_id=model.model_id)
136
+
140
137
  def _parse_topic(self, topic):
141
138
  tpc_delim = topic.split('/')
142
139
 
@@ -157,7 +154,8 @@ class TwistAPI:
157
154
  await self.function_map[data["opcode"]](data["twist_id"], payload, data["model_id"])
158
155
 
159
156
  async def _context_msg(self, twist_id, payload, model_id):
160
- device = next((d for d in self.device_list if d.twist_id == twist_id), None)
157
+ device = next(
158
+ (d for d in self.device_list if d.twist_id == twist_id), None)
161
159
  await device.context_msg(model_id, payload)
162
160
 
163
161
  async def _get_board(self, twist_id, payload, model_id=None):
@@ -37,7 +37,8 @@ class TwistModel():
37
37
  "prio": None
38
38
  }
39
39
 
40
- self._update_callback: Callable[[TwistModel], Awaitable[None]] | None = None
40
+ self._update_callback: Callable[[
41
+ TwistModel], Awaitable[None]] | None = None
41
42
 
42
43
  @property
43
44
  def friendly_name(self) -> str:
@@ -54,11 +55,9 @@ class TwistModel():
54
55
  # Replace underscores with spaces and capitalize first letter
55
56
  return self.name.replace('_', ' ').capitalize()
56
57
 
57
-
58
58
  def print_context(self):
59
59
  print("function is not supported")
60
60
 
61
-
62
61
  def parse_general_context(self, payload: str):
63
62
  data = json.loads(payload)
64
63
 
@@ -76,42 +75,34 @@ class TwistModel():
76
75
  self.errors["prio"] = value
77
76
  return data
78
77
 
79
-
80
78
  def _get_value_from_context(self, ctx: dict):
81
79
  return ctx["i"], ctx["vl"]
82
80
 
83
-
84
81
  async def context_msg(self, payload):
85
82
  raise NotImplementedError("Function not supported for this model")
86
83
 
87
-
88
84
  async def turn_on(self):
89
85
  raise NotImplementedError("Function not supported for this model")
90
86
 
91
-
92
87
  async def turn_off(self):
93
88
  raise NotImplementedError("Function not supported for this model")
94
89
 
95
-
96
90
  async def open(self):
97
91
  raise NotImplementedError("Function not supported for this model")
98
92
 
99
-
100
93
  async def stop(self):
101
94
  raise NotImplementedError("Function not supported for this model")
102
95
 
103
-
104
96
  async def close(self):
105
97
  raise NotImplementedError("Function not supported for this model")
106
98
 
107
-
108
99
  async def toggle(self):
109
100
  raise NotImplementedError("Function not supported for this model")
110
101
 
111
-
112
102
  async def set_value(self, value: int | list[int, int] | list[int, int, int], fading_time: int | None = None):
113
103
  raise NotImplementedError("Function not supported for this model")
114
104
 
115
-
116
- def register_update_cb(self, cb: Callable[[TwistModel], Awaitable[None]]):
105
+ async def register_update_cb(self, cb: Callable[[TwistModel], Awaitable[None]]):
117
106
  self._update_callback = cb
107
+
108
+ await self.parent_device.api.request_context(self)
@@ -53,10 +53,25 @@ class TwistRgb(TwistModel):
53
53
  await self._activate_event(TwistRgb.EventIndexes.TOGGLE)
54
54
 
55
55
  async def set_value(self, value: list[int, int, int], fading_time: int | None = None):
56
+ """Set HSV values.
57
+
58
+ Args:
59
+ value: [H, S, V] where H is 0-360 degrees, S is 0-100%, V is 0-100%
60
+ fading_time: Optional transition time in milliseconds
61
+ """
62
+ # Convert from standard ranges to device range (0-65535)
63
+ # H: 0-360 -> 0-65535 (multiply by 182.04)
64
+ # S: 0-100 -> 0-65535 (multiply by 655.35)
65
+ # V: 0-100 -> 0-65535 (multiply by 655.35)
66
+ h_value = round(value[0] * 182.04)
67
+ s_value = round(value[1] * 655.35)
68
+ v_value = round(value[2] * 655.35)
69
+ converted_value = [h_value, s_value, v_value]
70
+
56
71
  if fading_time is None:
57
- await self._activate_event(TwistRgb.EventIndexes.VALUE, value)
72
+ await self._activate_event(TwistRgb.EventIndexes.VALUE, converted_value)
58
73
  else:
59
- await self._activate_event(TwistRgb.EventIndexes.VALUE_FADING, value, fading_time)
74
+ await self._activate_event(TwistRgb.EventIndexes.VALUE_FADING, converted_value, fading_time)
60
75
 
61
76
  async def _activate_event(self, index: TwistRgb.EventIndexes, value: tuple[int, int, int] | None = None,
62
77
  fading_time: int | None = None):
@@ -67,28 +82,33 @@ class TwistRgb(TwistModel):
67
82
  if value is None:
68
83
  data["vl"] = []
69
84
  elif fading_time is None:
70
- data["vl"] = [int(v / 655.35) for v in value]
85
+ data["vl"] = list(value)
71
86
  else:
72
- value = list(value)
73
- value.append(fading_time)
74
- data["vl"] = value
87
+ data["vl"] = list(value) + [fading_time]
75
88
 
76
89
  await self.parent_device.api.activate_event(self, data)
77
90
 
78
91
  async def context_msg(self, payload: str):
92
+ """Parse context message from device.
93
+
94
+ Device sends HSV in 0-65535 range, convert to standard ranges:
95
+ H: 0-65535 -> 0-360 degrees (divide by 182.04)
96
+ S: 0-65535 -> 0-100% (divide by 655.35)
97
+ V: 0-65535 -> 0-100% (divide by 655.35)
98
+ """
79
99
  data = self.parse_general_context(payload)
80
100
 
81
101
  for ctx in data["cl"]:
82
102
  index, value = self._get_value_from_context(ctx)
83
103
  if index < ContextErrors.MAX.value:
84
104
  if ContextErrors(index) == ContextErrors.ACTUAL:
85
- self.actual_h = round(value[0] / 655.35, 0)
86
- self.actual_s = round(value[1] / 655.35, 0)
87
- self.actual_v = round(value[2] / 655.35, 0)
105
+ self.actual_h = round(value[0] / 182.04, 1)
106
+ self.actual_s = round(value[1] / 655.35, 1)
107
+ self.actual_v = round(value[2] / 655.35, 1)
88
108
  elif ContextErrors(index) == ContextErrors.REQUESTED:
89
- self.requested_h = round(value[0] / 655.35, 0)
90
- self.requested_s = round(value[0] / 655.35, 0)
91
- self.requested_v = round(value[0] / 655.35, 0)
109
+ self.requested_h = round(value[0] / 182.04, 1)
110
+ self.requested_s = round(value[1] / 655.35, 1)
111
+ self.requested_v = round(value[2] / 655.35, 1)
92
112
  else:
93
113
  if index == 6:
94
114
  self.operating_time = value[0]
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
2
- Name: twist-innovation-api
3
- Version: 0.0.5
1
+ Metadata-Version: 2.4
2
+ Name: twist_innovation_api
3
+ Version: 0.0.7
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
@@ -11,6 +11,15 @@ Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.7
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
+ Dynamic: author
15
+ Dynamic: author-email
16
+ Dynamic: classifier
17
+ Dynamic: description
18
+ Dynamic: description-content-type
19
+ Dynamic: home-page
20
+ Dynamic: license-file
21
+ Dynamic: requires-python
22
+ Dynamic: summary
14
23
 
15
24
  # Twist Innovation API
16
25