gryfsmartio 0.1__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.
- gryfsmartio-0.1/LICENCE +21 -0
- gryfsmartio-0.1/PKG-INFO +15 -0
- gryfsmartio-0.1/gryfsmartio/__init__.py +0 -0
- gryfsmartio-0.1/gryfsmartio/parsing.py +155 -0
- gryfsmartio-0.1/gryfsmartio/transport.py +294 -0
- gryfsmartio-0.1/gryfsmartio.egg-info/PKG-INFO +15 -0
- gryfsmartio-0.1/gryfsmartio.egg-info/SOURCES.txt +11 -0
- gryfsmartio-0.1/gryfsmartio.egg-info/dependency_links.txt +1 -0
- gryfsmartio-0.1/gryfsmartio.egg-info/requires.txt +1 -0
- gryfsmartio-0.1/gryfsmartio.egg-info/top_level.txt +1 -0
- gryfsmartio-0.1/pyproject.toml +3 -0
- gryfsmartio-0.1/setup.cfg +24 -0
gryfsmartio-0.1/LICENCE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Gryf Smart
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
gryfsmartio-0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gryfsmartio
|
|
3
|
+
Version: 0.1
|
|
4
|
+
Summary: Library for the GryfSmart system
|
|
5
|
+
Home-page: https://github.com/karlowiczpl/gryfsmartio
|
|
6
|
+
Author: @karlowiczpl
|
|
7
|
+
Author-email: kkarlowicz@gryfsmart.pl
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENCE
|
|
14
|
+
Requires-Dist: pyserial-asyncio==0.6
|
|
15
|
+
Dynamic: license-file
|
|
File without changes
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
from pydantic import BaseModel, Field, EmailStr
|
|
5
|
+
|
|
6
|
+
_LOGGER = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
class ParsedFunctions():
|
|
9
|
+
INPUTS = "I"
|
|
10
|
+
OUTPUTS = "O"
|
|
11
|
+
PWM = "LED"
|
|
12
|
+
COVER = "R"
|
|
13
|
+
TEMP = "T"
|
|
14
|
+
FIND = "AT+FIND"
|
|
15
|
+
PONG = "PONG"
|
|
16
|
+
PRESS_LONG = "PL"
|
|
17
|
+
PRESS_SHORT = "PS"
|
|
18
|
+
|
|
19
|
+
broadcastingFunctions = [
|
|
20
|
+
ParsedFunctions.INPUTS,
|
|
21
|
+
ParsedFunctions.OUTPUTS,
|
|
22
|
+
ParsedFunctions.COVER,
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
subscriptableFunction = [
|
|
26
|
+
ParsedFunctions.INPUTS,
|
|
27
|
+
ParsedFunctions.OUTPUTS,
|
|
28
|
+
ParsedFunctions.PWM,
|
|
29
|
+
ParsedFunctions.COVER,
|
|
30
|
+
ParsedFunctions.TEMP,
|
|
31
|
+
ParsedFunctions.PRESS_SHORT,
|
|
32
|
+
ParsedFunctions.PRESS_LONG,
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
class ParsedData:
|
|
36
|
+
|
|
37
|
+
_function: str
|
|
38
|
+
_pin: int
|
|
39
|
+
_id: int
|
|
40
|
+
_parsed_states: list[str]
|
|
41
|
+
_broadcast_function: bool
|
|
42
|
+
_error = True
|
|
43
|
+
|
|
44
|
+
def __init__(self, data: str) -> None:
|
|
45
|
+
|
|
46
|
+
if('=' not in data or not data):
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
parts = data.split('=', 1)
|
|
51
|
+
|
|
52
|
+
self._function = parts[0].upper()
|
|
53
|
+
self._parsed_states = parts[1].split(',')
|
|
54
|
+
self._id = int(self._parsed_states[0])
|
|
55
|
+
|
|
56
|
+
if(self._function in broadcastingFunctions):
|
|
57
|
+
self._broadcast_function = True
|
|
58
|
+
else:
|
|
59
|
+
self._broadcast_function = False
|
|
60
|
+
|
|
61
|
+
self._error = False
|
|
62
|
+
|
|
63
|
+
except Exception as e:
|
|
64
|
+
_LOGGER.error(f"Error occurred while parsing: {e}")
|
|
65
|
+
|
|
66
|
+
def error_occurred(self) -> bool:
|
|
67
|
+
return self._error
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def is_broadcast(self) -> bool:
|
|
71
|
+
return self._broadcast_function
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def function(self) -> str:
|
|
75
|
+
return self._function
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def parsed_states(self) -> list[str]:
|
|
79
|
+
return self._parsed_states
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def id(self) -> int:
|
|
83
|
+
return self._id
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def pin(self) -> int:
|
|
87
|
+
if(not self._broadcast_function):
|
|
88
|
+
return int(self._parsed_states[1])
|
|
89
|
+
|
|
90
|
+
return 0
|
|
91
|
+
|
|
92
|
+
class Driver:
|
|
93
|
+
id: int
|
|
94
|
+
inputs: list[int]
|
|
95
|
+
outputs: list[int]
|
|
96
|
+
pwms: list[int]
|
|
97
|
+
covers: list[int]
|
|
98
|
+
|
|
99
|
+
def __init__(
|
|
100
|
+
self,
|
|
101
|
+
id: int
|
|
102
|
+
) -> None:
|
|
103
|
+
self.id = id
|
|
104
|
+
self.inputs: list[int] = [0] * 20
|
|
105
|
+
self.outputs: list[int] = [0] * 20
|
|
106
|
+
self.pwms: list[int] = [0] * 20
|
|
107
|
+
self.covers: list[int] = [0] * 20
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def function_map(self) -> dict[str, list[int]]:
|
|
111
|
+
return {
|
|
112
|
+
ParsedFunctions.INPUTS: self.inputs,
|
|
113
|
+
ParsedFunctions.OUTPUTS: self.outputs,
|
|
114
|
+
ParsedFunctions.PWM: self.pwms,
|
|
115
|
+
ParsedFunctions.COVER: self.covers,
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
def __getitem__(self, key: str) -> list[int]:
|
|
119
|
+
return self.function_map[key]
|
|
120
|
+
|
|
121
|
+
class Subscription:
|
|
122
|
+
_function: str
|
|
123
|
+
_id: int
|
|
124
|
+
_pin: int
|
|
125
|
+
_fun_ptr = None
|
|
126
|
+
|
|
127
|
+
def __init__(
|
|
128
|
+
self,
|
|
129
|
+
hardware_id: int,
|
|
130
|
+
hardware_pin: int,
|
|
131
|
+
function: str,
|
|
132
|
+
async_fun_ptr
|
|
133
|
+
) -> None:
|
|
134
|
+
self._id = hardware_id
|
|
135
|
+
self._pin = hardware_pin
|
|
136
|
+
self._function = function.strip()
|
|
137
|
+
|
|
138
|
+
self._fun_ptr = async_fun_ptr
|
|
139
|
+
|
|
140
|
+
def cover_with_data(self, parsed_data: ParsedData) -> bool:
|
|
141
|
+
if(parsed_data.function.strip() != self._function):
|
|
142
|
+
return False
|
|
143
|
+
|
|
144
|
+
if(parsed_data.is_broadcast):
|
|
145
|
+
if(parsed_data.id == self._id):
|
|
146
|
+
return True
|
|
147
|
+
else:
|
|
148
|
+
if(parsed_data.id == self._id and parsed_data.pin == self._pin):
|
|
149
|
+
return True
|
|
150
|
+
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
async def exec_fun(self, parsed_data):
|
|
154
|
+
await self._fun_ptr(parsed_data)
|
|
155
|
+
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import re
|
|
4
|
+
import serial_asyncio
|
|
5
|
+
|
|
6
|
+
from .parsing import ParsedData, ParsedFunctions, Subscription, subscriptableFunction
|
|
7
|
+
from gryfsmartio.parsing import Driver
|
|
8
|
+
|
|
9
|
+
_LOGGER = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
TCP_PORT = 4510
|
|
12
|
+
SERIAL_BAUDRATE = 115200
|
|
13
|
+
|
|
14
|
+
class WriterBase:
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
port: str,
|
|
19
|
+
) -> None:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
def write(
|
|
23
|
+
self,
|
|
24
|
+
data: str
|
|
25
|
+
) -> None:
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
async def read(self) -> str:
|
|
29
|
+
return ""
|
|
30
|
+
|
|
31
|
+
async def close(self) -> None:
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
async def open(self) -> None:
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
class SerialWriter(WriterBase):
|
|
38
|
+
_port: str
|
|
39
|
+
_baudrate: int
|
|
40
|
+
_reader: asyncio.StreamReader | None = None
|
|
41
|
+
_writer: asyncio.StreamWriter | None = None
|
|
42
|
+
|
|
43
|
+
def __init__(self, port: str, baudrate: int = SERIAL_BAUDRATE) -> None:
|
|
44
|
+
self._port = port
|
|
45
|
+
self._baudrate = baudrate
|
|
46
|
+
|
|
47
|
+
async def open(self) -> None:
|
|
48
|
+
self._reader, self._writer = await serial_asyncio.open_serial_connection(
|
|
49
|
+
url=self._port,
|
|
50
|
+
baudrate=self._baudrate
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
async def write(
|
|
54
|
+
self,
|
|
55
|
+
data: str
|
|
56
|
+
) -> None:
|
|
57
|
+
if self._writer is None or self._writer.is_closing():
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
if not data.endswith("\n"):
|
|
62
|
+
data += "\n"
|
|
63
|
+
|
|
64
|
+
self._writer.write(data.encode("utf-8"))
|
|
65
|
+
|
|
66
|
+
await self._writer.drain()
|
|
67
|
+
_LOGGER.debug(f"Serial sent: {data.strip()}")
|
|
68
|
+
|
|
69
|
+
except Exception as err:
|
|
70
|
+
_LOGGER.error(f"Error while serial send: {err}")
|
|
71
|
+
|
|
72
|
+
async def read(self) -> str:
|
|
73
|
+
if self._reader is None:
|
|
74
|
+
return ""
|
|
75
|
+
|
|
76
|
+
line_bytes = await asyncio.wait_for(self._reader.readline(), timeout=10.0)
|
|
77
|
+
|
|
78
|
+
return line_bytes.decode("utf-8", errors="ignore").strip()
|
|
79
|
+
|
|
80
|
+
async def close(self) -> None:
|
|
81
|
+
if self._writer is not None:
|
|
82
|
+
try:
|
|
83
|
+
self._writer.close()
|
|
84
|
+
await self._writer.wait_closed()
|
|
85
|
+
except Exception:
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
self._reader = None
|
|
89
|
+
self._writer = None
|
|
90
|
+
|
|
91
|
+
class TcpWriter(WriterBase):
|
|
92
|
+
_ip: str
|
|
93
|
+
_reader = None
|
|
94
|
+
_writer = None
|
|
95
|
+
|
|
96
|
+
def __init__(
|
|
97
|
+
self,
|
|
98
|
+
ip: str,
|
|
99
|
+
) -> None:
|
|
100
|
+
self._ip = ip
|
|
101
|
+
|
|
102
|
+
async def write(
|
|
103
|
+
self,
|
|
104
|
+
data: str
|
|
105
|
+
) -> None:
|
|
106
|
+
if self._writer is None or self._writer.is_closing():
|
|
107
|
+
_LOGGER.error("No active TCP connecction - cannot send data")
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
if not data.endswith("\n"):
|
|
112
|
+
data += "\n"
|
|
113
|
+
|
|
114
|
+
self._writer.write(data.encode("utf-8"))
|
|
115
|
+
await self._writer.drain()
|
|
116
|
+
|
|
117
|
+
_LOGGER.debug(f"Command sended: {data.strip()}")
|
|
118
|
+
|
|
119
|
+
except Exception as err:
|
|
120
|
+
_LOGGER.error(f"En Error while bufforing TCP data: {err}")
|
|
121
|
+
|
|
122
|
+
async def read(self) -> str:
|
|
123
|
+
if self._reader is None:
|
|
124
|
+
_LOGGER.error("Connection don't exist")
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
line_bytes = await asyncio.wait_for(self._reader.readline(), timeout=1000.0)
|
|
128
|
+
|
|
129
|
+
if not line_bytes:
|
|
130
|
+
_LOGGER.error("Connection don't exist")
|
|
131
|
+
|
|
132
|
+
return line_bytes.decode("utf-8", errors="ignore").strip()
|
|
133
|
+
|
|
134
|
+
except (AttributeError, Exception) as err:
|
|
135
|
+
_LOGGER.debug(f"En Error occurred while reading TCP: {err}")
|
|
136
|
+
raise err
|
|
137
|
+
|
|
138
|
+
async def close(self) -> None:
|
|
139
|
+
if self._writer is not None:
|
|
140
|
+
try:
|
|
141
|
+
self._writer.close()
|
|
142
|
+
|
|
143
|
+
await self._writer.wait_closed()
|
|
144
|
+
except Exception as e:
|
|
145
|
+
_LOGGER.error(f"An Error occurred while closing connection: {self._ip}")
|
|
146
|
+
|
|
147
|
+
self._reader = None
|
|
148
|
+
self._writer = None
|
|
149
|
+
|
|
150
|
+
async def open(self) -> None:
|
|
151
|
+
self._reader, self._writer = await asyncio.wait_for(
|
|
152
|
+
asyncio.open_connection(self._ip, TCP_PORT),
|
|
153
|
+
timeout=5.0
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
sock = self._writer.get_extra_info('socket')
|
|
157
|
+
if sock is not None:
|
|
158
|
+
import socket
|
|
159
|
+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
|
160
|
+
|
|
161
|
+
class Transport():
|
|
162
|
+
|
|
163
|
+
_connection: WriterBase
|
|
164
|
+
_target: str
|
|
165
|
+
_subscriptions = None
|
|
166
|
+
_task: asyncio.Task | None = None
|
|
167
|
+
_drivers_data: list[Driver] = []
|
|
168
|
+
|
|
169
|
+
def __init__(
|
|
170
|
+
self,
|
|
171
|
+
connection_target: str
|
|
172
|
+
) -> None:
|
|
173
|
+
|
|
174
|
+
self._target = connection_target
|
|
175
|
+
|
|
176
|
+
ipv4_patern = r"\b(?:\d{1,3}\.){3}\d{1,3}\b"
|
|
177
|
+
serial_port_patern = r"/dev/tty\S*"
|
|
178
|
+
|
|
179
|
+
if re.search(ipv4_patern , connection_target):
|
|
180
|
+
self._connection = TcpWriter(connection_target)
|
|
181
|
+
elif re.search(serial_port_patern , connection_target):
|
|
182
|
+
pass
|
|
183
|
+
else:
|
|
184
|
+
_LOGGER.error(f"Incorrect Communication Port: {connection_target}")
|
|
185
|
+
|
|
186
|
+
def register_subscription(self, subscription: Subscription):
|
|
187
|
+
if self._subscriptions is None:
|
|
188
|
+
self._subscriptions = []
|
|
189
|
+
|
|
190
|
+
self._subscriptions.append(subscription)
|
|
191
|
+
|
|
192
|
+
async def write(
|
|
193
|
+
self,
|
|
194
|
+
data: str,
|
|
195
|
+
) -> None:
|
|
196
|
+
await self._connection.write(data)
|
|
197
|
+
|
|
198
|
+
def start_communication(self) -> None:
|
|
199
|
+
|
|
200
|
+
if self._task is not None and not self._task.done():
|
|
201
|
+
_LOGGER.warning("Communication: %s already exists!", self._target)
|
|
202
|
+
return
|
|
203
|
+
|
|
204
|
+
self._task = asyncio.create_task(
|
|
205
|
+
self.communication_task(),
|
|
206
|
+
name=f"transport_task_{self._target}"
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
async def stop_communication(self) -> None:
|
|
210
|
+
|
|
211
|
+
if self._task is not None:
|
|
212
|
+
self._task.cancel()
|
|
213
|
+
try:
|
|
214
|
+
await self._task
|
|
215
|
+
except asyncio.CancelledError:
|
|
216
|
+
pass
|
|
217
|
+
finally:
|
|
218
|
+
self._task = None
|
|
219
|
+
|
|
220
|
+
async def communication_task(self):
|
|
221
|
+
|
|
222
|
+
while True:
|
|
223
|
+
try:
|
|
224
|
+
_LOGGER.info(f"Connecting with: {self._target}")
|
|
225
|
+
await self._connection.open()
|
|
226
|
+
|
|
227
|
+
_LOGGER.info(f"Successfuly connected to: {self._target}")
|
|
228
|
+
|
|
229
|
+
while True:
|
|
230
|
+
try:
|
|
231
|
+
readed = await self._connection.read()
|
|
232
|
+
|
|
233
|
+
if(readed):
|
|
234
|
+
_LOGGER.info(f"New command has arived: {readed}")
|
|
235
|
+
|
|
236
|
+
if(readed == "??????????"):
|
|
237
|
+
continue
|
|
238
|
+
|
|
239
|
+
parsed_data = ParsedData(readed)
|
|
240
|
+
|
|
241
|
+
if(parsed_data.error_occurred() or parsed_data.function not in subscriptableFunction):
|
|
242
|
+
continue
|
|
243
|
+
|
|
244
|
+
if(self._subscriptions is not None):
|
|
245
|
+
for sub in self._subscriptions:
|
|
246
|
+
if(sub.cover_with_data(parsed_data)):
|
|
247
|
+
await sub.exec_fun(parsed_data)
|
|
248
|
+
|
|
249
|
+
exist = False
|
|
250
|
+
for item in self._drivers_data:
|
|
251
|
+
if item.id == parsed_data.id:
|
|
252
|
+
exist = True
|
|
253
|
+
|
|
254
|
+
if not exist:
|
|
255
|
+
self._drivers_data.append(Driver(parsed_data.id))
|
|
256
|
+
|
|
257
|
+
for item in self._drivers_data:
|
|
258
|
+
if item.id == parsed_data.id:
|
|
259
|
+
if parsed_data.is_broadcast:
|
|
260
|
+
pass
|
|
261
|
+
else:
|
|
262
|
+
item[parsed_data.function][int(parsed_data.parsed_states[1])] = int(parsed_data.parsed_states[2])
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
except asyncio.TimeoutError:
|
|
266
|
+
_LOGGER.debug("Sending heartbeat to keep TCP connection alive...")
|
|
267
|
+
await self._connection.write("\n")
|
|
268
|
+
|
|
269
|
+
except asyncio.CancelledError:
|
|
270
|
+
_LOGGER.info("Stopping Transport loop...")
|
|
271
|
+
|
|
272
|
+
await self._connection.close()
|
|
273
|
+
|
|
274
|
+
break
|
|
275
|
+
|
|
276
|
+
except Exception as err:
|
|
277
|
+
_LOGGER.error("Connection ERROR(%s): %s. Next connection attempt in 3s", self._target, err)
|
|
278
|
+
|
|
279
|
+
await self._connection.close()
|
|
280
|
+
await asyncio.sleep(3)
|
|
281
|
+
|
|
282
|
+
async def set_led(
|
|
283
|
+
self,
|
|
284
|
+
id: int,
|
|
285
|
+
pin: int,
|
|
286
|
+
level: int,
|
|
287
|
+
) -> None:
|
|
288
|
+
attempts = 0
|
|
289
|
+
while attempts < 10:
|
|
290
|
+
await self.write(f"SetLED={id},{pin},{level}")
|
|
291
|
+
await self.write(f"StateLED={id},{pin}")
|
|
292
|
+
|
|
293
|
+
await asyncio.sleep(attempts * 0.1)
|
|
294
|
+
attempts += 1
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gryfsmartio
|
|
3
|
+
Version: 0.1
|
|
4
|
+
Summary: Library for the GryfSmart system
|
|
5
|
+
Home-page: https://github.com/karlowiczpl/gryfsmartio
|
|
6
|
+
Author: @karlowiczpl
|
|
7
|
+
Author-email: kkarlowicz@gryfsmart.pl
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENCE
|
|
14
|
+
Requires-Dist: pyserial-asyncio==0.6
|
|
15
|
+
Dynamic: license-file
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENCE
|
|
2
|
+
pyproject.toml
|
|
3
|
+
setup.cfg
|
|
4
|
+
gryfsmartio/__init__.py
|
|
5
|
+
gryfsmartio/parsing.py
|
|
6
|
+
gryfsmartio/transport.py
|
|
7
|
+
gryfsmartio.egg-info/PKG-INFO
|
|
8
|
+
gryfsmartio.egg-info/SOURCES.txt
|
|
9
|
+
gryfsmartio.egg-info/dependency_links.txt
|
|
10
|
+
gryfsmartio.egg-info/requires.txt
|
|
11
|
+
gryfsmartio.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pyserial-asyncio==0.6
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gryfsmartio
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[metadata]
|
|
2
|
+
name = gryfsmartio
|
|
3
|
+
version = 0.1
|
|
4
|
+
author = @karlowiczpl
|
|
5
|
+
author_email = kkarlowicz@gryfsmart.pl
|
|
6
|
+
description = Library for the GryfSmart system
|
|
7
|
+
long_description = file: README.md
|
|
8
|
+
long_description_content_type = text/markdown
|
|
9
|
+
url = https://github.com/karlowiczpl/gryfsmartio
|
|
10
|
+
license = MIT
|
|
11
|
+
classifiers =
|
|
12
|
+
Programming Language :: Python :: 3
|
|
13
|
+
Operating System :: OS Independent
|
|
14
|
+
|
|
15
|
+
[options]
|
|
16
|
+
packages = find:
|
|
17
|
+
python_requires = >=3.8
|
|
18
|
+
install_requires =
|
|
19
|
+
pyserial-asyncio==0.6
|
|
20
|
+
|
|
21
|
+
[egg_info]
|
|
22
|
+
tag_build =
|
|
23
|
+
tag_date = 0
|
|
24
|
+
|