easyhttp-python 0.3.2__tar.gz → 0.4.0a6__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easyhttp-python
3
- Version: 0.3.2
3
+ Version: 0.4.0a6
4
4
  Summary: Simple HTTP-based P2P framework for IoT
5
5
  Author-email: slpuk <yarik6052@gmail.com>
6
6
  License: MIT
@@ -9,7 +9,7 @@ Project-URL: Documentation, https://github.com/slpuk/easyhttp-python#readme
9
9
  Project-URL: Repository, https://github.com/slpuk/easyhttp-python
10
10
  Project-URL: Issue Tracker, https://github.com/slpuk/easyhttp-python/issues
11
11
  Keywords: iot,p2p,http,framework
12
- Classifier: Development Status :: 4 - Beta
12
+ Classifier: Development Status :: 3 - Alpha
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
15
  Classifier: Topic :: Communications
@@ -27,6 +27,7 @@ License-File: LICENSE
27
27
  Requires-Dist: fastapi>=0.103.2
28
28
  Requires-Dist: uvicorn[standard]>=0.22.0
29
29
  Requires-Dist: aiohttp>=3.7.0
30
+ Requires-Dist: loggity>=0.5.0a6
30
31
  Provides-Extra: dev
31
32
  Requires-Dist: pytest>=6.0; extra == "dev"
32
33
  Requires-Dist: black; extra == "dev"
@@ -36,15 +37,15 @@ Dynamic: license-file
36
37
  # EasyHTTP
37
38
 
