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."""