bwt-api 1.0.2__tar.gz → 1.0.3__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.
- {bwt_api-1.0.2 → bwt_api-1.0.3}/PKG-INFO +1 -1
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api/data.py +79 -5
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api/smart_dos_api.py +37 -10
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api.egg-info/PKG-INFO +1 -1
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api.egg-info/SOURCES.txt +14 -4
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api.egg-info/scm_file_list.json +30 -21
- bwt_api-1.0.3/src/bwt_api.egg-info/scm_version.json +8 -0
- bwt_api-1.0.3/tests/data/perla/GetCurrentData.json +33 -0
- bwt_api-1.0.3/tests/data/perla/GetDailyData.json +50 -0
- bwt_api-1.0.3/tests/data/perla/GetMonthlyData.json +33 -0
- bwt_api-1.0.3/tests/data/perla/GetYearlyData.json +14 -0
- bwt_api-1.0.3/tests/data/silk/registers.json +3 -0
- bwt_api-1.0.3/tests/data/smartdos/gatt_0104.json +13 -0
- bwt_api-1.0.3/tests/data/smartdos/gatt_0201.json +17 -0
- bwt_api-1.0.3/tests/data/smartdos/gatt_0202.json +24 -0
- bwt_api-1.0.3/tests/data/smartdos/gatt_0208.json +4 -0
- bwt_api-1.0.3/tests/data/smartdos/gatt_0401.json +8 -0
- bwt_api-1.0.3/tests/data/smartdos/gatt_0402.json +8 -0
- bwt_api-1.0.3/tests/data/smartdos/gatt_0503.json +12 -0
- bwt_api-1.0.3/tests/data/smartdos/gatt_0505.json +3 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/tests/test_api.py +101 -5
- bwt_api-1.0.2/AUTHORS.rst +0 -5
- bwt_api-1.0.2/CHANGELOG.rst +0 -8
- bwt_api-1.0.2/CONTRIBUTING.rst +0 -278
- bwt_api-1.0.2/src/bwt_api.egg-info/scm_version.json +0 -8
- {bwt_api-1.0.2 → bwt_api-1.0.3}/.coveragerc +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/.devcontainer/devcontainer.json +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/.github/workflows/python-package.yml +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/.github/workflows/python-publish.yml +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/.gitignore +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/LICENSE.txt +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/README.rst +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/pyproject.toml +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/setup.cfg +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/setup.py +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api/__init__.py +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api/api.py +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api/bwt.py +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api/bwt_api.py +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api/error.py +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api/exception.py +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api/silk_api.py +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api/skeleton.py +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api.egg-info/dependency_links.txt +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api.egg-info/entry_points.txt +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api.egg-info/not-zip-safe +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api.egg-info/requires.txt +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/src/bwt_api.egg-info/top_level.txt +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/tests/conftest.py +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/tests/test_distinguish.py +0 -0
- {bwt_api-1.0.2 → bwt_api-1.0.3}/tox.ini +0 -0
|
@@ -33,6 +33,56 @@ class SmartDosStatus(enum.IntEnum):
|
|
|
33
33
|
def _missing_(cls, value):
|
|
34
34
|
return None
|
|
35
35
|
|
|
36
|
+
@classmethod
|
|
37
|
+
def normal(cls) -> list["SmartDosStatus"]:
|
|
38
|
+
"""Return SmartDosStatus values considered normal/OK."""
|
|
39
|
+
return [
|
|
40
|
+
cls.STANDBY,
|
|
41
|
+
cls.METERING_ACTIVE,
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def warning(cls) -> list["SmartDosStatus"]:
|
|
46
|
+
"""Return SmartDosStatus values that indicate warnings."""
|
|
47
|
+
return [
|
|
48
|
+
cls.MINERAL_CONTAINER_LEVEL_LOW,
|
|
49
|
+
cls.SMART_MINERAL_RUNNING_LOW,
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def error(cls) -> list["SmartDosStatus"]:
|
|
54
|
+
"""Return SmartDosStatus values that indicate errors."""
|
|
55
|
+
return [
|
|
56
|
+
cls.MINERAL_CONTAINER_EMPTY,
|
|
57
|
+
cls.PUMP_FAULT,
|
|
58
|
+
cls.PUMP_POWER_FAULT,
|
|
59
|
+
cls.SMART_MINERAL_RUN_OUT,
|
|
60
|
+
cls.AQA_VOLUME_ALARM,
|
|
61
|
+
cls.AQA_WATCH_ALARM,
|
|
62
|
+
cls.AQA_MAXFLOW_ALARM,
|
|
63
|
+
cls.PUMP_CONTROL_FAULT,
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def category(cls, value) -> BwtStatus | None:
|
|
68
|
+
"""Return the `BwtStatus` category for a SmartDosStatus value.
|
|
69
|
+
|
|
70
|
+
Accepts either an int or a SmartDosStatus member. Returns `BwtStatus.OK`,
|
|
71
|
+
`BwtStatus.WARNING`, or `BwtStatus.ERROR`, or `None` for unknown values.
|
|
72
|
+
"""
|
|
73
|
+
try:
|
|
74
|
+
status = cls(value)
|
|
75
|
+
except Exception:
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
if status in cls.normal():
|
|
79
|
+
return BwtStatus.OK
|
|
80
|
+
if status in cls.warning():
|
|
81
|
+
return BwtStatus.WARNING
|
|
82
|
+
if status in cls.error():
|
|
83
|
+
return BwtStatus.ERROR
|
|
84
|
+
return None
|
|
85
|
+
|
|
36
86
|
|
|
37
87
|
class SubstanceType(enum.IntEnum):
|
|
38
88
|
L1_LE = 1
|
|
@@ -68,7 +118,7 @@ class CurrentResponse:
|
|
|
68
118
|
holiday_mode: int # -1 or 0: inactive, 1: active, unix timestamp: start in future
|
|
69
119
|
regeneration_last_1: datetime
|
|
70
120
|
regeneration_last_2: datetime
|
|
71
|
-
service_customer: datetime
|
|
121
|
+
service_customer: datetime
|
|
72
122
|
service_technician: datetime
|
|
73
123
|
out_of_service: int
|
|
74
124
|
regeneration_count_1: int
|
|
@@ -81,7 +131,7 @@ class CurrentResponse:
|
|
|
81
131
|
treated_day: int # treated water current day
|
|
82
132
|
treated_month: int # treated water current month
|
|
83
133
|
treated_year: int # treated water current year
|
|
84
|
-
columns: int
|
|
134
|
+
columns: int # number of columns: 2 for BWT Duo and 1 for BWT Perla One
|
|
85
135
|
|
|
86
136
|
|
|
87
137
|
@dataclass
|
|
@@ -91,11 +141,13 @@ class DailyResponse:
|
|
|
91
141
|
# [1] = 00:30 - 00:59
|
|
92
142
|
values: list[int]
|
|
93
143
|
|
|
144
|
+
|
|
94
145
|
@dataclass
|
|
95
146
|
class MonthlyResponse:
|
|
96
147
|
# treated water in 1 day intervals
|
|
97
148
|
values: list[int]
|
|
98
149
|
|
|
150
|
+
|
|
99
151
|
@dataclass
|
|
100
152
|
class YearlyResponse:
|
|
101
153
|
# treated water in 1 month intervals
|
|
@@ -109,6 +161,15 @@ class WifiResponse:
|
|
|
109
161
|
"""UUID 0104: Wi-Fi Information"""
|
|
110
162
|
ssid: str # Wi-Fi network name
|
|
111
163
|
rssi: int # Wi-Fi signal strength in dBm
|
|
164
|
+
rssiAvg: str # Average Wi-Fi signal strength in dBm
|
|
165
|
+
rssiSig: str # Signal strength standard deviation in dBm
|
|
166
|
+
dhcp: bool # DHCP enabled
|
|
167
|
+
ip: str | None # IP address
|
|
168
|
+
sn: str | None # Serial number
|
|
169
|
+
sg: str | None # Subnet mask
|
|
170
|
+
pDns: str | None # Primary DNS
|
|
171
|
+
sDns: str | None # Secondary DNS
|
|
172
|
+
mac: str | None # MAC address
|
|
112
173
|
|
|
113
174
|
|
|
114
175
|
@dataclass
|
|
@@ -122,6 +183,11 @@ class DeviceInfoResponse:
|
|
|
122
183
|
dev_state: SmartDosStatus | None # Current device status
|
|
123
184
|
active_states: list[SmartDosStatus | None] # List of active device statuses
|
|
124
185
|
comm_date: str # Start-up date (ISO format)
|
|
186
|
+
device_id: str # Device ID
|
|
187
|
+
device_type: str # Device type
|
|
188
|
+
device_variant: str # Device variant
|
|
189
|
+
total_flow: int # Total treated water in ml
|
|
190
|
+
total_dosed: int # Total dosed substance in ml
|
|
125
191
|
|
|
126
192
|
|
|
127
193
|
@dataclass
|
|
@@ -133,6 +199,12 @@ class ConfigurationResponse:
|
|
|
133
199
|
aqa_watch_en: bool # AQA Watch active
|
|
134
200
|
aqa_max_flow_en: bool # AQA MaxFlow active
|
|
135
201
|
aqa_volume_val: float # AQA Volume value (l)
|
|
202
|
+
volume_per_stroke: float # Volume per stroke (ml)
|
|
203
|
+
pouch_empty_timeout: int # Pouch empty timeout (s)
|
|
204
|
+
pouch_not_empty_timeout: int # Pouch not empty timeout (s)
|
|
205
|
+
aqa_watch_val: int # AQA Watch value (s)
|
|
206
|
+
aqa_max_flow_val: float # AQA MaxFlow value (l/h)
|
|
207
|
+
rest_server_en: bool # REST server active
|
|
136
208
|
|
|
137
209
|
|
|
138
210
|
@dataclass
|
|
@@ -156,15 +228,17 @@ class PouchInfoResponse:
|
|
|
156
228
|
@dataclass
|
|
157
229
|
class RemainingCapacityResponse:
|
|
158
230
|
"""UUID 0402: Remaining Capacity"""
|
|
159
|
-
rem_capacity:
|
|
160
|
-
rem_capacity_pct:
|
|
161
|
-
rem_capacity_days:
|
|
231
|
+
rem_capacity: float # Remaining volume in ml
|
|
232
|
+
rem_capacity_pct: float # Remaining volume in %
|
|
233
|
+
rem_capacity_days: int # Remaining volume in days
|
|
234
|
+
unit: int # Unit (0 = ml)
|
|
162
235
|
|
|
163
236
|
|
|
164
237
|
@dataclass
|
|
165
238
|
class TreatedWaterResponse:
|
|
166
239
|
"""UUID 0503: Treated Water"""
|
|
167
240
|
total_flow: int # Total treated water in ml
|
|
241
|
+
total_ticks: int # Total treated water ticks??
|
|
168
242
|
|
|
169
243
|
|
|
170
244
|
@dataclass
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""The BWT Smart Dos API class."""
|
|
2
2
|
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
|
|
3
5
|
import aiohttp
|
|
4
6
|
import logging
|
|
5
7
|
from typing import Any
|
|
@@ -61,7 +63,19 @@ class BwtSmartDosApi:
|
|
|
61
63
|
"""UUID 0104: Get Wi-Fi name and signal strength."""
|
|
62
64
|
self._logger.debug("Fetching Wi-Fi info from %s", self._host)
|
|
63
65
|
raw = await self._get_gatt("0104")
|
|
64
|
-
return WifiResponse(
|
|
66
|
+
return WifiResponse(
|
|
67
|
+
ssid=raw["ssid"],
|
|
68
|
+
rssi=raw["rssi"],
|
|
69
|
+
rssiAvg=raw["rssiAvg"],
|
|
70
|
+
rssiSig=raw["rssiSig"],
|
|
71
|
+
dhcp=raw["dhcp"],
|
|
72
|
+
ip=raw["ip"],
|
|
73
|
+
sn=raw["sn"],
|
|
74
|
+
sg=raw["sg"],
|
|
75
|
+
pDns=raw["pDns"],
|
|
76
|
+
sDns=raw["sDns"],
|
|
77
|
+
mac=raw["mac"]
|
|
78
|
+
)
|
|
65
79
|
|
|
66
80
|
async def get_device_info(self) -> DeviceInfoResponse:
|
|
67
81
|
"""UUID 0201: Get device information."""
|
|
@@ -71,11 +85,16 @@ class BwtSmartDosApi:
|
|
|
71
85
|
fw_rev=raw["fwRev"],
|
|
72
86
|
hw_rev=raw["hwRev"],
|
|
73
87
|
product_code=raw["productCode"],
|
|
88
|
+
device_id=raw["iotDevId"],
|
|
89
|
+
device_type=raw["iotDevType"],
|
|
90
|
+
device_variant=raw["iotDevVariant"],
|
|
74
91
|
uptime=raw["uptime"],
|
|
75
92
|
operating_time=raw["operatingTime"],
|
|
76
93
|
dev_state=SmartDosStatus(raw["devState"]),
|
|
77
94
|
active_states=[SmartDosStatus(state) for state in raw["activeStates"]],
|
|
78
95
|
comm_date=raw["commDate"],
|
|
96
|
+
total_flow=raw["lifeTimeFlow_ml"],
|
|
97
|
+
total_dosed=raw["lifeTimeDosed_ml"],
|
|
79
98
|
)
|
|
80
99
|
|
|
81
100
|
async def get_configuration(self) -> ConfigurationResponse:
|
|
@@ -85,10 +104,16 @@ class BwtSmartDosApi:
|
|
|
85
104
|
return ConfigurationResponse(
|
|
86
105
|
buzzer_en=raw["buzzerEn"],
|
|
87
106
|
dosing_rate=raw["dosingRate"],
|
|
107
|
+
volume_per_stroke=raw["volumePerStroke"],
|
|
108
|
+
pouch_empty_timeout=raw["pouchEmptyTimeout"],
|
|
109
|
+
pouch_not_empty_timeout=raw["pouchNotEmptyTimeout"],
|
|
88
110
|
aqa_volume_en=raw["aqaVolumeEn"],
|
|
89
111
|
aqa_watch_en=raw["aqaWatchEn"],
|
|
90
112
|
aqa_max_flow_en=raw["aqaMaxFlowEn"],
|
|
91
113
|
aqa_volume_val=raw["aqaVolumeVal"],
|
|
114
|
+
aqa_watch_val=raw["aqaWatchVal"],
|
|
115
|
+
aqa_max_flow_val=raw["aqaMaxFlowVal"],
|
|
116
|
+
rest_server_en=raw["restServerEn"],
|
|
92
117
|
)
|
|
93
118
|
|
|
94
119
|
async def get_time_info(self) -> TimeResponse:
|
|
@@ -110,22 +135,24 @@ class BwtSmartDosApi:
|
|
|
110
135
|
unit=raw["unit"],
|
|
111
136
|
)
|
|
112
137
|
|
|
113
|
-
async def get_remaining_capacity(self) -> RemainingCapacityResponse:
|
|
138
|
+
async def get_remaining_capacity(self) -> Mapping[int, RemainingCapacityResponse]:
|
|
114
139
|
"""UUID 0402: Get remaining capacity information."""
|
|
115
140
|
self._logger.debug("Fetching remaining capacity from %s", self._host)
|
|
116
141
|
raw = await self._get_gatt("0402")
|
|
117
|
-
return RemainingCapacityResponse(
|
|
118
|
-
rem_capacity=
|
|
119
|
-
rem_capacity_pct=
|
|
120
|
-
rem_capacity_days=
|
|
121
|
-
|
|
142
|
+
return {int(k): RemainingCapacityResponse(
|
|
143
|
+
rem_capacity=v["remCapacity"],
|
|
144
|
+
rem_capacity_pct=v["remCapacityPct"],
|
|
145
|
+
rem_capacity_days=v["remCapacityDays"],
|
|
146
|
+
unit=v["unit"]) for k, v in raw.items()}
|
|
122
147
|
|
|
123
|
-
async def get_treated_water(self) -> TreatedWaterResponse:
|
|
148
|
+
async def get_treated_water(self) -> Mapping[int, TreatedWaterResponse]:
|
|
124
149
|
"""UUID 0503: Get treated water information."""
|
|
125
150
|
self._logger.debug("Fetching treated water from %s", self._host)
|
|
126
151
|
raw = await self._get_gatt("0503")
|
|
127
|
-
|
|
128
|
-
|
|
152
|
+
return {int(k): TreatedWaterResponse(
|
|
153
|
+
total_flow=v["totFlow"],
|
|
154
|
+
total_ticks=v["totTicks"],
|
|
155
|
+
) for k, v in raw["flow"].items()}
|
|
129
156
|
|
|
130
157
|
async def get_substance_dosage(self) -> SubstanceDosageResponse:
|
|
131
158
|
"""UUID 0505: Get substance dosage information."""
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
.coveragerc
|
|
2
2
|
.gitignore
|
|
3
|
-
AUTHORS.rst
|
|
4
|
-
CHANGELOG.rst
|
|
5
|
-
CONTRIBUTING.rst
|
|
6
3
|
LICENSE.txt
|
|
7
4
|
README.rst
|
|
8
5
|
pyproject.toml
|
|
@@ -33,4 +30,17 @@ src/bwt_api.egg-info/scm_version.json
|
|
|
33
30
|
src/bwt_api.egg-info/top_level.txt
|
|
34
31
|
tests/conftest.py
|
|
35
32
|
tests/test_api.py
|
|
36
|
-
tests/test_distinguish.py
|
|
33
|
+
tests/test_distinguish.py
|
|
34
|
+
tests/data/perla/GetCurrentData.json
|
|
35
|
+
tests/data/perla/GetDailyData.json
|
|
36
|
+
tests/data/perla/GetMonthlyData.json
|
|
37
|
+
tests/data/perla/GetYearlyData.json
|
|
38
|
+
tests/data/silk/registers.json
|
|
39
|
+
tests/data/smartdos/gatt_0104.json
|
|
40
|
+
tests/data/smartdos/gatt_0201.json
|
|
41
|
+
tests/data/smartdos/gatt_0202.json
|
|
42
|
+
tests/data/smartdos/gatt_0208.json
|
|
43
|
+
tests/data/smartdos/gatt_0401.json
|
|
44
|
+
tests/data/smartdos/gatt_0402.json
|
|
45
|
+
tests/data/smartdos/gatt_0503.json
|
|
46
|
+
tests/data/smartdos/gatt_0505.json
|
|
@@ -1,32 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"files": [
|
|
3
|
-
".gitignore",
|
|
4
|
-
"AUTHORS.rst",
|
|
5
|
-
"LICENSE.txt",
|
|
6
|
-
"CHANGELOG.rst",
|
|
7
3
|
"tox.ini",
|
|
8
|
-
"
|
|
9
|
-
".
|
|
4
|
+
"LICENSE.txt",
|
|
5
|
+
"README.rst",
|
|
10
6
|
"setup.py",
|
|
11
|
-
"
|
|
7
|
+
"pyproject.toml",
|
|
12
8
|
"setup.cfg",
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"src/bwt_api/api.py",
|
|
16
|
-
"src/bwt_api/exception.py",
|
|
17
|
-
"src/bwt_api/bwt.py",
|
|
18
|
-
"src/bwt_api/data.py",
|
|
19
|
-
"src/bwt_api/smart_dos_api.py",
|
|
20
|
-
"src/bwt_api/bwt_api.py",
|
|
21
|
-
"src/bwt_api/silk_api.py",
|
|
22
|
-
"src/bwt_api/error.py",
|
|
23
|
-
"src/bwt_api/skeleton.py",
|
|
24
|
-
"src/bwt_api/__init__.py",
|
|
25
|
-
".devcontainer/devcontainer.json",
|
|
9
|
+
".gitignore",
|
|
10
|
+
".coveragerc",
|
|
26
11
|
".github/workflows/python-publish.yml",
|
|
27
12
|
".github/workflows/python-package.yml",
|
|
13
|
+
".devcontainer/devcontainer.json",
|
|
14
|
+
"tests/test_api.py",
|
|
28
15
|
"tests/test_distinguish.py",
|
|
29
16
|
"tests/conftest.py",
|
|
30
|
-
"tests/
|
|
17
|
+
"tests/data/silk/registers.json",
|
|
18
|
+
"tests/data/smartdos/gatt_0202.json",
|
|
19
|
+
"tests/data/smartdos/gatt_0208.json",
|
|
20
|
+
"tests/data/smartdos/gatt_0401.json",
|
|
21
|
+
"tests/data/smartdos/gatt_0104.json",
|
|
22
|
+
"tests/data/smartdos/gatt_0503.json",
|
|
23
|
+
"tests/data/smartdos/gatt_0505.json",
|
|
24
|
+
"tests/data/smartdos/gatt_0402.json",
|
|
25
|
+
"tests/data/smartdos/gatt_0201.json",
|
|
26
|
+
"tests/data/perla/GetMonthlyData.json",
|
|
27
|
+
"tests/data/perla/GetDailyData.json",
|
|
28
|
+
"tests/data/perla/GetCurrentData.json",
|
|
29
|
+
"tests/data/perla/GetYearlyData.json",
|
|
30
|
+
"src/bwt_api/data.py",
|
|
31
|
+
"src/bwt_api/smart_dos_api.py",
|
|
32
|
+
"src/bwt_api/exception.py",
|
|
33
|
+
"src/bwt_api/__init__.py",
|
|
34
|
+
"src/bwt_api/bwt.py",
|
|
35
|
+
"src/bwt_api/api.py",
|
|
36
|
+
"src/bwt_api/error.py",
|
|
37
|
+
"src/bwt_api/bwt_api.py",
|
|
38
|
+
"src/bwt_api/skeleton.py",
|
|
39
|
+
"src/bwt_api/silk_api.py"
|
|
31
40
|
]
|
|
32
41
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ActiveErrorIDs": "",
|
|
3
|
+
"BlendedWaterSinceSetup_l": 123456,
|
|
4
|
+
"CapacityColumn1_ml_dH": 500000,
|
|
5
|
+
"CapacityColumn2_ml_dH": -1,
|
|
6
|
+
"CurrentFlowrate_l_h": 100,
|
|
7
|
+
"DosingSinceSetup_ml": 2500,
|
|
8
|
+
"FirmwareVersion": "1.2.3",
|
|
9
|
+
"HardnessIN_CaCO3": 200,
|
|
10
|
+
"HardnessIN_dH": 11,
|
|
11
|
+
"HardnessIN_fH": 19,
|
|
12
|
+
"HardnessIN_mmol_l": 2.0,
|
|
13
|
+
"HardnessOUT_CaCO3": 20,
|
|
14
|
+
"HardnessOUT_dH": 1,
|
|
15
|
+
"HardnessOUT_fH": 2,
|
|
16
|
+
"HardnessOUT_mmol_l": 0.2,
|
|
17
|
+
"HolidayModeStartTime": "",
|
|
18
|
+
"LastRegenerationColumn1": "2023-07-01 12:00:00",
|
|
19
|
+
"LastRegenerationColumn2": "2023-07-02 12:00:00",
|
|
20
|
+
"LastServiceCustomer": "2024-01-01 09:00:00",
|
|
21
|
+
"LastServiceTechnican": "2024-02-01 10:00:00",
|
|
22
|
+
"OutOfService": 0,
|
|
23
|
+
"RegenerationCounterColumn1": 5,
|
|
24
|
+
"RegenerationCounterColumn2": 0,
|
|
25
|
+
"RegenerationCountSinceSetup": 10,
|
|
26
|
+
"RegenerativLevel": 80,
|
|
27
|
+
"RegenerativRemainingDays": 100,
|
|
28
|
+
"RegenerativSinceSetup_g": 12345,
|
|
29
|
+
"ShowError": 0,
|
|
30
|
+
"WaterTreatedCurrentDay_l": 1234,
|
|
31
|
+
"WaterTreatedCurrentMonth_l": 5678,
|
|
32
|
+
"WaterTreatedCurrentYear_l": 23456
|
|
33
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"0000_0029_l": 0,
|
|
3
|
+
"0030_0059_l": 1,
|
|
4
|
+
"0100_0129_l": 2,
|
|
5
|
+
"0130_0159_l": 3,
|
|
6
|
+
"0200_0229_l": 4,
|
|
7
|
+
"0230_0259_l": 5,
|
|
8
|
+
"0300_0329_l": 6,
|
|
9
|
+
"0330_0359_l": 7,
|
|
10
|
+
"0400_0429_l": 8,
|
|
11
|
+
"0430_0459_l": 9,
|
|
12
|
+
"0500_0529_l": 10,
|
|
13
|
+
"0530_0559_l": 11,
|
|
14
|
+
"0600_0629_l": 12,
|
|
15
|
+
"0630_0659_l": 13,
|
|
16
|
+
"0700_0729_l": 14,
|
|
17
|
+
"0730_0759_l": 15,
|
|
18
|
+
"0800_0829_l": 16,
|
|
19
|
+
"0830_0859_l": 17,
|
|
20
|
+
"0900_0929_l": 18,
|
|
21
|
+
"0930_0959_l": 19,
|
|
22
|
+
"1000_1029_l": 20,
|
|
23
|
+
"1030_1059_l": 21,
|
|
24
|
+
"1100_1129_l": 22,
|
|
25
|
+
"1130_1159_l": 23,
|
|
26
|
+
"1200_1229_l": 24,
|
|
27
|
+
"1230_1259_l": 25,
|
|
28
|
+
"1300_1329_l": 26,
|
|
29
|
+
"1330_1359_l": 27,
|
|
30
|
+
"1400_1429_l": 28,
|
|
31
|
+
"1430_1459_l": 29,
|
|
32
|
+
"1500_1529_l": 30,
|
|
33
|
+
"1530_1559_l": 31,
|
|
34
|
+
"1600_1629_l": 32,
|
|
35
|
+
"1630_1659_l": 33,
|
|
36
|
+
"1700_1729_l": 34,
|
|
37
|
+
"1730_1759_l": 35,
|
|
38
|
+
"1800_1829_l": 36,
|
|
39
|
+
"1830_1859_l": 37,
|
|
40
|
+
"1900_1929_l": 38,
|
|
41
|
+
"1930_1959_l": 39,
|
|
42
|
+
"2000_2029_l": 40,
|
|
43
|
+
"2030_2059_l": 41,
|
|
44
|
+
"2100_2129_l": 42,
|
|
45
|
+
"2130_2159_l": 43,
|
|
46
|
+
"2200_2229_l": 44,
|
|
47
|
+
"2230_2259_l": 45,
|
|
48
|
+
"2300_2329_l": 46,
|
|
49
|
+
"2330_2359_l": 47
|
|
50
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Day01_l": 1,
|
|
3
|
+
"Day02_l": 2,
|
|
4
|
+
"Day03_l": 3,
|
|
5
|
+
"Day04_l": 4,
|
|
6
|
+
"Day05_l": 5,
|
|
7
|
+
"Day06_l": 6,
|
|
8
|
+
"Day07_l": 7,
|
|
9
|
+
"Day08_l": 8,
|
|
10
|
+
"Day09_l": 9,
|
|
11
|
+
"Day10_l": 10,
|
|
12
|
+
"Day11_l": 11,
|
|
13
|
+
"Day12_l": 12,
|
|
14
|
+
"Day13_l": 13,
|
|
15
|
+
"Day14_l": 14,
|
|
16
|
+
"Day15_l": 15,
|
|
17
|
+
"Day16_l": 16,
|
|
18
|
+
"Day17_l": 17,
|
|
19
|
+
"Day18_l": 18,
|
|
20
|
+
"Day19_l": 19,
|
|
21
|
+
"Day20_l": 20,
|
|
22
|
+
"Day21_l": 21,
|
|
23
|
+
"Day22_l": 22,
|
|
24
|
+
"Day23_l": 23,
|
|
25
|
+
"Day24_l": 24,
|
|
26
|
+
"Day25_l": 25,
|
|
27
|
+
"Day26_l": 26,
|
|
28
|
+
"Day27_l": 27,
|
|
29
|
+
"Day28_l": 28,
|
|
30
|
+
"Day29_l": 29,
|
|
31
|
+
"Day30_l": 30,
|
|
32
|
+
"Day31_l": 31
|
|
33
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"fwRev": "1.2.0",
|
|
3
|
+
"hwRev": "2.4.0(A)",
|
|
4
|
+
"productCode": "3HZR-1R37",
|
|
5
|
+
"iotDevId": "5b29b9e9-24c5-4eea-a599-927922a89f5a",
|
|
6
|
+
"iotDevType": "bwt_bewados",
|
|
7
|
+
"iotDevVariant": "dev",
|
|
8
|
+
"uptime": 6510811,
|
|
9
|
+
"operatingTime": 40773211,
|
|
10
|
+
"devState": 2001,
|
|
11
|
+
"activeStates": [
|
|
12
|
+
2001
|
|
13
|
+
],
|
|
14
|
+
"commDate": "2026-01-04T10:01:05.891Z",
|
|
15
|
+
"lifeTimeFlow_ml": 112507432,
|
|
16
|
+
"lifeTimeDosed_ml": 2746.1572265625
|
|
17
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"buzzerEn": false,
|
|
3
|
+
"dosingRate": 10,
|
|
4
|
+
"volumePerStroke": 0.053784001618623734,
|
|
5
|
+
"pouchEmptyTimeout": 7200,
|
|
6
|
+
"pouchNotEmptyTimeout": 20,
|
|
7
|
+
"aqaVolumeEn": false,
|
|
8
|
+
"aqaWatchEn": false,
|
|
9
|
+
"aqaMaxFlowEn": false,
|
|
10
|
+
"aqaVolumeVal": 9999,
|
|
11
|
+
"aqaWatchVal": 0,
|
|
12
|
+
"aqaMaxFlowVal": 7,
|
|
13
|
+
"restServerEn": true,
|
|
14
|
+
"defaults": {
|
|
15
|
+
"consumable": {
|
|
16
|
+
"totCap": 1500
|
|
17
|
+
},
|
|
18
|
+
"aqaAlert": {
|
|
19
|
+
"aqaVolumeVal": 9999,
|
|
20
|
+
"aqaWatchVal": 60,
|
|
21
|
+
"aqaMaxFlowVal": 7
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from zoneinfo import ZoneInfo
|
|
3
3
|
from unittest.mock import Mock
|
|
4
|
+
from pathlib import Path
|
|
4
5
|
import pytest
|
|
5
6
|
from aiohttp.client_reqrep import ClientResponse
|
|
6
7
|
|
|
@@ -101,6 +102,11 @@ current_json_empty_errors = """
|
|
|
101
102
|
}
|
|
102
103
|
"""
|
|
103
104
|
|
|
105
|
+
|
|
106
|
+
def load_json_str(name: str) -> str:
|
|
107
|
+
"""Load a JSON file from tests/data/smartdos and return its text."""
|
|
108
|
+
return (Path(__file__).parent / "data" / "smartdos" / name).read_text()
|
|
109
|
+
|
|
104
110
|
current_json_perla_one = """
|
|
105
111
|
{
|
|
106
112
|
"ActiveErrorIDs" : "",
|
|
@@ -300,17 +306,33 @@ def test_unknown_error_no_mutation():
|
|
|
300
306
|
# Different unknown codes produce different instances
|
|
301
307
|
assert err1 is not err2
|
|
302
308
|
|
|
309
|
+
async def test_smartdos_get_wifi_info():
|
|
310
|
+
with aioresponses() as mocked:
|
|
311
|
+
mocked.get(
|
|
312
|
+
"http://host:80/api/v1/gatt/0104",
|
|
313
|
+
status=200,
|
|
314
|
+
body=load_json_str("gatt_0104.json"),
|
|
315
|
+
headers={"Content-Type": "application/json"},
|
|
316
|
+
)
|
|
317
|
+
async with BwtSmartDosApi("host") as api:
|
|
318
|
+
result = await api.get_wifi_info()
|
|
319
|
+
assert result.ssid == "Graf WLAN"
|
|
320
|
+
assert result.rssiAvg == "-54.00"
|
|
321
|
+
assert result.mac == "AA:BB:CC:DD:EE:FF"
|
|
322
|
+
|
|
323
|
+
|
|
303
324
|
async def test_smartdos_get_gatt_0201():
|
|
304
325
|
with aioresponses() as mocked:
|
|
305
326
|
mocked.get(
|
|
306
327
|
"http://host:80/api/v1/gatt/0201",
|
|
307
328
|
status=200,
|
|
308
|
-
|
|
329
|
+
body=load_json_str("gatt_0201.json"),
|
|
309
330
|
headers={"Content-Type": "application/json"},
|
|
310
331
|
)
|
|
311
332
|
async with BwtSmartDosApi("host") as api:
|
|
312
333
|
result = await api.get_gatt_0201()
|
|
313
|
-
assert result
|
|
334
|
+
assert result["fwRev"] == "1.2.0"
|
|
335
|
+
assert result["productCode"] == "3HZR-1R37"
|
|
314
336
|
|
|
315
337
|
|
|
316
338
|
async def test_smartdos_unknown_response():
|
|
@@ -321,12 +343,41 @@ async def test_smartdos_unknown_response():
|
|
|
321
343
|
await api.get_gatt_0201()
|
|
322
344
|
|
|
323
345
|
|
|
346
|
+
async def test_smartdos_get_configuration():
|
|
347
|
+
with aioresponses() as mocked:
|
|
348
|
+
mocked.get(
|
|
349
|
+
"http://host:80/api/v1/gatt/0202",
|
|
350
|
+
status=200,
|
|
351
|
+
body=load_json_str("gatt_0202.json"),
|
|
352
|
+
headers={"Content-Type": "application/json"},
|
|
353
|
+
)
|
|
354
|
+
async with BwtSmartDosApi("host") as api:
|
|
355
|
+
result = await api.get_configuration()
|
|
356
|
+
assert result.buzzer_en is False
|
|
357
|
+
assert result.dosing_rate == 10
|
|
358
|
+
assert result.rest_server_en is True
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
async def test_smartdos_get_time_info():
|
|
362
|
+
with aioresponses() as mocked:
|
|
363
|
+
mocked.get(
|
|
364
|
+
"http://host:80/api/v1/gatt/0208",
|
|
365
|
+
status=200,
|
|
366
|
+
body=load_json_str("gatt_0208.json"),
|
|
367
|
+
headers={"Content-Type": "application/json"},
|
|
368
|
+
)
|
|
369
|
+
async with BwtSmartDosApi("host") as api:
|
|
370
|
+
result = await api.get_time_info()
|
|
371
|
+
assert result.time == "2026-07-14 12:00:00"
|
|
372
|
+
assert result.timezone == "UTC"
|
|
373
|
+
|
|
374
|
+
|
|
324
375
|
async def test_smartdos_device_info_parses_status_values():
|
|
325
376
|
with aioresponses() as mocked:
|
|
326
377
|
mocked.get(
|
|
327
378
|
"http://host:80/api/v1/gatt/0201",
|
|
328
379
|
status=200,
|
|
329
|
-
body='{"fwRev":"1.
|
|
380
|
+
body='{"fwRev":"1.2.0","hwRev":"2.4.0(A)","productCode":"3HZR-1R37","iotDevId":"5b29b9e9-24c5-4eea-a599-927922a89f5a","iotDevType":"bwt_bewados","iotDevVariant":"dev","uptime":6510811,"operatingTime":40773211,"devState":2001,"activeStates":[2001],"commDate":"2026-01-04T10:01:05.891Z","lifeTimeFlow_ml":112507432,"lifeTimeDosed_ml":2746.1572265625}',
|
|
330
381
|
headers={"Content-Type": "application/json"},
|
|
331
382
|
)
|
|
332
383
|
async with BwtSmartDosApi("host") as api:
|
|
@@ -335,17 +386,62 @@ async def test_smartdos_device_info_parses_status_values():
|
|
|
335
386
|
assert result.active_states == [SmartDosStatus.STANDBY]
|
|
336
387
|
|
|
337
388
|
|
|
389
|
+
async def test_smartdos_get_remaining_capacity():
|
|
390
|
+
with aioresponses() as mocked:
|
|
391
|
+
mocked.get(
|
|
392
|
+
"http://host:80/api/v1/gatt/0402",
|
|
393
|
+
status=200,
|
|
394
|
+
body=load_json_str("gatt_0402.json"),
|
|
395
|
+
headers={"Content-Type": "application/json"},
|
|
396
|
+
)
|
|
397
|
+
async with BwtSmartDosApi("host") as api:
|
|
398
|
+
result = await api.get_remaining_capacity()
|
|
399
|
+
assert 1 in result
|
|
400
|
+
assert result[1].rem_capacity == 1451.1103515625
|
|
401
|
+
assert result[1].rem_capacity_pct == 97
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
async def test_smartdos_get_treated_water():
|
|
405
|
+
with aioresponses() as mocked:
|
|
406
|
+
mocked.get(
|
|
407
|
+
"http://host:80/api/v1/gatt/0503",
|
|
408
|
+
status=200,
|
|
409
|
+
body=load_json_str("gatt_0503.json"),
|
|
410
|
+
headers={"Content-Type": "application/json"},
|
|
411
|
+
)
|
|
412
|
+
async with BwtSmartDosApi("host") as api:
|
|
413
|
+
result = await api.get_treated_water()
|
|
414
|
+
assert 1 in result
|
|
415
|
+
assert result[1].total_flow == 112507432
|
|
416
|
+
assert 2 in result
|
|
417
|
+
assert result[2].total_flow == 20244130
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
async def test_smartdos_get_substance_dosage():
|
|
421
|
+
with aioresponses() as mocked:
|
|
422
|
+
mocked.get(
|
|
423
|
+
"http://host:80/api/v1/gatt/0505",
|
|
424
|
+
status=200,
|
|
425
|
+
body=load_json_str("gatt_0505.json"),
|
|
426
|
+
headers={"Content-Type": "application/json"},
|
|
427
|
+
)
|
|
428
|
+
async with BwtSmartDosApi("host") as api:
|
|
429
|
+
result = await api.get_substance_dosage()
|
|
430
|
+
assert result.dosed_mineral == 2746.1572265625
|
|
431
|
+
|
|
432
|
+
|
|
338
433
|
async def test_smartdos_pouch_info_parses_substance_type():
|
|
339
434
|
with aioresponses() as mocked:
|
|
340
435
|
mocked.get(
|
|
341
436
|
"http://host:80/api/v1/gatt/0401",
|
|
342
437
|
status=200,
|
|
343
|
-
|
|
438
|
+
body=load_json_str("gatt_0401.json"),
|
|
344
439
|
headers={"Content-Type": "application/json"},
|
|
345
440
|
)
|
|
346
441
|
async with BwtSmartDosApi("host") as api:
|
|
347
442
|
result = await api.get_pouch_info()
|
|
348
|
-
assert result.substance_type == SubstanceType.
|
|
443
|
+
assert result.substance_type == SubstanceType.L1_LE
|
|
444
|
+
assert result.tot_cap == 1000
|
|
349
445
|
|
|
350
446
|
def test_treated_to_blended():
|
|
351
447
|
assert treated_to_blended(0, 21, 4) == 0
|
bwt_api-1.0.2/AUTHORS.rst
DELETED
bwt_api-1.0.2/CHANGELOG.rst
DELETED
bwt_api-1.0.2/CONTRIBUTING.rst
DELETED
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
============
|
|
2
|
-
Contributing
|
|
3
|
-
============
|
|
4
|
-
|
|
5
|
-
Welcome to ``bwt_api`` contributor's guide.
|
|
6
|
-
|
|
7
|
-
This document focuses on getting any potential contributor familiarized
|
|
8
|
-
with the development processes, but `other kinds of contributions`_ are also
|
|
9
|
-
appreciated.
|
|
10
|
-
|
|
11
|
-
If you are new to using git_ or have never collaborated in a project previously,
|
|
12
|
-
please have a look at `contribution-guide.org`_. Other resources are also
|
|
13
|
-
listed in the excellent `guide created by FreeCodeCamp`_ [#contrib1]_.
|
|
14
|
-
|
|
15
|
-
Please notice, all users and contributors are expected to be **open,
|
|
16
|
-
considerate, reasonable, and respectful**. When in doubt, `Python Software
|
|
17
|
-
Foundation's Code of Conduct`_ is a good reference in terms of behavior
|
|
18
|
-
guidelines.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Issue Reports
|
|
22
|
-
=============
|
|
23
|
-
|
|
24
|
-
If you experience bugs or general issues with ``bwt_api``, please have a look
|
|
25
|
-
on the `issue tracker`_. If you don't see anything useful there, please feel
|
|
26
|
-
free to fire an issue report.
|
|
27
|
-
|
|
28
|
-
.. tip::
|
|
29
|
-
Please don't forget to include the closed issues in your search.
|
|
30
|
-
Sometimes a solution was already reported, and the problem is considered
|
|
31
|
-
**solved**.
|
|
32
|
-
|
|
33
|
-
New issue reports should include information about your programming environment
|
|
34
|
-
(e.g., operating system, Python version) and steps to reproduce the problem.
|
|
35
|
-
Please try also to simplify the reproduction steps to a very minimal example
|
|
36
|
-
that still illustrates the problem you are facing. By removing other factors,
|
|
37
|
-
you help us to identify the root cause of the issue.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
Documentation Improvements
|
|
41
|
-
==========================
|
|
42
|
-
|
|
43
|
-
You can help improve ``bwt_api`` docs by making them more readable and coherent, or
|
|
44
|
-
by adding missing information and correcting mistakes.
|
|
45
|
-
|
|
46
|
-
.. tip::
|
|
47
|
-
Please notice that the `GitHub web interface`_ provides a quick way of
|
|
48
|
-
propose changes in ``bwt_api``'s files. While this mechanism can
|
|
49
|
-
be tricky for normal code contributions, it works perfectly fine for
|
|
50
|
-
contributing to the docs, and can be quite handy.
|
|
51
|
-
|
|
52
|
-
If you are interested in trying this method out, please navigate to
|
|
53
|
-
the ``docs`` folder in the source repository_, find which file you
|
|
54
|
-
would like to propose changes and click in the little pencil icon at the
|
|
55
|
-
top, to open `GitHub's code editor`_. Once you finish editing the file,
|
|
56
|
-
please write a message in the form at the bottom of the page describing
|
|
57
|
-
which changes have you made and what are the motivations behind them and
|
|
58
|
-
submit your proposal.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
Code Contributions
|
|
62
|
-
==================
|
|
63
|
-
|
|
64
|
-
Submit an issue
|
|
65
|
-
---------------
|
|
66
|
-
|
|
67
|
-
Before you work on any non-trivial code contribution it's best to first create
|
|
68
|
-
a report in the `issue tracker`_ to start a discussion on the subject.
|
|
69
|
-
This often provides additional considerations and avoids unnecessary work.
|
|
70
|
-
|
|
71
|
-
Create an environment
|
|
72
|
-
---------------------
|
|
73
|
-
|
|
74
|
-
Before you start coding, we recommend creating an isolated `virtual
|
|
75
|
-
environment`_ to avoid any problems with your installed Python packages.
|
|
76
|
-
This can easily be done via either |virtualenv|_::
|
|
77
|
-
|
|
78
|
-
virtualenv <PATH TO VENV>
|
|
79
|
-
source <PATH TO VENV>/bin/activate
|
|
80
|
-
|
|
81
|
-
or Miniconda_::
|
|
82
|
-
|
|
83
|
-
conda create -n bwt_api python=3 six virtualenv pytest pytest-cov
|
|
84
|
-
conda activate bwt_api
|
|
85
|
-
|
|
86
|
-
Clone the repository
|
|
87
|
-
--------------------
|
|
88
|
-
|
|
89
|
-
#. Create an user account on |the repository service| if you do not already have one.
|
|
90
|
-
#. Fork the project repository_: click on the *Fork* button near the top of the
|
|
91
|
-
page. This creates a copy of the code under your account on |the repository service|.
|
|
92
|
-
#. Clone this copy to your local disk::
|
|
93
|
-
|
|
94
|
-
git clone git@github.com:YourLogin/bwt_api.git
|
|
95
|
-
cd bwt_api
|
|
96
|
-
|
|
97
|
-
#. You should run::
|
|
98
|
-
|
|
99
|
-
pip install -U pip setuptools -e .
|
|
100
|
-
|
|
101
|
-
to be able to import the package under development in the Python REPL.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
Implement your changes
|
|
105
|
-
----------------------
|
|
106
|
-
|
|
107
|
-
#. Create a branch to hold your changes::
|
|
108
|
-
|
|
109
|
-
git checkout -b my-feature
|
|
110
|
-
|
|
111
|
-
and start making changes. Never work on the main branch!
|
|
112
|
-
|
|
113
|
-
#. Start your work on this branch. Don't forget to add docstrings_ to new
|
|
114
|
-
functions, modules and classes, especially if they are part of public APIs.
|
|
115
|
-
|
|
116
|
-
#. Add yourself to the list of contributors in ``AUTHORS.rst``.
|
|
117
|
-
|
|
118
|
-
#. When you’re done editing, do::
|
|
119
|
-
|
|
120
|
-
git add <MODIFIED FILES>
|
|
121
|
-
git commit
|
|
122
|
-
|
|
123
|
-
to record your changes in git_.
|
|
124
|
-
|
|
125
|
-
.. important:: Don't forget to add unit tests and documentation in case your
|
|
126
|
-
contribution adds an additional feature and is not just a bugfix.
|
|
127
|
-
|
|
128
|
-
Moreover, writing a `descriptive commit message`_ is highly recommended.
|
|
129
|
-
In case of doubt, you can check the commit history with::
|
|
130
|
-
|
|
131
|
-
git log --graph --decorate --pretty=oneline --abbrev-commit --all
|
|
132
|
-
|
|
133
|
-
to look for recurring communication patterns.
|
|
134
|
-
|
|
135
|
-
#. Please check that your changes don't break any unit tests with::
|
|
136
|
-
|
|
137
|
-
tox
|
|
138
|
-
|
|
139
|
-
(after having installed |tox|_ with ``pip install tox`` or ``pipx``).
|
|
140
|
-
|
|
141
|
-
You can also use |tox|_ to run several other pre-configured tasks in the
|
|
142
|
-
repository. Try ``tox -av`` to see a list of the available checks.
|
|
143
|
-
|
|
144
|
-
Submit your contribution
|
|
145
|
-
------------------------
|
|
146
|
-
|
|
147
|
-
#. If everything works fine, push your local branch to |the repository service| with::
|
|
148
|
-
|
|
149
|
-
git push -u origin my-feature
|
|
150
|
-
|
|
151
|
-
#. Go to the web page of your fork and click |contribute button|
|
|
152
|
-
to send your changes for review.
|
|
153
|
-
|
|
154
|
-
Find more detailed information in `creating a PR`_. You might also want to open
|
|
155
|
-
the PR as a draft first and mark it as ready for review after the feedbacks
|
|
156
|
-
from the continuous integration (CI) system or any required fixes.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
Troubleshooting
|
|
160
|
-
---------------
|
|
161
|
-
|
|
162
|
-
The following tips can be used when facing problems to build or test the
|
|
163
|
-
package:
|
|
164
|
-
|
|
165
|
-
#. Make sure to fetch all the tags from the upstream repository_.
|
|
166
|
-
The command ``git describe --abbrev=0 --tags`` should return the version you
|
|
167
|
-
are expecting. If you are trying to run CI scripts in a fork repository,
|
|
168
|
-
make sure to push all the tags.
|
|
169
|
-
You can also try to remove all the egg files or the complete egg folder, i.e.,
|
|
170
|
-
``.eggs``, as well as the ``*.egg-info`` folders in the ``src`` folder or
|
|
171
|
-
potentially in the root of your project.
|
|
172
|
-
|
|
173
|
-
#. Sometimes |tox|_ misses out when new dependencies are added, especially to
|
|
174
|
-
``setup.cfg`` and ``docs/requirements.txt``. If you find any problems with
|
|
175
|
-
missing dependencies when running a command with |tox|_, try to recreate the
|
|
176
|
-
``tox`` environment using the ``-r`` flag. For example, instead of::
|
|
177
|
-
|
|
178
|
-
tox -e docs
|
|
179
|
-
|
|
180
|
-
Try running::
|
|
181
|
-
|
|
182
|
-
tox -r -e docs
|
|
183
|
-
|
|
184
|
-
#. Make sure to have a reliable |tox|_ installation that uses the correct
|
|
185
|
-
Python version (e.g., 3.7+). When in doubt you can run::
|
|
186
|
-
|
|
187
|
-
tox --version
|
|
188
|
-
# OR
|
|
189
|
-
which tox
|
|
190
|
-
|
|
191
|
-
If you have trouble and are seeing weird errors upon running |tox|_, you can
|
|
192
|
-
also try to create a dedicated `virtual environment`_ with a |tox|_ binary
|
|
193
|
-
freshly installed. For example::
|
|
194
|
-
|
|
195
|
-
virtualenv .venv
|
|
196
|
-
source .venv/bin/activate
|
|
197
|
-
.venv/bin/pip install tox
|
|
198
|
-
.venv/bin/tox -e all
|
|
199
|
-
|
|
200
|
-
#. `Pytest can drop you`_ in an interactive session in the case an error occurs.
|
|
201
|
-
In order to do that you need to pass a ``--pdb`` option (for example by
|
|
202
|
-
running ``tox -- -k <NAME OF THE FALLING TEST> --pdb``).
|
|
203
|
-
You can also setup breakpoints manually instead of using the ``--pdb`` option.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
Maintainer tasks
|
|
207
|
-
================
|
|
208
|
-
|
|
209
|
-
Releases
|
|
210
|
-
--------
|
|
211
|
-
|
|
212
|
-
If you are part of the group of maintainers and have correct user permissions
|
|
213
|
-
on PyPI_, the following steps can be used to release a new version for
|
|
214
|
-
``bwt_api``:
|
|
215
|
-
|
|
216
|
-
#. Make sure all unit tests are successful.
|
|
217
|
-
#. Tag the current commit on the main branch with a release tag, e.g., ``v1.2.3``.
|
|
218
|
-
#. Push the new tag to the upstream repository_, e.g., ``git push upstream v1.2.3``
|
|
219
|
-
#. Clean up the ``dist`` and ``build`` folders with ``tox -e clean``
|
|
220
|
-
(or ``rm -rf dist build``)
|
|
221
|
-
to avoid confusion with old builds and Sphinx docs.
|
|
222
|
-
#. Run ``tox -e build`` and check that the files in ``dist`` have
|
|
223
|
-
the correct version (no ``.dirty`` or git_ hash) according to the git_ tag.
|
|
224
|
-
Also check the sizes of the distributions, if they are too big (e.g., >
|
|
225
|
-
500KB), unwanted clutter may have been accidentally included.
|
|
226
|
-
#. Run ``tox -e publish -- --repository pypi`` and check that everything was
|
|
227
|
-
uploaded to PyPI_ correctly.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
.. [#contrib1] Even though, these resources focus on open source projects and
|
|
232
|
-
communities, the general ideas behind collaborating with other developers
|
|
233
|
-
to collectively create software are general and can be applied to all sorts
|
|
234
|
-
of environments, including private companies and proprietary code bases.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
.. <-- start -->
|
|
238
|
-
|
|
239
|
-
.. |the repository service| replace:: GitHub
|
|
240
|
-
.. |contribute button| replace:: "Create pull request"
|
|
241
|
-
|
|
242
|
-
.. _repository: https://github.com/dkarv/bwt_api
|
|
243
|
-
.. _issue tracker: https://github.com/dkarv/bwt_api/issues
|
|
244
|
-
.. <-- end -->
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
.. |virtualenv| replace:: ``virtualenv``
|
|
248
|
-
.. |pre-commit| replace:: ``pre-commit``
|
|
249
|
-
.. |tox| replace:: ``tox``
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
.. _black: https://pypi.org/project/black/
|
|
253
|
-
.. _CommonMark: https://commonmark.org/
|
|
254
|
-
.. _contribution-guide.org: https://www.contribution-guide.org/
|
|
255
|
-
.. _creating a PR: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request
|
|
256
|
-
.. _descriptive commit message: https://chris.beams.io/posts/git-commit
|
|
257
|
-
.. _docstrings: https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html
|
|
258
|
-
.. _first-contributions tutorial: https://github.com/firstcontributions/first-contributions
|
|
259
|
-
.. _flake8: https://flake8.pycqa.org/en/stable/
|
|
260
|
-
.. _git: https://git-scm.com
|
|
261
|
-
.. _GitHub's fork and pull request workflow: https://guides.github.com/activities/forking/
|
|
262
|
-
.. _guide created by FreeCodeCamp: https://github.com/FreeCodeCamp/how-to-contribute-to-open-source
|
|
263
|
-
.. _Miniconda: https://docs.conda.io/en/latest/miniconda.html
|
|
264
|
-
.. _MyST: https://myst-parser.readthedocs.io/en/latest/syntax/syntax.html
|
|
265
|
-
.. _other kinds of contributions: https://opensource.guide/how-to-contribute
|
|
266
|
-
.. _pre-commit: https://pre-commit.com/
|
|
267
|
-
.. _PyPI: https://pypi.org/
|
|
268
|
-
.. _PyScaffold's contributor's guide: https://pyscaffold.org/en/stable/contributing.html
|
|
269
|
-
.. _Pytest can drop you: https://docs.pytest.org/en/stable/how-to/failures.html#using-python-library-pdb-with-pytest
|
|
270
|
-
.. _Python Software Foundation's Code of Conduct: https://www.python.org/psf/conduct/
|
|
271
|
-
.. _reStructuredText: https://www.sphinx-doc.org/en/master/usage/restructuredtext/
|
|
272
|
-
.. _Sphinx: https://www.sphinx-doc.org/en/master/
|
|
273
|
-
.. _tox: https://tox.wiki/en/stable/
|
|
274
|
-
.. _virtual environment: https://realpython.com/python-virtual-environments-a-primer/
|
|
275
|
-
.. _virtualenv: https://virtualenv.pypa.io/en/stable/
|
|
276
|
-
|
|
277
|
-
.. _GitHub web interface: https://docs.github.com/en/repositories/working-with-files/managing-files/editing-files
|
|
278
|
-
.. _GitHub's code editor: https://docs.github.com/en/repositories/working-with-files/managing-files/editing-files
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|