38
39
  [![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/slpuk/easyhttp-python)
39
- ![Protocol Version](https://img.shields.io/badge/version-0.3.2-blue?style=for-the-badge)
40
- ![Development Status](https://img.shields.io/badge/status-beta-orange?style=for-the-badge)
40
+ ![Protocol Version](https://img.shields.io/badge/version-0.4.0-blue?style=for-the-badge)
41
+ ![Development Status](https://img.shields.io/badge/status-alpha-red?style=for-the-badge)
41
42
  ![License](https://img.shields.io/badge/license-MIT-green?style=for-the-badge)
42
43
  ![Python](https://img.shields.io/badge/python-3.7+-blue?style=for-the-badge&logo=python&logoColor=white)
43
44
 
44
45
  > **A lightweight HTTP-based P2P framework for IoT and device-to-device communication**
45
46
 
46
47
  ## 🛠️ Changelog
47
- - Added context managers support
48
+ - Added UDP multicast discovery
48
49
  - Fixed some bugs
49
50
 
50
51
  ## 📖 About
@@ -58,7 +59,7 @@ Dynamic: license-file
58
59
  - **🆔 Human-Readable Device IDs** - Base32 identifiers instead of IP addresses
59
60
  - **✅ Easy to Use** - Simple API with minimal setup
60
61
  - **🚀 Performance** - Asynchronous code and lightweight libraries(FastAPI/aiohttp)
61
-
62
+ - **⚙️ Auto-detect** - Devices automatically find each other
62
63
 
63
64
  ## 🏗️ Architecture
64
65
 
@@ -86,7 +87,7 @@ EasyHTTP uses a simple JSON-based command system:
86
87
  ### Basic Example with Callbacks (synchronous)
87
88
  ```python
88
89
  import time
89
- from easyhttp import EasyHTTP
90
+ from easyhttp_python import EasyHTTP
90
91
 
91
92
  # Callback function
92
93
  def handle_data(sender_id, data, timestamp):
@@ -127,10 +128,6 @@ def main():
127
128
  easy.start() # Starting server
128
129
  print(f"Device {easy.id} is running on port 5000!")
129
130
 
130
- # Adding device
131
- easy.add("ABC123", "192.168.1.100", 5000)
132
- print("Added device ABC123")
133
-
134
131
  # Monitoring device's status
135
132
  try:
136
133
  while True:
@@ -149,4 +146,12 @@ def main():
149
146
  if __name__ == "__main__":
150
147
  main()
151
148
  ```
149
+ ## 📦 Version History
150
+
151
+ | Version | Date | Changes |
152
+ |---------|------|---------|
153
+ | 0.4.0 | 2026-11-03 | UDP Discovery, auto-detect |
154
+ | 0.3.3 | 2026-03-01 | Fixed imports, renamed to easyhttp_python |
155
+ | 0.3.2 | 2026-02-14 | Context managers |
156
+
152
157
  **More examples available on [GitHub](https://github.com/slpuk/easyhttp-python)**
@@ -2,57 +2,32 @@
2
2
  [EN README](README.md) | [RU README](README_RU.md)
3
3
  > **A lightweight HTTP-based P2P framework for IoT and device-to-device communication**
4
4
 
5
- ![Protocol Version](https://img.shields.io/badge/version-0.3.2-blue?style=for-the-badge)
6
- ![Development Status](https://img.shields.io/badge/status-beta-orange?style=for-the-badge)
5
+ ![Protocol Version](https://img.shields.io/badge/version-0.4.0-blue?style=for-the-badge)
6
+ ![Development Status](https://img.shields.io/badge/status-alpha-red?style=for-the-badge)
7
7
  ![License](https://img.shields.io/badge/license-MIT-green?style=for-the-badge)
8
8
  ![Python](https://img.shields.io/badge/python-3.7+-blue?style=for-the-badge&logo=python&logoColor=white)
9
9
 
10
10
  > [!WARNING]
11
- > **Breaking Changes from 0.2.0**
11
+ > **Breaking Changes from 0.3.2**
12
12
  >
13
13
  > ### API Changes
14
- > | 0.2.0 | 0.3.2 | Notes |
15
- > |--------|--------|-------|
16
- > | `get()` | `fetch()` | Same functionality |
17
- > | `pull()` | `push()` | Same functionality |
18
- > | `'on_get'` | `'on_fetch'` | Callback name |
19
- > | `'on_data_response'` | `'on_data'` | Callback name |
20
- > | `'on_pull'` | `'on_push'` | Callback name |
21
- >
22
- > ### Migration Example
23
14
  > ```python
24
- > # 0.2.0 (OLD):
25
- > easy = EasyHTTP()
26
- > easy.get("ABC123")
27
- > easy.pull("ABC123", data)
28
- >
29
- > # 0.3.2 (NEW):
30
- > # Sync
31
- > easy = EasyHTTP()
32
- > easy.fetch("ABC123")
33
- > easy.push("ABC123", data)
15
+ > # 0.3.2 (OLD)
16
+ > from easyhttp import ...
34
17
  >
35
- > # Async
36
- > easy = EasyHTTPAsync() # Async!
37
- > await easy.fetch("ABC123") # Await!
38
- > await easy.push("ABC123", data)
39
- > ```
40
-
18
+ > # 0.3.3 - newer (NEW)
19
+ > from easyhttp_python import ...
20
+ >```
41
21
 
42
22
  ## 🚀 Quick Start
43
23
 
44
24
  ### Installation
45
- > [!NOTE]
46
- > Both methods require **Git** to be installed.<br>
47
- > PyPI upload is **preparing**
48
25
 
49
26
  ```bash
50
- # Clone and install
51
- git clone https://github.com/slpuk/easyhttp-python.git
52
- cd easyhttp-python
53
- pip install -e .
27
+ # Install by PyPI
28
+ pip install easyhttp-python
54
29
 
55
- # Or directly from GitHub
30
+ # Or from GitHub
56
31
  pip install git+https://github.com/slpuk/easyhttp-python.git
57
32
  ```
58
33
 
@@ -60,16 +35,13 @@ pip install git+https://github.com/slpuk/easyhttp-python.git
60
35
  Syntax with context managers and full code is supported
61
36
 
62
37
  ```python
63
- from easyhttp import EasyHTTP
38
+ from easyhttp_python import EasyHTTP
64
39
 
65
40
  def main():
66
41
  # Initialize a device with context manager
67
42
  with EasyHTTP(debug=True, port=5000) as easy:
68
43
  print(f"Device ID: {easy.id}")
69
44
 
70
- # Manually add another device
71
- easy.add("ABC123", "192.168.1.100", 5000)
72
-
73
45
  # Ping to check if device is online
74
46
  if easy.ping("ABC123"):
75
47
  print("Device is online!")
@@ -93,7 +65,7 @@ if __name__ == "__main__":
93
65
 
94
66
  ```python
95
67
  import asyncio
96
- from easyhttp import EasyHTTPAsync
68
+ from easyhttp_python import EasyHTTPAsync
97
69
 
98
70
  async def main():
99
71
  # Initialize a device
@@ -102,9 +74,6 @@ async def main():
102
74
 
103
75
  print(f"Device ID: {easy.id}")
104
76
 
105
- # Manually add another device
106
- easy.add("ABC123", "192.168.1.100", 5000)
107
-
108
77
  # Ping to check if device is online
109
78
  if await easy.ping("ABC123"):
110
79
  print("Device is online!")
@@ -136,6 +105,7 @@ if __name__ == "__main__":
136
105
  - **🆔 Human-Readable Device IDs** - Base32 identifiers instead of IP addresses
137
106
  - **✅ Easy to Use** - Simple API with minimal setup
138
107
  - **🚀 Performance** - Asynchronous code and lightweight libraries(FastAPI/aiohttp)
108
+ - **⚙️ Auto-detect** - Devices automatically find each other
139
109
 
140
110
  ## Project Structure
141
111
  ```
@@ -143,10 +113,11 @@ easyhttp-python/
143
113
  ├── docs/
144
114
  │ ├── EasyHTTP.md # Sync API reference
145
115
  │ └── EasyHTTPAsync.md # Async API reference
146
- ├── easyhttp/
116
+ ├── easyhttp_python/
147
117
  │ ├── __init__.py
148
- │ ├── core.py # Main framework file/core
149
- └── wrapper.py # Synchronous wrapper
118
+ │ ├── core.py # Main framework file/core
119
+ ├── discovery.py # Discovery module
120
+ │ └── wrapper.py # Synchronous wrapper
150
121
  ├── examples/
151
122
  │ ├── async/ # Asynchronous examples
152
123
  │ │ ├── basic_ping.py
@@ -163,6 +134,7 @@ easyhttp-python/
163
134
  ├── .gitignore
164
135
  ├── LICENSE # MIT license
165
136
  ├── pyproject.toml # Project config
137
+ ├── README_PY.md # Documentation for PyPI
166
138
  ├── README_RU.md # Russian documentation
167
139
  ├── README.md # This file
168
140
  └── requirements.txt # Project dependencies
@@ -209,25 +181,20 @@ sequenceDiagram
209
181
 
210
182
  ## 📦 Installation & Setup
211
183
 
212
- ### Python Installation
213
- > [!NOTE]
214
- > Both methods require **Git** to be installed.<br>
215
- > PyPI upload is **preparing**
184
+ ### Installation
216
185
 
217
186
  ```bash
218
- # Install directly from GitHub
219
- pip install git+https://github.com/slpuk/easyhttp-python.git
187
+ # Install from PyPI
188
+ pip install easyhttp-python
220
189
 
221
- # Or install from source
222
- git clone https://github.com/slpuk/easyhttp-python
223
- cd easyhttp-python
224
- pip install -e .
190
+ # Or from GitHub
191
+ pip install git+https://github.com/slpuk/easyhttp-python.git
225
192
  ```
226
193
 
227
194
  ### Basic Example with Callbacks(Synchronous)
228
195
  ```python
229
196
  import time
230
- from easyhttp import EasyHTTP
197
+ from easyhttp_python import EasyHTTP
231
198
 
232
199
  # Callback function
233
200
  def handle_data(sender_id, data, timestamp):
@@ -1,15 +1,15 @@
1
1
  # EasyHTTP
2
2
 
3
3
  [![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/slpuk/easyhttp-python)
4
- ![Protocol Version](https://img.shields.io/badge/version-0.3.2-blue?style=for-the-badge)
5
- ![Development Status](https://img.shields.io/badge/status-beta-orange?style=for-the-badge)
4
+ ![Protocol Version](https://img.shields.io/badge/version-0.4.0-blue?style=for-the-badge)
5
+ ![Development Status](https://img.shields.io/badge/status-alpha-red?style=for-the-badge)
6
6
  ![License](https://img.shields.io/badge/license-MIT-green?style=for-the-badge)
7
7
  ![Python](https://img.shields.io/badge/python-3.7+-blue?style=for-the-badge&logo=python&logoColor=white)
8
8
 
9
9
  > **A lightweight HTTP-based P2P framework for IoT and device-to-device communication**
10
10
 
11
11
  ## 🛠️ Changelog
12
- - Added context managers support
12
+ - Added UDP multicast discovery
13
13
  - Fixed some bugs
14
14
 
15
15
  ## 📖 About
@@ -23,7 +23,7 @@
23
23
  - **🆔 Human-Readable Device IDs** - Base32 identifiers instead of IP addresses
24
24
  - **✅ Easy to Use** - Simple API with minimal setup
25
25
  - **🚀 Performance** - Asynchronous code and lightweight libraries(FastAPI/aiohttp)
26
-
26
+ - **⚙️ Auto-detect** - Devices automatically find each other
27
27
 
28
28
  ## 🏗️ Architecture
29
29
 
@@ -51,7 +51,7 @@ EasyHTTP uses a simple JSON-based command system:
51
51
  ### Basic Example with Callbacks (synchronous)
52
52
  ```python
53
53
  import time
54
- from easyhttp import EasyHTTP
54
+ from easyhttp_python import EasyHTTP
55
55
 
56
56
  # Callback function
57
57
  def handle_data(sender_id, data, timestamp):
@@ -92,10 +92,6 @@ def main():
92
92
  easy.start() # Starting server
93
93
  print(f"Device {easy.id} is running on port 5000!")
94
94
 
95
- # Adding device
96
- easy.add("ABC123", "192.168.1.100", 5000)
97
- print("Added device ABC123")
98
-
99
95
  # Monitoring device's status
100
96
  try:
101
97
  while True:
@@ -114,4 +110,12 @@ def main():
114
110
  if __name__ == "__main__":
115
111
  main()
116
112
  ```
113
+ ## 📦 Version History
114
+
115
+ | Version | Date | Changes |
116
+ |---------|------|---------|
117
+ | 0.4.0 | 2026-11-03 | UDP Discovery, auto-detect |
118
+ | 0.3.3 | 2026-03-01 | Fixed imports, renamed to easyhttp_python |
119
+ | 0.3.2 | 2026-02-14 | Context managers |
120
+
117
121
  **More examples available on [GitHub](https://github.com/slpuk/easyhttp-python)**
@@ -0,0 +1,6 @@
1
+ from .core import EasyHTTPAsync
2
+ from .wrapper import EasyHTTP
3
+
4
+ __version__ = EasyHTTPAsync.__version__
5
+ __author__ = "slpuk"
6
+ __all__ = ["EasyHTTPAsync", "EasyHTTP"]
@@ -0,0 +1,145 @@
1
+ """UDP multicast discovery module for EasyHTTP."""
2
+
3
+ import asyncio
4
+ import socket
5
+ import struct
6
+ import json
7
+ from typing import TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ from .core import EasyHTTPAsync
11
+
12
+ from loggity import Logger, Colors, LoggerConfig
13
+ log_config = LoggerConfig(
14
+ colored = True,
15
+ timestamps = False,
16
+ timeformat = None,
17
+ file = None
18
+ )
19
+ log = Logger(config = log_config)
20
+
21
+ class Discovery:
22
+ """Manages UDP multicast discovery for EasyHTTP devices."""
23
+
24
+ def __init__(
25
+ self,
26
+ parent: "EasyHTTPAsync",
27
+ multicast_group: str = "224.0.0.106",
28
+ multicast_port: int = 37020,
29
+ ):
30
+ self.parent = parent
31
+ self.version = self.parent.__version__
32
+ self.multicast_group = multicast_group
33
+ self.multicast_port = multicast_port
34
+ self.discovery_task: asyncio.Task | None = None
35
+ self.enabled = False
36
+
37
+ async def start(self):
38
+ """Start discovery listener and broadcaster."""
39
+ self.enabled = True
40
+ self.discovery_task = asyncio.create_task(self._discovery_loop())
41
+
42
+ async def stop(self):
43
+ """Stop discovery tasks."""
44
+ if self.discovery_task:
45
+ self.discovery_task.cancel()
46
+ try:
47
+ await self.discovery_task
48
+ except asyncio.CancelledError:
49
+ pass
50
+ self.discovery_task = None
51
+
52
+ async def _discovery_loop(self):
53
+ """Run listener and broadcaster concurrently."""
54
+ listener = asyncio.create_task(self._listen_multicast())
55
+ broadcaster = asyncio.create_task(self._broadcast_presence())
56
+ await asyncio.gather(listener, broadcaster)
57
+
58
+ async def _listen_multicast(self):
59
+ """Listen for DISCOVERY messages from other devices."""
60
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
61
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
62
+ sock.bind(("", self.multicast_port))
63
+
64
+ mreq = struct.pack(
65
+ "4sl", socket.inet_aton(self.multicast_group), socket.INADDR_ANY
66
+ )
67
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
68
+ sock.setblocking(False)
69
+
70
+ loop = asyncio.get_event_loop()
71
+ while True:
72
+ try:
73
+ data, addr = await loop.sock_recvfrom(sock, 1024)
74
+ await self._handle_discovery_message(data, addr)
75
+ except asyncio.CancelledError:
76
+ break
77
+ except Exception as e:
78
+ if self.parent.debug:
79
+ log.custom("DISCOVERY", Colors.RED, e)
80
+
81
+ async def _broadcast_presence(self):
82
+ """Periodically broadcast DISCOVERY messages."""
83
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
84
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
85
+
86
+ while True:
87
+ try:
88
+ packet = {
89
+ "version": self.version,
90
+ "type": self.parent.commands.DISCOVERY.value,
91
+ "id": self.parent.id,
92
+ "port": self.parent.port,
93
+ }
94
+ sock.sendto(
95
+ json.dumps(packet).encode(),
96
+ (self.multicast_group, self.multicast_port),
97
+ )
98
+ await asyncio.sleep(30)
99
+ except asyncio.CancelledError:
100
+ break
101
+ except Exception as e:
102
+ if self.parent.debug:
103
+ log.custom("DISCOVERY", Colors.RED, e)
104
+
105
+ async def _handle_discovery_message(self, data: bytes, addr: tuple):
106
+ """Process incoming discovery messages."""
107
+ try:
108
+ message = json.loads(data.decode())
109
+ cmd_type = message.get("type")
110
+
111
+ # Received DISCOVERY -> send DISCOVERY_ACK
112
+ if cmd_type == self.parent.commands.DISCOVERY.value:
113
+ device_id = message.get("id")
114
+ device_port = message.get("port")
115
+
116
+ if device_id and device_id != self.parent.id:
117
+ ack_packet = {
118
+ "version": self.version,
119
+ "type": self.parent.commands.DISCOVERY_ACK.value,
120
+ "id": self.parent.id,
121
+ "port": self.parent.port,
122
+ }
123
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
124
+ sock.sendto(
125
+ json.dumps(ack_packet).encode(),
126
+ (addr[0], self.multicast_port),
127
+ )
128
+ if self.parent.debug:
129
+ log.custom("DISCOVERY", Colors.GREEN, f"Responded to {device_id} at {addr[0]}")
130
+
131
+ # Received DISCOVERY_ACK -> add device
132
+ elif cmd_type == self.parent.commands.DISCOVERY_ACK.value:
133
+ device_id = message.get("id")
134
+ device_port = message.get("port")
135
+
136
+ if device_id and device_id != self.parent.id:
137
+ if device_id not in self.parent.devices:
138
+ self.parent.add(device_id, addr[0], device_port)
139
+ if self.parent.debug:
140
+ log.custom("DISCOVERY", Colors.GREEN, f"Found device {device_id} at {addr[0]}")
141
+ asyncio.create_task(self.parent.ping(device_id))
142
+
143
+ except Exception as e:
144
+ if self.parent.debug:
145
+ log.custom("DISCOVERY", Colors.RED, e)