ipee-lemon 0.1.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.
- ipee/lemon/__init__.py +0 -0
- ipee/lemon/ble/__init__.py +0 -0
- ipee/lemon/ble/device.py +62 -0
- ipee/lemon/ble/lemon_ble.py +20 -0
- ipee/lemon/ble/nus_service.py +30 -0
- ipee/lemon/ble/ota_service.py +24 -0
- ipee/lemon/ble/service.py +28 -0
- ipee/lemon/ble/wifi_provision_service.py +25 -0
- ipee/lemon/cli/__init__.py +0 -0
- ipee/lemon/cli/base_service.py +14 -0
- ipee/lemon/cli/cli_ble_service.py +18 -0
- ipee/lemon/cli/cli_wifi_service.py +19 -0
- ipee_lemon-0.1.0.dist-info/METADATA +17 -0
- ipee_lemon-0.1.0.dist-info/RECORD +16 -0
- ipee_lemon-0.1.0.dist-info/WHEEL +4 -0
- ipee_lemon-0.1.0.dist-info/licenses/LICENSE +19 -0
ipee/lemon/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
ipee/lemon/ble/device.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
3
|
+
from bleak import BleakClient
|
|
4
|
+
|
|
5
|
+
from .service import BLECharacteristic, BLEService, CharMode
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BleDevice:
|
|
9
|
+
"""Base class for all BLE devices.
|
|
10
|
+
|
|
11
|
+
For specific devices ble services and characteristics
|
|
12
|
+
can be registered in their respective class through
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
ble_service = SpecificBleService()
|
|
16
|
+
self.register_service(ble_service)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, address: str):
|
|
22
|
+
self.address = address
|
|
23
|
+
self.client = BleakClient(self.address)
|
|
24
|
+
self.services: Dict[str, BLEService] = {}
|
|
25
|
+
|
|
26
|
+
def register_service(self, service: BLEService):
|
|
27
|
+
self.services[service.uuid] = service
|
|
28
|
+
|
|
29
|
+
async def connect(self):
|
|
30
|
+
await self.client.connect()
|
|
31
|
+
# IMPORTANT: force service discovery
|
|
32
|
+
for service in self.client.services:
|
|
33
|
+
print(f"Service: {service.uuid}")
|
|
34
|
+
for char in service.characteristics:
|
|
35
|
+
print(f" Characteristic: {char.uuid}")
|
|
36
|
+
print("Connected: ", self.address)
|
|
37
|
+
|
|
38
|
+
for service in self.services.values():
|
|
39
|
+
for char in service.characteristics.values():
|
|
40
|
+
if char.modes & {CharMode.NOTIFY, CharMode.INDICATE} and char.on_data:
|
|
41
|
+
await self.client.start_notify(char.uuid, self._make_data_handler(char))
|
|
42
|
+
|
|
43
|
+
async def disconnect(self):
|
|
44
|
+
await self.client.disconnect()
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def _make_data_handler(char: BLECharacteristic):
|
|
48
|
+
def handler(_, data: bytearray):
|
|
49
|
+
if char.on_data:
|
|
50
|
+
char.on_data(bytes(data))
|
|
51
|
+
|
|
52
|
+
return handler
|
|
53
|
+
|
|
54
|
+
# ---------------------------
|
|
55
|
+
# Read / Write API
|
|
56
|
+
# ---------------------------
|
|
57
|
+
|
|
58
|
+
async def read(self, char_uuid: str) -> bytes:
|
|
59
|
+
return await self.client.read_gatt_char(char_uuid)
|
|
60
|
+
|
|
61
|
+
async def write(self, char_uuid: str, data: bytes, response: bool = False):
|
|
62
|
+
await self.client.write_gatt_char(char_uuid, data, response)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from .device import BleDevice
|
|
2
|
+
from .nus_service import NUSService
|
|
3
|
+
from .ota_service import OtaService
|
|
4
|
+
from .wifi_provision_service import WifiProvisioningService
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class LemonBleDevice(BleDevice):
|
|
8
|
+
def __init__(self, address: str):
|
|
9
|
+
super().__init__(address)
|
|
10
|
+
|
|
11
|
+
nus = NUSService(on_rx=self.on_nus_data)
|
|
12
|
+
self.register_service(nus)
|
|
13
|
+
ota = OtaService()
|
|
14
|
+
self.register_service(ota)
|
|
15
|
+
wifi = WifiProvisioningService()
|
|
16
|
+
self.register_service(wifi)
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def on_nus_data(data: bytearray):
|
|
20
|
+
print("NUS RX:", data.decode(errors="ignore"))
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from .device import BLECharacteristic, BLEService, CharMode
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class NUSService(BLEService):
|
|
5
|
+
"""The NUS service is a BLEService that can be used for testing if the connection works
|
|
6
|
+
|
|
7
|
+
it has an rx and tx characteristic that prints data on the command line.
|
|
8
|
+
From the lemon perspective, you can send data to ble nodes by writing `ble send <some-text>`
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
RX_UUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
|
|
12
|
+
TX_UUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
|
|
13
|
+
|
|
14
|
+
def __init__(self, on_rx=None):
|
|
15
|
+
super().__init__(uuid="6E400001-B5A3-F393-E0A9-E50E24DCCA9E", name="Nordic UART Service")
|
|
16
|
+
|
|
17
|
+
self.add_characteristic(
|
|
18
|
+
BLECharacteristic(
|
|
19
|
+
uuid=self.TX_UUID,
|
|
20
|
+
modes={CharMode.NOTIFY},
|
|
21
|
+
description="Device → Host",
|
|
22
|
+
on_data=on_rx,
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
self.add_characteristic(
|
|
27
|
+
BLECharacteristic(
|
|
28
|
+
uuid=self.RX_UUID, modes={CharMode.WRITE}, description="Host → Device"
|
|
29
|
+
)
|
|
30
|
+
)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from .service import BLECharacteristic, BLEService, CharMode
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class OtaService(BLEService):
|
|
5
|
+
"""The OTA service is used for setting the OTA URL and start updating
|
|
6
|
+
|
|
7
|
+
Make sure the device is connected through Wi-Fi before starting the update.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
OTA_URL_UUID = "6E400008-B5A3-F393-E0A9-E50E24DCCA9E"
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
super().__init__(
|
|
14
|
+
uuid="6E400009-B5A3-F393-E0A9-E50E24DCCA9E",
|
|
15
|
+
name="OTA Service",
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
self.add_characteristic(
|
|
19
|
+
BLECharacteristic(
|
|
20
|
+
uuid=self.OTA_URL_UUID,
|
|
21
|
+
modes={CharMode.WRITE},
|
|
22
|
+
description="Set the OTA URL and start updating",
|
|
23
|
+
)
|
|
24
|
+
)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import Callable, Dict, Optional, Set
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CharMode(Enum):
|
|
7
|
+
READ = "read"
|
|
8
|
+
WRITE = "write"
|
|
9
|
+
NOTIFY = "notify"
|
|
10
|
+
INDICATE = "indicate"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class BLECharacteristic:
|
|
15
|
+
uuid: str
|
|
16
|
+
modes: Set[CharMode]
|
|
17
|
+
description: str = ""
|
|
18
|
+
on_data: Optional[Callable[[bytes], None]] = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class BLEService:
|
|
23
|
+
uuid: str
|
|
24
|
+
name: str
|
|
25
|
+
characteristics: Dict[str, BLECharacteristic] = field(default_factory=dict)
|
|
26
|
+
|
|
27
|
+
def add_characteristic(self, characteristic: BLECharacteristic):
|
|
28
|
+
self.characteristics[characteristic.uuid] = characteristic
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .service import BLECharacteristic, BLEService, CharMode
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class WifiProvisioningService(BLEService):
|
|
5
|
+
"""The Wi-Fi Provisioning Service is used for setting the SSID and PW of the AP to connect to.
|
|
6
|
+
|
|
7
|
+
Once the SSID and Password are written, the device shall attempt connection.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
SSID_UUID = "6E400005-B5A3-F393-E0A9-E50E24DCCA9E"
|
|
11
|
+
PASSWORD_UUID = "6E400006-B5A3-F393-E0A9-E50E24DCCA9E" # NOSONAR - not a password field
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
super().__init__(
|
|
15
|
+
uuid="6E400004-B5A3-F393-E0A9-E50E24DCCA9E",
|
|
16
|
+
name="Wifi provisioning service",
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
self.add_characteristic(
|
|
20
|
+
BLECharacteristic(
|
|
21
|
+
uuid=self.SSID_UUID,
|
|
22
|
+
modes={CharMode.NOTIFY, CharMode.READ, CharMode.WRITE},
|
|
23
|
+
description="The SSID to connect to",
|
|
24
|
+
)
|
|
25
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from serial import Serial
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class CliBaseService:
|
|
5
|
+
def __init__(self, tag: str, service_name: str):
|
|
6
|
+
self.tag = tag
|
|
7
|
+
self.service_name = service_name
|
|
8
|
+
|
|
9
|
+
def write(self, serial: Serial, command: str):
|
|
10
|
+
serial.write((self.tag + " " + command + "\n").encode())
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def read(self, serial: Serial):
|
|
14
|
+
return serial.readline()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from serial import Serial
|
|
2
|
+
|
|
3
|
+
from .base_service import CliBaseService
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CliBleService(CliBaseService):
|
|
7
|
+
def __init__(self):
|
|
8
|
+
super().__init__("ble", "ble service")
|
|
9
|
+
|
|
10
|
+
def enable(self, serial: Serial):
|
|
11
|
+
self.write(serial, "enable")
|
|
12
|
+
|
|
13
|
+
def disable(self, serial: Serial):
|
|
14
|
+
self.write(serial, "disable")
|
|
15
|
+
|
|
16
|
+
def send(self, serial: Serial, data: str):
|
|
17
|
+
command = "send " + data
|
|
18
|
+
self.write(serial, command)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from serial import Serial
|
|
2
|
+
|
|
3
|
+
from .base_service import CliBaseService
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CliWifiService(CliBaseService):
|
|
7
|
+
def __init__(self):
|
|
8
|
+
super().__init__("wifi", "wifi-service")
|
|
9
|
+
|
|
10
|
+
def set_ssid(self, serial: Serial, ssid: str):
|
|
11
|
+
command = "set_ssid " + ssid
|
|
12
|
+
self.write(serial, command)
|
|
13
|
+
|
|
14
|
+
def set_password(self, serial: Serial, password: str):
|
|
15
|
+
command = "set_password " + password
|
|
16
|
+
self.write(serial, command)
|
|
17
|
+
|
|
18
|
+
def connect(self, serial: Serial):
|
|
19
|
+
self.write(serial, "connect")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ipee-lemon
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary:
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Author: Jeroen Van den Broeck
|
|
7
|
+
Author-email: jeroen.vandenbroeck@ipee.eu
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
|
+
Requires-Dist: bleak (>=3.0.0,<4.0.0)
|
|
14
|
+
Requires-Dist: pyserial (>=3.5,<4.0)
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
ipee/lemon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
ipee/lemon/ble/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
ipee/lemon/ble/device.py,sha256=NT0krhsUwZKmG6RgPATuTIjhKMdPQV7NJigAWfg1ThA,1949
|
|
4
|
+
ipee/lemon/ble/lemon_ble.py,sha256=OY0kAgD0J_wk9OaylUNZzenJz7U_6WnPI92P5cILw7I,605
|
|
5
|
+
ipee/lemon/ble/nus_service.py,sha256=m6PRoKHh49iB2CZBFvS2OD3Knrfyt8Rs85ZA_RIzBig,1030
|
|
6
|
+
ipee/lemon/ble/ota_service.py,sha256=MaIbkAbT00Bhe6qTiOGH9CPtSbhYmTknCJCgjWr3LKg,700
|
|
7
|
+
ipee/lemon/ble/service.py,sha256=M7eKVepA_8wpi_tKpmlau5W39FJW_RizwRvVdfrFjzc,655
|
|
8
|
+
ipee/lemon/ble/wifi_provision_service.py,sha256=IsPzA85S5l4icJPpKLbi1HE0duATStUPD3tN7Zz26dc,860
|
|
9
|
+
ipee/lemon/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
ipee/lemon/cli/base_service.py,sha256=PpJr56pc6pX3b62nDQ2iIg2FT1ltdkiLoxf_R7axfPU,372
|
|
11
|
+
ipee/lemon/cli/cli_ble_service.py,sha256=wLbleMRtkdOsxUzS6QzL2jOi0CEDN5SZZqiCrLjYh1A,449
|
|
12
|
+
ipee/lemon/cli/cli_wifi_service.py,sha256=D49zT_Es62Rae5Zj9qk9F7PFV2YCvby5kZ3BKAAVFZs,525
|
|
13
|
+
ipee_lemon-0.1.0.dist-info/METADATA,sha256=Hktsz6aEFs4rWOBOSJuINJiKyT9gAjg03xUWddJHeyI,500
|
|
14
|
+
ipee_lemon-0.1.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
15
|
+
ipee_lemon-0.1.0.dist-info/licenses/LICENSE,sha256=qA9L0aqMG2k42p6wETagTQehNeFSQ4llTn0ogPyb60w,1051
|
|
16
|
+
ipee_lemon-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2026 IPEE. All rights reserved.
|
|
2
|
+
|
|
3
|
+
This software and associated documentation files (the "Software") are the
|
|
4
|
+
proprietary and confidential property of IPEE. Unauthorized copying, transfer,
|
|
5
|
+
or reproduction of the Software, via any medium, is strictly prohibited.
|
|
6
|
+
|
|
7
|
+
The Software is provided under a commercial license and may only be used in
|
|
8
|
+
accordance with the terms and conditions agreed upon between IPEE and the
|
|
9
|
+
licensee. No part of the Software may be modified, distributed, sublicensed,
|
|
10
|
+
or otherwise made available to third parties without the prior written consent
|
|
11
|
+
of IPEE.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|