tinkerforge-async 1.5.0__py3-none-any.whl → 1.5.2__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.
- tinkerforge_async/_version.py +1 -1
- tinkerforge_async/bricklet_ptc.py +11 -3
- tinkerforge_async/bricklet_ptc_v2.py +10 -2
- tinkerforge_async/bricklet_thermocouple_v2.py +419 -0
- tinkerforge_async/device_factory.py +2 -0
- tinkerforge_async/devices.py +1 -0
- {tinkerforge_async-1.5.0.dist-info → tinkerforge_async-1.5.2.dist-info}/METADATA +9 -1
- {tinkerforge_async-1.5.0.dist-info → tinkerforge_async-1.5.2.dist-info}/RECORD +11 -10
- {tinkerforge_async-1.5.0.dist-info → tinkerforge_async-1.5.2.dist-info}/WHEEL +1 -1
- {tinkerforge_async-1.5.0.dist-info → tinkerforge_async-1.5.2.dist-info}/LICENSE +0 -0
- {tinkerforge_async-1.5.0.dist-info → tinkerforge_async-1.5.2.dist-info}/top_level.txt +0 -0
tinkerforge_async/_version.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
# pylint: disable=missing-module-docstring
|
2
|
-
__version__ = "1.5.
|
2
|
+
__version__ = "1.5.2"
|
@@ -33,6 +33,8 @@ class CallbackID(Enum):
|
|
33
33
|
SENSOR_CONNECTED = 24
|
34
34
|
|
35
35
|
|
36
|
+
# We need the alias for MyPy type hinting
|
37
|
+
# See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
|
36
38
|
_CallbackID = CallbackID
|
37
39
|
|
38
40
|
|
@@ -73,13 +75,15 @@ class LineFilter(Enum):
|
|
73
75
|
FREQUENCY_60HZ = 1
|
74
76
|
|
75
77
|
|
76
|
-
|
78
|
+
# We need the alias for MyPy type hinting
|
79
|
+
# See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
|
80
|
+
_LineFilter = LineFilter
|
77
81
|
|
78
82
|
|
79
83
|
@unique
|
80
84
|
class WireMode(Enum):
|
81
85
|
"""
|
82
|
-
Select the measurement setup. Use 3 or wires to eliminate most/all
|
86
|
+
Select the measurement setup. Use 3 or 4 wires to eliminate most/all the
|
83
87
|
resistance of the wire. Use 3 or 4 wire setups when using PT100 and long
|
84
88
|
cables.
|
85
89
|
"""
|
@@ -89,7 +93,9 @@ class WireMode(Enum):
|
|
89
93
|
WIRE_4 = 4
|
90
94
|
|
91
95
|
|
92
|
-
|
96
|
+
# We need the alias for MyPy type hinting
|
97
|
+
# See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
|
98
|
+
_WireMode = WireMode
|
93
99
|
|
94
100
|
|
95
101
|
@unique
|
@@ -102,6 +108,8 @@ class SensorType(Enum):
|
|
102
108
|
PT_1000 = 1
|
103
109
|
|
104
110
|
|
111
|
+
# We need the alias for MyPy type hinting
|
112
|
+
# See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
|
105
113
|
_SensorType = SensorType
|
106
114
|
|
107
115
|
|
@@ -30,6 +30,8 @@ class CallbackID(Enum):
|
|
30
30
|
SENSOR_CONNECTED = 18
|
31
31
|
|
32
32
|
|
33
|
+
# We need the alias for MyPy type hinting
|
34
|
+
# See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
|
33
35
|
_CallbackID = CallbackID
|
34
36
|
|
35
37
|
|
@@ -67,7 +69,9 @@ class LineFilter(Enum):
|
|
67
69
|
FREQUENCY_60HZ = 1
|
68
70
|
|
69
71
|
|
70
|
-
|
72
|
+
# We need the alias for MyPy type hinting
|
73
|
+
# See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
|
74
|
+
_LineFilter = LineFilter
|
71
75
|
|
72
76
|
|
73
77
|
@unique
|
@@ -82,6 +86,8 @@ class WireMode(Enum):
|
|
82
86
|
WIRE_4 = 4
|
83
87
|
|
84
88
|
|
89
|
+
# We need the alias for MyPy type hinting
|
90
|
+
# See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
|
85
91
|
_WireMode = WireMode # We need the alias for MyPy type hinting
|
86
92
|
|
87
93
|
|
@@ -95,7 +101,9 @@ class SensorType(Enum):
|
|
95
101
|
PT_1000 = 1
|
96
102
|
|
97
103
|
|
98
|
-
|
104
|
+
# We need the alias for MyPy type hinting
|
105
|
+
# See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
|
106
|
+
_SensorType = SensorType
|
99
107
|
|
100
108
|
|
101
109
|
class GetMovingAverageConfiguration(NamedTuple):
|
@@ -0,0 +1,419 @@
|
|
1
|
+
"""
|
2
|
+
Module for the Tinkerforge Thermocouple Bricklet 2.0
|
3
|
+
(https://www.tinkerforge.com/en/doc/Hardware/Bricklets/Thermocouple_V2.html) implemented using Python asyncio. It does
|
4
|
+
the low-level communication with the Tinkerforge ip connection and also handles conversion of raw units to SI units.
|
5
|
+
"""
|
6
|
+
# pylint: disable=duplicate-code # Many sensors of different generations have a similar API
|
7
|
+
from __future__ import annotations
|
8
|
+
|
9
|
+
from decimal import Decimal
|
10
|
+
from enum import Enum, unique
|
11
|
+
from typing import TYPE_CHECKING, AsyncGenerator, NamedTuple
|
12
|
+
|
13
|
+
from .devices import AdvancedCallbackConfiguration, BrickletWithMCU, DeviceIdentifier, Event
|
14
|
+
from .devices import ThresholdOption as Threshold
|
15
|
+
from .devices import _FunctionID
|
16
|
+
from .ip_connection_helper import pack_payload, unpack_payload
|
17
|
+
|
18
|
+
if TYPE_CHECKING:
|
19
|
+
from .ip_connection import IPConnectionAsync
|
20
|
+
|
21
|
+
|
22
|
+
@unique
|
23
|
+
class CallbackID(Enum):
|
24
|
+
"""
|
25
|
+
The callbacks available to this bricklet
|
26
|
+
"""
|
27
|
+
|
28
|
+
TEMPERATURE = 4
|
29
|
+
ERROR_STATE = 8
|
30
|
+
|
31
|
+
|
32
|
+
# We need the alias for MyPy type hinting
|
33
|
+
# See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
|
34
|
+
_CallbackID = CallbackID
|
35
|
+
|
36
|
+
|
37
|
+
@unique
|
38
|
+
class FunctionID(_FunctionID):
|
39
|
+
"""
|
40
|
+
The function calls available to this bricklet
|
41
|
+
"""
|
42
|
+
|
43
|
+
GET_TEMPERATURE = 1
|
44
|
+
SET_TEMPERATURE_CALLBACK_CONFIGURATION = 2
|
45
|
+
GET_TEMPERATURE_CALLBACK_CONFIGURATION = 3
|
46
|
+
SET_CONFIGURATION = 5
|
47
|
+
GET_CONFIGURATION = 6
|
48
|
+
GET_ERROR_STATE = 7
|
49
|
+
|
50
|
+
|
51
|
+
@unique
|
52
|
+
class Averaging(Enum):
|
53
|
+
"""
|
54
|
+
The number of values averaged before returning the measurement result.
|
55
|
+
"""
|
56
|
+
|
57
|
+
AVERAGING_1 = 1
|
58
|
+
AVERAGING_2 = 2
|
59
|
+
AVERAGING_4 = 4
|
60
|
+
AVERAGING_8 = 8
|
61
|
+
AVERAGING_16 = 16
|
62
|
+
|
63
|
+
|
64
|
+
# We need the alias for MyPy type hinting
|
65
|
+
# See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
|
66
|
+
_Averaging = Averaging
|
67
|
+
|
68
|
+
|
69
|
+
@unique
|
70
|
+
class SensorType(Enum):
|
71
|
+
"""
|
72
|
+
The type of thermocouple connected to the bricklet.
|
73
|
+
"""
|
74
|
+
|
75
|
+
TYPE_B = 0
|
76
|
+
TYPE_E = 1
|
77
|
+
TYPE_J = 2
|
78
|
+
TYPE_K = 3
|
79
|
+
TYPE_N = 4
|
80
|
+
TYPE_R = 5
|
81
|
+
TYPE_S = 6
|
82
|
+
TYPE_T = 7
|
83
|
+
TYPE_G8 = 8
|
84
|
+
TYPE_G32 = 9
|
85
|
+
|
86
|
+
|
87
|
+
# We need the alias for MyPy type hinting
|
88
|
+
# See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
|
89
|
+
_SensorType = SensorType
|
90
|
+
|
91
|
+
|
92
|
+
class LineFilter(Enum):
|
93
|
+
"""
|
94
|
+
Selects the notch filter to filter out the mains frequency hum
|
95
|
+
"""
|
96
|
+
|
97
|
+
FREQUENCY_50HZ = 0
|
98
|
+
FREQUENCY_60HZ = 1
|
99
|
+
|
100
|
+
|
101
|
+
# We need the alias for MyPy type hinting
|
102
|
+
# See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
|
103
|
+
_LineFilter = LineFilter
|
104
|
+
|
105
|
+
|
106
|
+
class GetConfiguration(NamedTuple):
|
107
|
+
averaging: Averaging
|
108
|
+
sensor_type: SensorType
|
109
|
+
filter: LineFilter
|
110
|
+
|
111
|
+
|
112
|
+
class GetErrorState(NamedTuple):
|
113
|
+
"""
|
114
|
+
Tuple that contains the error states of the system. Over-/undervoltage indicates either a voltage above 3.3 V or
|
115
|
+
below 0 V and likely a defective thermocouple. An open circuit indicates a missing or defective thermocouple.
|
116
|
+
"""
|
117
|
+
|
118
|
+
over_under: bool
|
119
|
+
open_circuit: bool
|
120
|
+
|
121
|
+
|
122
|
+
class BrickletThermocoupleV2(BrickletWithMCU):
|
123
|
+
"""
|
124
|
+
Measures temperature with a thermocouple.
|
125
|
+
"""
|
126
|
+
|
127
|
+
DEVICE_IDENTIFIER = DeviceIdentifier.BRICKLET_THERMOCOUPLE_V2
|
128
|
+
DEVICE_DISPLAY_NAME = "Thermocouple Bricklet 2.0"
|
129
|
+
|
130
|
+
# Convenience imports, so that the user does not need to additionally import them
|
131
|
+
CallbackID = CallbackID
|
132
|
+
FunctionID = FunctionID
|
133
|
+
ThresholdOption = Threshold
|
134
|
+
Averaging = Averaging
|
135
|
+
SensorType = SensorType
|
136
|
+
LineFilter = LineFilter
|
137
|
+
|
138
|
+
CALLBACK_FORMATS = {CallbackID.TEMPERATURE: "i", CallbackID.ERROR_STATE: "! !"}
|
139
|
+
|
140
|
+
SID_TO_CALLBACK = {
|
141
|
+
0: (CallbackID.TEMPERATURE,),
|
142
|
+
1: (CallbackID.ERROR_STATE,),
|
143
|
+
}
|
144
|
+
|
145
|
+
@property
|
146
|
+
def sensor_type(self) -> _SensorType:
|
147
|
+
"""
|
148
|
+
Return the type of sensor. Either PT100 oder PT1000 as a SensorType enum.
|
149
|
+
"""
|
150
|
+
return self.__sensor_type
|
151
|
+
|
152
|
+
@sensor_type.setter
|
153
|
+
def sensor_type(self, value: _SensorType):
|
154
|
+
if not isinstance(value, SensorType):
|
155
|
+
self.__sensor_type: SensorType = SensorType(value)
|
156
|
+
else:
|
157
|
+
self.__sensor_type = value
|
158
|
+
|
159
|
+
def __init__(self, uid, ipcon: IPConnectionAsync, sensor_type: _SensorType = SensorType.TYPE_K) -> None:
|
160
|
+
"""
|
161
|
+
Creates an object with the unique device ID *uid* and adds it to
|
162
|
+
the IP Connection *ipcon*.
|
163
|
+
"""
|
164
|
+
super().__init__(self.DEVICE_DISPLAY_NAME, uid, ipcon)
|
165
|
+
|
166
|
+
self.api_version = (2, 0, 0)
|
167
|
+
self.sensor_type = sensor_type # Use the setter to automatically convert to enum
|
168
|
+
|
169
|
+
def __repr__(self) -> str:
|
170
|
+
return (
|
171
|
+
f"{self.__class__.__module__}.{self.__class__.__qualname__}"
|
172
|
+
f"(uid={self.uid}, ipcon={self.ipcon!r}, sensor_type={self.sensor_type})"
|
173
|
+
)
|
174
|
+
|
175
|
+
async def get_value(self, sid: int) -> Decimal | GetErrorState:
|
176
|
+
assert sid in (0, 1)
|
177
|
+
|
178
|
+
if sid == 0:
|
179
|
+
return await self.get_temperature()
|
180
|
+
if sid == 1:
|
181
|
+
return await self.get_error_state()
|
182
|
+
raise ValueError(f"Invalid sid: {sid}. sid must be in (0, 1).")
|
183
|
+
|
184
|
+
async def set_callback_configuration( # pylint: disable=too-many-arguments
|
185
|
+
self,
|
186
|
+
sid: int,
|
187
|
+
period: int = 0,
|
188
|
+
value_has_to_change: bool = False,
|
189
|
+
option: Threshold | int = Threshold.OFF,
|
190
|
+
minimum: float | Decimal | None = None,
|
191
|
+
maximum: float | Decimal | None = None,
|
192
|
+
response_expected: bool = True,
|
193
|
+
) -> None:
|
194
|
+
minimum = Decimal("273.15") if minimum is None else minimum
|
195
|
+
maximum = Decimal("273.15") if maximum is None else maximum
|
196
|
+
|
197
|
+
if sid == 0:
|
198
|
+
await self.set_temperature_callback_configuration(
|
199
|
+
period, value_has_to_change, option, minimum, maximum, response_expected
|
200
|
+
)
|
201
|
+
else:
|
202
|
+
raise ValueError(f"Invalid sid: {sid}. sid must be in (0, ).")
|
203
|
+
|
204
|
+
async def get_callback_configuration(self, sid: int) -> AdvancedCallbackConfiguration:
|
205
|
+
if sid == 0:
|
206
|
+
return await self.get_temperature_callback_configuration()
|
207
|
+
|
208
|
+
raise ValueError(f"Invalid sid: {sid}. sid must be in (0, ).")
|
209
|
+
|
210
|
+
async def get_temperature(self) -> Decimal:
|
211
|
+
"""
|
212
|
+
Returns the temperature of the thermocouple. The value is given in °C/100,
|
213
|
+
e.g. a value of 4223 means that a temperature of 42.23 °C is measured.
|
214
|
+
|
215
|
+
If you want to get the temperature periodically, it is recommended
|
216
|
+
to use the :cb:`Temperature` callback and set the period with
|
217
|
+
:func:`Set Temperature Callback Configuration`.
|
218
|
+
|
219
|
+
If you want to get the value periodically, it is recommended to use the
|
220
|
+
:cb:`Temperature` callback. You can set the callback configuration
|
221
|
+
with :func:`Set Temperature Callback Configuration`.
|
222
|
+
"""
|
223
|
+
_, payload = await self.ipcon.send_request(
|
224
|
+
device=self, function_id=FunctionID.GET_TEMPERATURE, response_expected=True
|
225
|
+
)
|
226
|
+
return self.__value_to_si(unpack_payload(payload, "i"))
|
227
|
+
|
228
|
+
async def set_temperature_callback_configuration( # pylint: disable=too-many-arguments
|
229
|
+
self,
|
230
|
+
period: int = 0,
|
231
|
+
value_has_to_change: bool = False,
|
232
|
+
option: Threshold | int = Threshold.OFF,
|
233
|
+
minimum: Decimal | int | float = Decimal("273.15"),
|
234
|
+
maximum: Decimal | int | float = Decimal("273.15"),
|
235
|
+
response_expected=True,
|
236
|
+
) -> None:
|
237
|
+
"""
|
238
|
+
The period is the period with which the :cb:`Temperature` callback is triggered
|
239
|
+
periodically. A value of 0 turns the callback off.
|
240
|
+
|
241
|
+
If the `value has to change`-parameter is set to true, the callback is only
|
242
|
+
triggered after the value has changed. If the value didn't change
|
243
|
+
within the period, the callback is triggered immediately on change.
|
244
|
+
|
245
|
+
If it is set to false, the callback is continuously triggered with the period,
|
246
|
+
independent of the value.
|
247
|
+
|
248
|
+
It is furthermore possible to constrain the callback with thresholds.
|
249
|
+
|
250
|
+
The `option`-parameter together with min/max sets a threshold for the :cb:`Temperature` callback.
|
251
|
+
|
252
|
+
The following options are possible:
|
253
|
+
|
254
|
+
.. csv-table::
|
255
|
+
:header: "Option", "Description"
|
256
|
+
:widths: 10, 100
|
257
|
+
|
258
|
+
"'x'", "Threshold is turned off"
|
259
|
+
"'o'", "Threshold is triggered when the value is *outside* the min and max values"
|
260
|
+
"'i'", "Threshold is triggered when the value is *inside* or equal to the min and max values"
|
261
|
+
"'<'", "Threshold is triggered when the value is smaller than the min value (max is ignored)"
|
262
|
+
"'>'", "Threshold is triggered when the value is greater than the min value (max is ignored)"
|
263
|
+
|
264
|
+
If the option is set to 'x' (threshold turned off) the callback is triggered with the fixed period.
|
265
|
+
"""
|
266
|
+
if not isinstance(option, Threshold):
|
267
|
+
option = Threshold(option)
|
268
|
+
assert period >= 0
|
269
|
+
|
270
|
+
await self.ipcon.send_request(
|
271
|
+
device=self,
|
272
|
+
function_id=FunctionID.SET_TEMPERATURE_CALLBACK_CONFIGURATION,
|
273
|
+
data=pack_payload(
|
274
|
+
(
|
275
|
+
int(period),
|
276
|
+
bool(value_has_to_change),
|
277
|
+
option.value.encode("ascii"),
|
278
|
+
self.__si_to_value(minimum),
|
279
|
+
self.__si_to_value(maximum),
|
280
|
+
),
|
281
|
+
"I ! c i i",
|
282
|
+
),
|
283
|
+
response_expected=response_expected,
|
284
|
+
)
|
285
|
+
|
286
|
+
async def get_temperature_callback_configuration(self) -> AdvancedCallbackConfiguration:
|
287
|
+
"""
|
288
|
+
Returns the callback configuration as set by :func:`Set Temperature Callback Configuration`.
|
289
|
+
"""
|
290
|
+
_, payload = await self.ipcon.send_request(
|
291
|
+
device=self, function_id=FunctionID.GET_TEMPERATURE_CALLBACK_CONFIGURATION, response_expected=True
|
292
|
+
)
|
293
|
+
period, value_has_to_change, option, minimum, maximum = unpack_payload(payload, "I ! c i i")
|
294
|
+
option = Threshold(option)
|
295
|
+
minimum, maximum = self.__value_to_si(minimum), self.__value_to_si(maximum)
|
296
|
+
return AdvancedCallbackConfiguration(period, value_has_to_change, option, minimum, maximum)
|
297
|
+
|
298
|
+
async def set_configuration(
|
299
|
+
self,
|
300
|
+
averaging: _Averaging = Averaging.AVERAGING_16,
|
301
|
+
sensor_type: _SensorType = SensorType.TYPE_K,
|
302
|
+
line_filter: _LineFilter = LineFilter.FREQUENCY_50HZ,
|
303
|
+
response_expected: bool = True,
|
304
|
+
) -> None:
|
305
|
+
"""
|
306
|
+
You can configure averaging size, thermocouple type and frequency
|
307
|
+
filtering.
|
308
|
+
|
309
|
+
Available averaging sizes are 1, 2, 4, 8 and 16 samples.
|
310
|
+
|
311
|
+
As thermocouple type you can use B, E, J, K, N, R, S and T. If you have a
|
312
|
+
different thermocouple or a custom thermocouple you can also use
|
313
|
+
G8 and G32. With these types the returned value will not be in °C/100,
|
314
|
+
it will be calculated by the following formulas:
|
315
|
+
|
316
|
+
* G8: ``value = 8 * 1.6 * 2^17 * Vin``
|
317
|
+
* G32: ``value = 32 * 1.6 * 2^17 * Vin``
|
318
|
+
|
319
|
+
where Vin is the thermocouple input voltage.
|
320
|
+
|
321
|
+
The frequency filter can be either configured to 50Hz or to 60Hz. You should
|
322
|
+
configure it according to your utility frequency.
|
323
|
+
|
324
|
+
The conversion time depends on the averaging and filter configuration, it can
|
325
|
+
be calculated as follows:
|
326
|
+
|
327
|
+
* 60Hz: ``time = 82 + (samples - 1) * 16.67``
|
328
|
+
* 50Hz: ``time = 98 + (samples - 1) * 20``
|
329
|
+
"""
|
330
|
+
if not isinstance(averaging, Averaging):
|
331
|
+
averaging = Averaging(averaging)
|
332
|
+
if not isinstance(sensor_type, SensorType):
|
333
|
+
sensor_type = SensorType(sensor_type)
|
334
|
+
if not isinstance(line_filter, LineFilter):
|
335
|
+
line_filter = LineFilter(line_filter)
|
336
|
+
|
337
|
+
self.__sensor_type = sensor_type
|
338
|
+
await self.ipcon.send_request(
|
339
|
+
device=self,
|
340
|
+
function_id=FunctionID.SET_CONFIGURATION,
|
341
|
+
data=pack_payload((averaging.value, sensor_type.value, line_filter.value), "B B B"),
|
342
|
+
response_expected=response_expected,
|
343
|
+
)
|
344
|
+
|
345
|
+
async def get_configuration(self) -> GetConfiguration:
|
346
|
+
_, payload = await self.ipcon.send_request(
|
347
|
+
device=self, function_id=FunctionID.GET_CONFIGURATION, response_expected=True
|
348
|
+
)
|
349
|
+
|
350
|
+
averaging, sensor_type, line_filter = unpack_payload(payload, "B B B")
|
351
|
+
return GetConfiguration(Averaging(averaging), SensorType(sensor_type), LineFilter(line_filter))
|
352
|
+
|
353
|
+
async def get_error_state(self) -> GetErrorState:
|
354
|
+
"""
|
355
|
+
Returns the current error state. There are two possible errors:
|
356
|
+
|
357
|
+
* Over/Under Voltage and
|
358
|
+
* Open Circuit.
|
359
|
+
|
360
|
+
Over/Under Voltage happens for voltages below 0V or above 3.3V. In this case
|
361
|
+
it is very likely that your thermocouple is defective. An Open Circuit error
|
362
|
+
indicates that there is no thermocouple connected.
|
363
|
+
|
364
|
+
You can use the :cb:`Error State` callback to automatically get triggered
|
365
|
+
when the error state changes.
|
366
|
+
"""
|
367
|
+
_, payload = await self.ipcon.send_request(
|
368
|
+
device=self, function_id=FunctionID.GET_ERROR_STATE, response_expected=True
|
369
|
+
)
|
370
|
+
|
371
|
+
over_under, open_circuit = unpack_payload(payload, "! !")
|
372
|
+
return GetErrorState(over_under, open_circuit)
|
373
|
+
|
374
|
+
def __value_to_si(self, value: int) -> Decimal:
|
375
|
+
"""
|
376
|
+
Convert to the sensor value to SI units
|
377
|
+
"""
|
378
|
+
if self.__sensor_type is SensorType.TYPE_G8:
|
379
|
+
return Decimal(value / 8 / 1.6 / 2**17)
|
380
|
+
if self.__sensor_type is SensorType.TYPE_G32:
|
381
|
+
return Decimal(value / 8 / 1.6 / 2**17)
|
382
|
+
return Decimal(value + 27315) / 100
|
383
|
+
|
384
|
+
def __si_to_value(self, value: float | Decimal) -> int:
|
385
|
+
if self.__sensor_type is SensorType.TYPE_G8:
|
386
|
+
return int(float(value) * 8 * 1.6 * 2**17)
|
387
|
+
if self.__sensor_type is SensorType.TYPE_G32:
|
388
|
+
return int(float(value) * 8 * 1.6 * 2**17)
|
389
|
+
return int(value * 100) - 27315
|
390
|
+
|
391
|
+
async def read_events(
|
392
|
+
self,
|
393
|
+
events: tuple[int | _CallbackID, ...] | list[int | _CallbackID] | None = None,
|
394
|
+
sids: tuple[int, ...] | list[int] | None = None,
|
395
|
+
) -> AsyncGenerator[Event, None]:
|
396
|
+
registered_events = set()
|
397
|
+
if events:
|
398
|
+
for event in events:
|
399
|
+
registered_events.add(self.CallbackID(event))
|
400
|
+
if sids is not None:
|
401
|
+
for sid in sids:
|
402
|
+
for callback in self.SID_TO_CALLBACK.get(sid, []):
|
403
|
+
registered_events.add(callback)
|
404
|
+
|
405
|
+
if events is None and sids is None:
|
406
|
+
registered_events = set(self.CALLBACK_FORMATS.keys())
|
407
|
+
|
408
|
+
async for header, payload in super()._read_events():
|
409
|
+
try:
|
410
|
+
function_id = CallbackID(header.function_id)
|
411
|
+
except ValueError:
|
412
|
+
# Invalid header. Drop the packet.
|
413
|
+
continue
|
414
|
+
if function_id in registered_events:
|
415
|
+
value = unpack_payload(payload, self.CALLBACK_FORMATS[function_id])
|
416
|
+
if function_id is CallbackID.TEMPERATURE:
|
417
|
+
yield Event(self, 0, function_id, self.__value_to_si(value))
|
418
|
+
else:
|
419
|
+
yield Event(self, 1, function_id, value)
|
@@ -28,6 +28,7 @@ from .bricklet_segment_display_4x7 import BrickletSegmentDisplay4x7
|
|
28
28
|
from .bricklet_segment_display_4x7_v2 import BrickletSegmentDisplay4x7V2
|
29
29
|
from .bricklet_temperature import BrickletTemperature
|
30
30
|
from .bricklet_temperature_v2 import BrickletTemperatureV2
|
31
|
+
from .bricklet_thermocouple_v2 import BrickletThermocoupleV2
|
31
32
|
|
32
33
|
if TYPE_CHECKING:
|
33
34
|
from . import IPConnectionAsync
|
@@ -83,3 +84,4 @@ device_factory.register(BrickletSegmentDisplay4x7V2)
|
|
83
84
|
device_factory.register(BrickletRS232V2)
|
84
85
|
device_factory.register(BrickletTemperature)
|
85
86
|
device_factory.register(BrickletTemperatureV2)
|
87
|
+
device_factory.register(BrickletThermocoupleV2)
|
tinkerforge_async/devices.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: tinkerforge_async
|
3
|
-
Version: 1.5.
|
3
|
+
Version: 1.5.2
|
4
4
|
Summary: Python3 AsyncIO Tinkerforge driver
|
5
5
|
Author-email: Patrick Baus <patrick.baus@physik.tu-darmstadt.de>
|
6
6
|
License: GNU General Public License v3 (GPLv3)
|
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3.8
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.9
|
15
15
|
Classifier: Programming Language :: Python :: 3.10
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
17
18
|
Classifier: Development Status :: 5 - Production/Stable
|
18
19
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
19
20
|
Classifier: Operating System :: OS Independent
|
@@ -42,6 +43,7 @@ Provides-Extra: test
|
|
42
43
|
Requires-Dist: mypy ; extra == 'test'
|
43
44
|
Requires-Dist: pylint ; extra == 'test'
|
44
45
|
Requires-Dist: pytest ; extra == 'test'
|
46
|
+
Requires-Dist: setuptools ; extra == 'test'
|
45
47
|
|
46
48
|
[](../../actions/workflows/pylint.yml)
|
47
49
|
[](https://pypi.org/project/tinkerforge-async/)
|
@@ -84,6 +86,7 @@ The library is fully type-hinted.
|
|
84
86
|
| [Segment Display 4x7 2.0](https://www.tinkerforge.com/en/doc/Hardware/Bricklets/Segment_Display_4x7_V2.html) |:heavy_check_mark:|:heavy_check_mark:|
|
85
87
|
| [Temperature](https://www.tinkerforge.com/en/doc/Hardware/Bricklets/Temperature.html) |:heavy_check_mark:|:heavy_check_mark:|
|
86
88
|
| [Temperature 2.0](https://www.tinkerforge.com/en/doc/Hardware/Bricklets/Temperature_V2.html) |:heavy_check_mark:|:heavy_check_mark:|
|
89
|
+
| [Thermocouple 2.0](https://www.tinkerforge.com/en/doc/Hardware/Bricklets/Thermocouple_V2.html) |:heavy_check_mark:|:heavy_check_mark:|
|
87
90
|
|
88
91
|
## Documentation
|
89
92
|
The documentation is currently work in progress. The full documentation will be moved to
|
@@ -203,6 +206,11 @@ Some design choices of the original Tinkerforge API are overly complex. I theref
|
|
203
206
|
- `BrickletPtcV2()` takes an additional parameter to define the type of sensor. The options are `BrickletPtc.SensorType.PT_100` and `BrickletPtc.SensorType.PT_1000`. This only determines the resistance returned by the bricklet. The default is `BrickletPtc.SensorType.PT_100`.
|
204
207
|
- `BrickletPtcV2.sensor_type` getter and setter to change the type of sensor used.
|
205
208
|
|
209
|
+
- ### [Thermocouple Bricklet 2.0](https://www.tinkerforge.com/en/doc/Hardware/Bricklets/Thermocouple_V2.html)
|
210
|
+
- `BrickletThermocoupleV2()` takes an additional parameter to define the type of sensor. The options are of type `BrickletThermocoupleV2.SensorType`. The default is `BrickletPtc.SensorType.TYPE_K`.
|
211
|
+
- `BrickletThermocoupleV2.sensor_type` getter and setter to change the type of sensor used.
|
212
|
+
|
213
|
+
|
206
214
|
- ### [Segment Display 4x7 Bricklet 2.0](https://www.tinkerforge.com/en/doc/Hardware/Bricklets/Segment_Display_4x7_V2.html)
|
207
215
|
- `BrickletSegmentDisplay4x7V2.set_segments()` takes a `list`/`tuple` of 4 `int` instead of digit0, digit1, digit2, digit3. This is the same API as the older [Segment Display 4x7 Bricklet](https://www.tinkerforge.com/en/doc/Hardware/Bricklets/Segment_Display_4x7.html).
|
208
216
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
tinkerforge_async/__init__.py,sha256=wv8UTQrY0vlHwJEO-50PAeUt1cnrAdtSW-2by3CtaaE,376
|
2
|
-
tinkerforge_async/_version.py,sha256=
|
2
|
+
tinkerforge_async/_version.py,sha256=UqJ3HNCU2vsgoUrCIWF8t4wsYLyCQGZJ4mOgfolgaoU,65
|
3
3
|
tinkerforge_async/async_event_bus.py,sha256=Ng1bNw4tfPZUSMBudy47rTxLqJF96qpTEJIzh1MCq44,1806
|
4
4
|
tinkerforge_async/brick_master.py,sha256=kZDN_FzuYmGjaZvYFI2ba2Sfv1mwGoFx9ZayH1DJGqI,129607
|
5
5
|
tinkerforge_async/bricklet_ambient_light_v2.py,sha256=6OVxGK8kBU_ZcRXa9iJH_B8GbZ74XGBB5ix7WquS8yw,13402
|
@@ -16,19 +16,20 @@ tinkerforge_async/bricklet_io4_v2.py,sha256=GYb2N44jVV_hvijJ_YPx2Vf0Td-oJzGNT53g
|
|
16
16
|
tinkerforge_async/bricklet_isolator.py,sha256=miTYcDMzY6gQ8lNU8ZS6vcPs0HDr8JrJ0y1J7nKsJHs,11159
|
17
17
|
tinkerforge_async/bricklet_moisture.py,sha256=ICnnwPAcWmFcOL73OGWYlLlnAxsr_g7RUe6eZUNDR5I,10078
|
18
18
|
tinkerforge_async/bricklet_motion_detector_v2.py,sha256=nCbYQd4staw62_mYcUVF1qBhzG_04y0jbzXGOyLXcWQ,6451
|
19
|
-
tinkerforge_async/bricklet_ptc.py,sha256=
|
20
|
-
tinkerforge_async/bricklet_ptc_v2.py,sha256=
|
19
|
+
tinkerforge_async/bricklet_ptc.py,sha256=6tuVQvJVmM3LuRrvFJdDIXiNt-ArLeHQJkIqYuekqRY,22159
|
20
|
+
tinkerforge_async/bricklet_ptc_v2.py,sha256=tsH3e_Ppi7XQvhXzQclLxdubw8aZwSnkB0mPHaAsRUo,22648
|
21
21
|
tinkerforge_async/bricklet_rs232_v2.py,sha256=fsZ6SS5ZwJFUrQhXcKatvAki6Vni-ozz58zpri65ZRc,18053
|
22
22
|
tinkerforge_async/bricklet_segment_display_4x7.py,sha256=kl_ctg86w2-uPhEgiraSbtIDS0PH854_9LdA48xrBiQ,6404
|
23
23
|
tinkerforge_async/bricklet_segment_display_4x7_v2.py,sha256=T5g-voFLtLAOyWKn_TNuKDJVaG5vi_7eVCNvkE5ziIs,8902
|
24
24
|
tinkerforge_async/bricklet_temperature.py,sha256=wsaI5vq50d7X_FonRZfxpjtNOVM8tdqA74MPVrts8h4,11341
|
25
25
|
tinkerforge_async/bricklet_temperature_v2.py,sha256=uYSnfW1Hr1WxY5M7kVahtpaQEK5XvdIhM_HhdJdSc-g,9702
|
26
|
-
tinkerforge_async/
|
27
|
-
tinkerforge_async/
|
26
|
+
tinkerforge_async/bricklet_thermocouple_v2.py,sha256=KoVu6E3ZpXqR2aLMktepZVD86q0CQFYlGxUmBkVZO7o,15245
|
27
|
+
tinkerforge_async/device_factory.py,sha256=UkK8mU-UN3bBPI9Z8kRpbUslxwGbUiGglFnGRyg6c9o,3369
|
28
|
+
tinkerforge_async/devices.py,sha256=XQezaYuiH25MX8rD7sVMdfERYhDlvUg8x1jhj3xNsKI,15634
|
28
29
|
tinkerforge_async/ip_connection.py,sha256=KPAFGTysuCj8uVWMgVUDvP_zPOpjeYOKVb16SDLRcjE,27671
|
29
30
|
tinkerforge_async/ip_connection_helper.py,sha256=kt4JhONMxJdN4upIpVZlqTKmQcSyG2zXkmnCC2QL91Y,4305
|
30
|
-
tinkerforge_async-1.5.
|
31
|
-
tinkerforge_async-1.5.
|
32
|
-
tinkerforge_async-1.5.
|
33
|
-
tinkerforge_async-1.5.
|
34
|
-
tinkerforge_async-1.5.
|
31
|
+
tinkerforge_async-1.5.2.dist-info/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
|
32
|
+
tinkerforge_async-1.5.2.dist-info/METADATA,sha256=eUWus5K4ai4ueovreuHwyyy4TrTtBrBplXbE22D_C2w,16699
|
33
|
+
tinkerforge_async-1.5.2.dist-info/WHEEL,sha256=cpQTJ5IWu9CdaPViMhC9YzF8gZuS5-vlfoFihTBC86A,91
|
34
|
+
tinkerforge_async-1.5.2.dist-info/top_level.txt,sha256=YuYlWDVtbFvjqm-GoyH2asasmmn-lH1KeKBBtA17QJ4,18
|
35
|
+
tinkerforge_async-1.5.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|