pysaltsentry 0.1.0__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.
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pysaltsentry
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Async Python client for the Salt Sentry water softener sensor
|
|
5
|
+
Project-URL: Homepage, https://github.com/Lemcke-solutions/Salt-sentry-ha-integration
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# pysaltsentry
|
|
12
|
+
|
|
13
|
+
Async Python client for the [Salt Sentry](https://github.com/Lemcke-solutions/Salt-sentry-ha-integration) water softener sensor.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install pysaltsentry
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
import aiohttp
|
|
25
|
+
from pysaltsentry import SaltSentryDevice
|
|
26
|
+
|
|
27
|
+
async with aiohttp.ClientSession() as session:
|
|
28
|
+
device = SaltSentryDevice("192.168.1.100", session)
|
|
29
|
+
status = await device.get_status()
|
|
30
|
+
print(status.measurement_cm)
|
|
31
|
+
print(status.firmware_version)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## License
|
|
35
|
+
|
|
36
|
+
MIT
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# pysaltsentry
|
|
2
|
+
|
|
3
|
+
Async Python client for the [Salt Sentry](https://github.com/Lemcke-solutions/Salt-sentry-ha-integration) water softener sensor.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install pysaltsentry
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
import aiohttp
|
|
15
|
+
from pysaltsentry import SaltSentryDevice
|
|
16
|
+
|
|
17
|
+
async with aiohttp.ClientSession() as session:
|
|
18
|
+
device = SaltSentryDevice("192.168.1.100", session)
|
|
19
|
+
status = await device.get_status()
|
|
20
|
+
print(status.measurement_cm)
|
|
21
|
+
print(status.firmware_version)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## License
|
|
25
|
+
|
|
26
|
+
MIT
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pysaltsentry"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Async Python client for the Salt Sentry water softener sensor"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
dependencies = ["aiohttp>=3.9.0"]
|
|
13
|
+
|
|
14
|
+
[project.urls]
|
|
15
|
+
Homepage = "https://github.com/Lemcke-solutions/Salt-sentry-ha-integration"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from .device import SaltSentryDevice, SaltSentryStatus
|
|
2
|
+
from .exceptions import SaltSentryConnectionError, SaltSentryError, SaltSentryInvalidDataError
|
|
3
|
+
|
|
4
|
+
__all__ = [
|
|
5
|
+
"SaltSentryDevice",
|
|
6
|
+
"SaltSentryStatus",
|
|
7
|
+
"SaltSentryError",
|
|
8
|
+
"SaltSentryConnectionError",
|
|
9
|
+
"SaltSentryInvalidDataError",
|
|
10
|
+
]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
import aiohttp
|
|
6
|
+
|
|
7
|
+
from .exceptions import SaltSentryConnectionError, SaltSentryInvalidDataError
|
|
8
|
+
|
|
9
|
+
_TIMEOUT = aiohttp.ClientTimeout(total=10)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class SaltSentryStatus:
|
|
14
|
+
unique_id: str
|
|
15
|
+
measurement_cm: float
|
|
16
|
+
firmware_version: str
|
|
17
|
+
hardware_revision: str
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SaltSentryDevice:
|
|
21
|
+
def __init__(self, host: str, session: aiohttp.ClientSession) -> None:
|
|
22
|
+
self._host = host
|
|
23
|
+
self._session = session
|
|
24
|
+
|
|
25
|
+
async def get_status(self) -> SaltSentryStatus:
|
|
26
|
+
url = f"http://{self._host}/status"
|
|
27
|
+
try:
|
|
28
|
+
async with self._session.get(url, timeout=_TIMEOUT) as resp:
|
|
29
|
+
resp.raise_for_status()
|
|
30
|
+
data = await resp.json()
|
|
31
|
+
except aiohttp.ClientError as err:
|
|
32
|
+
raise SaltSentryConnectionError(f"Cannot connect to device at {self._host}") from err
|
|
33
|
+
|
|
34
|
+
if not data.get("valid", False):
|
|
35
|
+
raise SaltSentryInvalidDataError("Device returned an invalid measurement")
|
|
36
|
+
|
|
37
|
+
unique_id = data.get("unique_id")
|
|
38
|
+
if not unique_id:
|
|
39
|
+
raise SaltSentryInvalidDataError("Device did not return a unique_id")
|
|
40
|
+
|
|
41
|
+
distance_mm = data.get("distance_mm")
|
|
42
|
+
if distance_mm is None:
|
|
43
|
+
raise SaltSentryInvalidDataError("Device did not return a distance measurement")
|
|
44
|
+
|
|
45
|
+
return SaltSentryStatus(
|
|
46
|
+
unique_id=unique_id,
|
|
47
|
+
measurement_cm=distance_mm / 10,
|
|
48
|
+
firmware_version=data.get("firmware_version", "unknown"),
|
|
49
|
+
hardware_revision=data.get("hardware_revision", "A"),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
async def install_firmware(self, firmware_data: bytes) -> None:
|
|
53
|
+
url = f"http://{self._host}/update"
|
|
54
|
+
form = aiohttp.FormData()
|
|
55
|
+
form.add_field(
|
|
56
|
+
"firmware",
|
|
57
|
+
firmware_data,
|
|
58
|
+
filename="firmware.bin",
|
|
59
|
+
content_type="application/octet-stream",
|
|
60
|
+
)
|
|
61
|
+
try:
|
|
62
|
+
async with self._session.post(url, data=form, timeout=_TIMEOUT) as resp:
|
|
63
|
+
if resp.status != 200:
|
|
64
|
+
raise SaltSentryConnectionError(f"OTA update failed with status {resp.status}")
|
|
65
|
+
except aiohttp.ClientError as err:
|
|
66
|
+
raise SaltSentryConnectionError(f"Cannot connect to device at {self._host}") from err
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class SaltSentryError(Exception):
|
|
2
|
+
"""Base exception for Salt Sentry errors."""
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SaltSentryConnectionError(SaltSentryError):
|
|
6
|
+
"""Raised when the device cannot be reached."""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SaltSentryInvalidDataError(SaltSentryError):
|
|
10
|
+
"""Raised when the device returns unexpected or invalid data."""
|