iotfiy 1.0.0__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.
iotfiy/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """IOTFIY - MQTT gateway client for Raspberry Pi (3 / 4 / 5)."""
2
+
3
+ from .ref import Ref
4
+ from .client import IOTFIYManager, IOTFIY
5
+
6
+ __all__ = ["IOTFIY", "IOTFIYManager", "Ref"]
7
+ __version__ = "1.0.0"
iotfiy/client.py ADDED
@@ -0,0 +1,367 @@
1
+ """IOTFIY manager - Arduino IOTFIYManager API for Raspberry Pi."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import socket
7
+ import time
8
+ from typing import List, Optional
9
+
10
+ import paho.mqtt.client as mqtt
11
+
12
+ from .device_config import DeviceConfig
13
+ from .receive import Receive
14
+ from .ref import Ref
15
+ from .send import Send
16
+
17
+
18
+ class IOTFIYManager:
19
+ MAX_SENDS = 10
20
+ MAX_RECEIVES = 10
21
+
22
+ def __init__(self):
23
+ self._sends: List[Send] = []
24
+ self._receives: List[Receive] = []
25
+ self._config = DeviceConfig()
26
+ self._lwt_enabled = False
27
+ self._client: Optional[mqtt.Client] = None
28
+
29
+ def config_obj(self) -> DeviceConfig:
30
+ """Access underlying DeviceConfig (Arduino: config())."""
31
+ return self._config
32
+
33
+ def enable_lwt(self, enabled: bool = True) -> None:
34
+ self._lwt_enabled = enabled
35
+ state = "ENABLED (offline on disconnect)" if enabled else "disabled"
36
+ print(f"MQTT LWT {state}")
37
+
38
+ def config(
39
+ self,
40
+ ssid: str,
41
+ password: str,
42
+ mqtt_server: str,
43
+ mqtt_client_id: str,
44
+ device_id: str,
45
+ mqtt_port: int = 1883,
46
+ mqtt_username: str = "",
47
+ mqtt_password: str = "",
48
+ ) -> None:
49
+ """Same signature as Arduino IOTFIY.config(...).
50
+
51
+ On Raspberry Pi, WiFi is normally managed by the OS. ``ssid`` /
52
+ ``password`` are kept for API compatibility; MQTT settings are required.
53
+ """
54
+ self._config.ssid(ssid).password(password).server(mqtt_server).port(
55
+ mqtt_port
56
+ ).client_id(mqtt_client_id).device_id(device_id).mqtt_username(
57
+ mqtt_username
58
+ ).mqtt_password(mqtt_password)
59
+
60
+ def begin(self) -> None:
61
+ if not self._config.is_valid():
62
+ raise ValueError(
63
+ "IOTFIY config incomplete - set mqtt_server, client_id, device_id"
64
+ )
65
+
66
+ # paho-mqtt 2.x uses CallbackAPIVersion; 1.x does not.
67
+ try:
68
+ self._client = mqtt.Client(
69
+ mqtt.CallbackAPIVersion.VERSION1,
70
+ client_id=self._config.get_mqtt_client_id,
71
+ )
72
+ except (AttributeError, TypeError):
73
+ self._client = mqtt.Client(client_id=self._config.get_mqtt_client_id)
74
+
75
+ self._client.on_message = self._on_message
76
+ self._client.on_disconnect = self._on_disconnect
77
+
78
+ if self._config.get_mqtt_username:
79
+ self._client.username_pw_set(
80
+ self._config.get_mqtt_username,
81
+ self._config.get_mqtt_password,
82
+ )
83
+
84
+ if self._lwt_enabled:
85
+ will_payload = json.dumps(
86
+ {
87
+ "status": "offline",
88
+ "deviceId": self._config.get_device_id,
89
+ }
90
+ )
91
+ self._client.will_set(
92
+ self._config.get_status_topic(),
93
+ payload=will_payload,
94
+ qos=1,
95
+ retain=True,
96
+ )
97
+
98
+ self._bind_client_to_capabilities()
99
+
100
+ def register_all_capabilities(self) -> None:
101
+ start = {
102
+ "deviceId": self._config.get_device_id,
103
+ "event": "capabilities_registration_start",
104
+ "total_controls": len(self._receives),
105
+ "total_sensors": len(self._sends),
106
+ }
107
+ self._publish(self._config.get_status_topic(), start, retain=False)
108
+
109
+ for receive in self._receives:
110
+ receive.register_capability()
111
+ for send in self._sends:
112
+ send.register_capability()
113
+
114
+ end = {
115
+ "deviceId": self._config.get_device_id,
116
+ "event": "capabilities_registration_complete",
117
+ "controls_registered": len(self._receives),
118
+ "sensors_registered": len(self._sends),
119
+ }
120
+ self._publish(self._config.get_status_topic(), end, retain=False)
121
+
122
+ def launch(self) -> None:
123
+ for send in self._sends:
124
+ send.publish_data()
125
+
126
+ def publish_string_output(self, name: str, pin: str, value: str) -> bool:
127
+ if not self.is_mqtt_connected():
128
+ return False
129
+
130
+ cap = {
131
+ "deviceId": self._config.get_device_id,
132
+ "type": "capabilities_output",
133
+ "outputName": name,
134
+ "output": {"type": "string", "pin": pin},
135
+ }
136
+ cap_topic = (
137
+ f"devices/{self._config.get_device_id}/capabilities_output/{name}"
138
+ )
139
+ self._publish(cap_topic, cap, retain=True)
140
+
141
+ data = {
142
+ "type": "output",
143
+ "deviceId": self._config.get_device_id,
144
+ "data": {name: value, pin: value},
145
+ }
146
+ print(f"String output {pin}={value}")
147
+ return self._publish(self._config.get_data_topic(), data, retain=False)
148
+
149
+ def reconnect_mqtt(self) -> bool:
150
+ if self._client is None:
151
+ self.begin()
152
+
153
+ assert self._client is not None
154
+ if self._client.is_connected():
155
+ return True
156
+
157
+ for attempt in range(1, 6):
158
+ print(f"MQTT connection attempt {attempt}... ", end="", flush=True)
159
+ try:
160
+ self._client.connect(
161
+ self._config.get_mqtt_server,
162
+ self._config.get_mqtt_port,
163
+ keepalive=60,
164
+ )
165
+ # Give the network stack a moment, then poll until connected.
166
+ deadline = time.time() + 8
167
+ while time.time() < deadline and not self._client.is_connected():
168
+ self._client.loop(timeout=0.2)
169
+
170
+ if self._client.is_connected():
171
+ print("connected!")
172
+ if self._lwt_enabled:
173
+ print(f"LWT registered on {self._config.get_status_topic()}")
174
+
175
+ self._client.subscribe(self._config.get_commands_topic())
176
+ status = {
177
+ "status": "online",
178
+ "deviceId": self._config.get_device_id,
179
+ }
180
+ self._publish(
181
+ self._config.get_status_topic(), status, retain=True
182
+ )
183
+ self.register_all_capabilities()
184
+ return True
185
+
186
+ print("failed (timeout)")
187
+ except Exception as exc:
188
+ print(f"failed ({exc})")
189
+
190
+ if attempt < 5:
191
+ time.sleep(5)
192
+
193
+ return False
194
+
195
+ def check_and_reconnect_wifi(self) -> bool:
196
+ """On Pi, verify network path to the MQTT broker (OS manages Wi-Fi)."""
197
+ host = self._config.get_mqtt_server
198
+ port = self._config.get_mqtt_port
199
+ if not host:
200
+ print("WiFi/network check failed: MQTT server not configured")
201
+ return False
202
+
203
+ try:
204
+ with socket.create_connection((host, port), timeout=3):
205
+ return True
206
+ except OSError:
207
+ print("Network unreachable to MQTT broker. Will retry...")
208
+ return False
209
+
210
+ def is_mqtt_connected(self) -> bool:
211
+ return bool(self._client and self._client.is_connected())
212
+
213
+ def process_mqtt_messages(self) -> None:
214
+ if self._client is not None:
215
+ self._client.loop(timeout=0.05)
216
+
217
+ def write(
218
+ self,
219
+ name: str,
220
+ type_: str,
221
+ pin: str,
222
+ data_source: Ref,
223
+ min_value: Optional[float] = None,
224
+ max_value: Optional[float] = None,
225
+ ) -> Optional[Send]:
226
+ if len(self._sends) >= self.MAX_SENDS:
227
+ return None
228
+ if not isinstance(data_source, Ref):
229
+ raise TypeError("write() expects an iotfiy.Ref for data_source")
230
+
231
+ send = Send(
232
+ name,
233
+ type_,
234
+ pin,
235
+ data_source,
236
+ self._client,
237
+ self._config,
238
+ min_value,
239
+ max_value,
240
+ )
241
+ # Client may be created later in begin(); keep reference alive.
242
+ self._sends.append(send)
243
+ self._bind_client_to_capabilities()
244
+ return send
245
+
246
+ def read(
247
+ self,
248
+ name: str,
249
+ type_: str,
250
+ pin: str,
251
+ entry_point: Ref,
252
+ min_value: Optional[float] = None,
253
+ max_value: Optional[float] = None,
254
+ ) -> Optional[Receive]:
255
+ if len(self._receives) >= self.MAX_RECEIVES:
256
+ return None
257
+ if not isinstance(entry_point, Ref):
258
+ raise TypeError("read() expects an iotfiy.Ref for entry_point")
259
+
260
+ receive = Receive(
261
+ name,
262
+ type_,
263
+ pin,
264
+ entry_point,
265
+ self._client,
266
+ self._config,
267
+ min_value,
268
+ max_value,
269
+ )
270
+ self._receives.append(receive)
271
+ self._bind_client_to_capabilities()
272
+ return receive
273
+
274
+ def _bind_client_to_capabilities(self) -> None:
275
+ if self._client is None:
276
+ return
277
+ for send in self._sends:
278
+ send._client = self._client
279
+ for receive in self._receives:
280
+ receive._client = self._client
281
+
282
+ def _publish(self, topic: str, payload: dict, retain: bool) -> bool:
283
+ if not self.is_mqtt_connected():
284
+ return False
285
+ assert self._client is not None
286
+ try:
287
+ info = self._client.publish(topic, json.dumps(payload), retain=retain)
288
+ return info.rc == 0
289
+ except Exception as exc:
290
+ print(f"MQTT publish error: {exc}")
291
+ return False
292
+
293
+ def _on_disconnect(self, client, userdata, rc):
294
+ if rc != 0:
295
+ print(f"Unexpected MQTT disconnect (rc={rc})")
296
+
297
+ def _on_message(self, client, userdata, msg):
298
+ try:
299
+ message = msg.payload.decode("utf-8", errors="replace")
300
+ except Exception:
301
+ return
302
+
303
+ print(f"Message arrived [{msg.topic}] {message}")
304
+
305
+ try:
306
+ doc = json.loads(message)
307
+ except json.JSONDecodeError as exc:
308
+ print(f"JSON parse failed: {exc}")
309
+ return
310
+
311
+ if msg.topic != self._config.get_commands_topic():
312
+ return
313
+
314
+ value = None
315
+ control_name = None
316
+ pin_name = None
317
+
318
+ if isinstance(doc, dict) and "data" in doc and "type" in doc:
319
+ data = doc.get("data") or {}
320
+ value = data.get("value")
321
+ control_name = data.get("control")
322
+ pin_name = data.get("pin")
323
+ elif isinstance(doc, dict):
324
+ value = doc.get("value")
325
+ control_name = doc.get("control")
326
+ pin_name = doc.get("pin")
327
+
328
+ target: Optional[Receive] = None
329
+ if control_name:
330
+ for receive in self._receives:
331
+ if receive.get_name() == control_name:
332
+ target = receive
333
+ break
334
+ elif pin_name:
335
+ for receive in self._receives:
336
+ if receive.get_pin() == pin_name:
337
+ target = receive
338
+ break
339
+
340
+ if target is not None:
341
+ result = {}
342
+ success = target.handle_command(value, result)
343
+ response = {
344
+ "status": "success" if success else "error",
345
+ "control": target.get_name(),
346
+ "result": result,
347
+ }
348
+ self._publish(
349
+ self._config.get_command_response_topic(), response, retain=False
350
+ )
351
+ else:
352
+ print("ERROR: No matching control or pin found")
353
+ error = {
354
+ "status": "error",
355
+ "message": "No matching control or pin found",
356
+ }
357
+ if control_name:
358
+ error["control"] = control_name
359
+ if pin_name:
360
+ error["pin"] = pin_name
361
+ self._publish(
362
+ self._config.get_command_response_topic(), error, retain=False
363
+ )
364
+
365
+
366
+ # Global instance (Arduino: extern IOTFIYManager IOTFIY)
367
+ IOTFIY = IOTFIYManager()
@@ -0,0 +1,96 @@
1
+ """Device / MQTT configuration (mirrors Arduino DeviceConfig)."""
2
+
3
+
4
+ class DeviceConfig:
5
+ def __init__(self):
6
+ self._ssid = ""
7
+ self._password = ""
8
+ self._mqtt_server = ""
9
+ self._mqtt_port = 1883
10
+ self._mqtt_client_id = ""
11
+ self._mqtt_username = ""
12
+ self._mqtt_password = ""
13
+ self._device_id = ""
14
+
15
+ def ssid(self, value: str):
16
+ self._ssid = value or ""
17
+ return self
18
+
19
+ def password(self, value: str):
20
+ self._password = value or ""
21
+ return self
22
+
23
+ def server(self, value: str):
24
+ self._mqtt_server = value or ""
25
+ return self
26
+
27
+ def port(self, value: int):
28
+ self._mqtt_port = int(value)
29
+ return self
30
+
31
+ def client_id(self, value: str):
32
+ self._mqtt_client_id = value or ""
33
+ return self
34
+
35
+ def device_id(self, value: str):
36
+ self._device_id = value or ""
37
+ return self
38
+
39
+ def mqtt_username(self, value: str):
40
+ self._mqtt_username = value or ""
41
+ return self
42
+
43
+ def mqtt_password(self, value: str):
44
+ self._mqtt_password = value or ""
45
+ return self
46
+
47
+ @property
48
+ def get_ssid(self) -> str:
49
+ return self._ssid
50
+
51
+ @property
52
+ def get_password(self) -> str:
53
+ return self._password
54
+
55
+ @property
56
+ def get_mqtt_server(self) -> str:
57
+ return self._mqtt_server
58
+
59
+ @property
60
+ def get_mqtt_port(self) -> int:
61
+ return self._mqtt_port
62
+
63
+ @property
64
+ def get_mqtt_client_id(self) -> str:
65
+ return self._mqtt_client_id
66
+
67
+ @property
68
+ def get_device_id(self) -> str:
69
+ return self._device_id
70
+
71
+ @property
72
+ def get_mqtt_username(self) -> str:
73
+ return self._mqtt_username
74
+
75
+ @property
76
+ def get_mqtt_password(self) -> str:
77
+ return self._mqtt_password
78
+
79
+ def get_data_topic(self) -> str:
80
+ return f"devices/{self._device_id}/data"
81
+
82
+ def get_status_topic(self) -> str:
83
+ return f"devices/{self._device_id}/status"
84
+
85
+ def get_commands_topic(self) -> str:
86
+ return f"devices/{self._device_id}/commands"
87
+
88
+ def get_command_response_topic(self) -> str:
89
+ return f"devices/{self._device_id}/command-response"
90
+
91
+ def is_valid(self) -> bool:
92
+ return bool(
93
+ self._mqtt_server
94
+ and self._mqtt_client_id
95
+ and self._device_id
96
+ )
iotfiy/receive.py ADDED
@@ -0,0 +1,124 @@
1
+ """Control capability (mirrors Arduino Receive)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import time
7
+ from typing import TYPE_CHECKING, Any, Optional
8
+
9
+ from .ref import Ref
10
+
11
+ if TYPE_CHECKING:
12
+ from .device_config import DeviceConfig
13
+
14
+
15
+ class Receive:
16
+ def __init__(
17
+ self,
18
+ name: str,
19
+ type_: str,
20
+ pin: str,
21
+ entry_point: Ref,
22
+ client: Any,
23
+ config: DeviceConfig,
24
+ min_value: Optional[float] = None,
25
+ max_value: Optional[float] = None,
26
+ ):
27
+ self._client = client
28
+ self._config = config
29
+ self.name = name
30
+ self.type = type_
31
+ self.pin = pin
32
+ self.entry_point = entry_point
33
+ self.has_range = min_value is not None and max_value is not None
34
+ self.min = float(min_value) if min_value is not None else 0.0
35
+ self.max = float(max_value) if max_value is not None else 0.0
36
+ self.is_registered = False
37
+ self.default_delay = 0.05
38
+
39
+ def get_name(self) -> str:
40
+ return self.name
41
+
42
+ def get_type(self) -> str:
43
+ return self.type
44
+
45
+ def get_pin(self) -> str:
46
+ return self.pin
47
+
48
+ def register_capability(self) -> bool:
49
+ control = {"type": self.type, "pin": self.pin}
50
+ if self.type in ("float", "integer") and self.has_range:
51
+ control["min"] = self.min
52
+ control["max"] = self.max
53
+
54
+ payload = {
55
+ "deviceId": self._config.get_device_id,
56
+ "type": "capabilities_control",
57
+ "controlName": self.name,
58
+ "control": control,
59
+ }
60
+ topic = f"devices/{self._config.get_device_id}/capabilities_control/{self.name}"
61
+ published = self._publish(topic, payload, retain=True)
62
+ print(f"{self.name} control published: {'SUCCESS' if published else 'FAILED'}")
63
+ if published:
64
+ self.is_registered = True
65
+ time.sleep(self.default_delay)
66
+ return published
67
+
68
+ def handle_command(self, value: Any, result: dict) -> bool:
69
+ if self.type == "float":
70
+ float_value = float(value)
71
+ if self.has_range:
72
+ float_value = max(self.min, min(self.max, float_value))
73
+ self.entry_point.value = float_value
74
+ result["newState"] = float_value
75
+ print(
76
+ f"[RX] pin={self.pin} name={self.name} value={float_value} "
77
+ f"<- backend"
78
+ )
79
+ return True
80
+
81
+ if self.type == "integer":
82
+ int_value = int(value)
83
+ if self.has_range:
84
+ int_value = int(max(self.min, min(self.max, int_value)))
85
+ self.entry_point.value = int_value
86
+ result["newState"] = int_value
87
+ print(
88
+ f"[RX] pin={self.pin} name={self.name} value={int_value} "
89
+ f"<- backend"
90
+ )
91
+ return True
92
+
93
+ if self.type == "boolean":
94
+ str_value = str(value).strip().lower()
95
+ bool_value = 1.0 if str_value in ("on", "true", "1") else 0.0
96
+ self.entry_point.value = bool_value
97
+ result["newState"] = str(value)
98
+ print(
99
+ f"[RX] pin={self.pin} name={self.name} value={str_value} "
100
+ f"(truth={bool_value}) <- backend"
101
+ )
102
+ return True
103
+
104
+ if self.type == "string":
105
+ str_value = str(value)
106
+ self.entry_point.value = str_value
107
+ result["newState"] = str_value
108
+ print(
109
+ f"[RX] pin={self.pin} name={self.name} value={str_value} "
110
+ f"<- backend"
111
+ )
112
+ return True
113
+
114
+ return False
115
+
116
+ def _publish(self, topic: str, payload: dict, retain: bool) -> bool:
117
+ if self._client is None or not self._client.is_connected():
118
+ return False
119
+ try:
120
+ info = self._client.publish(topic, json.dumps(payload), retain=retain)
121
+ return info.rc == 0
122
+ except Exception as exc:
123
+ print(f"Receive publish error: {exc}")
124
+ return False
iotfiy/ref.py ADDED
@@ -0,0 +1,21 @@
1
+ """Mutable value holder (Arduino float* / String* equivalent)."""
2
+
3
+
4
+ class Ref:
5
+ """Hold a value that write()/read() can update in place.
6
+
7
+ Example::
8
+
9
+ sensor = Ref(0.0)
10
+ IOTFIY.write("SensorValue", "float", "A0", sensor)
11
+ sensor.value = 42.5
12
+ IOTFIY.launch()
13
+ """
14
+
15
+ __slots__ = ("value",)
16
+
17
+ def __init__(self, value=0.0):
18
+ self.value = value
19
+
20
+ def __repr__(self):
21
+ return f"Ref({self.value!r})"
iotfiy/send.py ADDED
@@ -0,0 +1,102 @@
1
+ """Output / sensor capability (mirrors Arduino Send)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import time
7
+ from typing import TYPE_CHECKING, Any, Optional
8
+
9
+ from .ref import Ref
10
+
11
+ if TYPE_CHECKING:
12
+ from .device_config import DeviceConfig
13
+
14
+
15
+ class Send:
16
+ def __init__(
17
+ self,
18
+ name: str,
19
+ type_: str,
20
+ pin: str,
21
+ exit_point: Ref,
22
+ client: Any,
23
+ config: DeviceConfig,
24
+ min_value: Optional[float] = None,
25
+ max_value: Optional[float] = None,
26
+ ):
27
+ self._client = client
28
+ self._config = config
29
+ self.name = name
30
+ self.type = type_
31
+ self.pin = pin
32
+ self.exit_point = exit_point
33
+ self.has_range = min_value is not None and max_value is not None
34
+ self.min = float(min_value) if min_value is not None else 0.0
35
+ self.max = float(max_value) if max_value is not None else 0.0
36
+ self.is_registered = False
37
+ self.default_delay = 0.05
38
+
39
+ def get_name(self) -> str:
40
+ return self.name
41
+
42
+ def get_type(self) -> str:
43
+ return self.type
44
+
45
+ def get_pin(self) -> str:
46
+ return self.pin
47
+
48
+ def get_current_value(self) -> float:
49
+ return float(self.exit_point.value)
50
+
51
+ def set_delay(self, ms: int) -> None:
52
+ self.default_delay = max(0, ms) / 1000.0
53
+
54
+ def register_capability(self) -> bool:
55
+ payload = {
56
+ "deviceId": self._config.get_device_id,
57
+ "type": "capabilities_output",
58
+ "outputName": self.name,
59
+ "output": {"type": self.type, "pin": self.pin},
60
+ }
61
+ topic = f"devices/{self._config.get_device_id}/capabilities_output/{self.name}"
62
+ published = self._publish(topic, payload, retain=True)
63
+ print(f"{self.name} output published: {'SUCCESS' if published else 'FAILED'}")
64
+ if published:
65
+ self.is_registered = True
66
+ time.sleep(self.default_delay)
67
+ return published
68
+
69
+ def publish_data(self) -> bool:
70
+ value = float(self.exit_point.value)
71
+ if self.has_range:
72
+ value = max(self.min, min(self.max, value))
73
+
74
+ if not self.is_registered:
75
+ self.register_capability()
76
+
77
+ data = {self.name: value}
78
+ if self.pin:
79
+ data[self.pin] = value
80
+
81
+ payload = {
82
+ "type": "output",
83
+ "deviceId": self._config.get_device_id,
84
+ "data": data,
85
+ }
86
+ published = self._publish(self._config.get_data_topic(), payload, retain=False)
87
+ status = "OK" if published else "FAIL"
88
+ print(
89
+ f"[TX] pin={self.pin} name={self.name} value={value} "
90
+ f"-> backend {status}"
91
+ )
92
+ return published
93
+
94
+ def _publish(self, topic: str, payload: dict, retain: bool) -> bool:
95
+ if self._client is None or not self._client.is_connected():
96
+ return False
97
+ try:
98
+ info = self._client.publish(topic, json.dumps(payload), retain=retain)
99
+ return info.rc == 0
100
+ except Exception as exc:
101
+ print(f"Send publish error: {exc}")
102
+ return False
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: iotfiy
3
+ Version: 1.0.0
4
+ Summary: IOTFIY MQTT gateway client for Raspberry Pi 3/4/5
5
+ Author-email: Iotfiy Solutions <iotfiy.solution@gmail.com>
6
+ Keywords: iot,mqtt,raspberry-pi,iotfiy
7
+ Requires-Python: >=3.9
8
+ Requires-Dist: paho-mqtt>=1.6.1
9
+ Provides-Extra: gpio
10
+ Requires-Dist: gpiozero; extra == "gpio"
@@ -0,0 +1,10 @@
1
+ iotfiy/__init__.py,sha256=FYm_DMp2GUmntEfHegdXUnZL8wd1z9nQxQTrS6Pw8VM,204
2
+ iotfiy/client.py,sha256=nAdt67Jhjbymr513dupNSPIWejZxwOUZepjuOd_64i8,12224
3
+ iotfiy/device_config.py,sha256=pqHENRKWy5uMhPBL3b9xd6tpsSdhOGdYJKGuy7Vg244,2442
4
+ iotfiy/receive.py,sha256=k9ZiZmUZiBkIiHokEykffruCAYRuWzD_PjhzZitE5m4,4180
5
+ iotfiy/ref.py,sha256=tfl7ptNrJbqTq4qdXVnQ2HYvSGdrSu-sIY993PxoHpA,476
6
+ iotfiy/send.py,sha256=NHgmgT-8OHgTRMJzoEpJ8ABaP5BScBvJgWlZvT2zIRg,3212
7
+ iotfiy-1.0.0.dist-info/METADATA,sha256=4iGl-GiraD0c7nuSIVtX7Bh3IMomIZESA6fslNRZvww,334
8
+ iotfiy-1.0.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
9
+ iotfiy-1.0.0.dist-info/top_level.txt,sha256=8-lQBy51P3eE9qiLhXWM4SyipsUTvkZANDMFUtIWFN8,7
10
+ iotfiy-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ iotfiy