Skip to content

Commit 496be11

Browse files
authored
Merge pull request #1321 from custom-components/Garnet-709BT
Add Garnet 709-BTP3 SeeLeveL II Tank Monitor
2 parents a219308 + 1ac8e93 commit 496be11

File tree

7 files changed

+240
-9
lines changed

7 files changed

+240
-9
lines changed

custom_components/ble_monitor/ble_parser/oras.py

+64-8
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,87 @@
1-
"""Parser for Oras BLE advertisements."""
1+
"""Parser for Oras/Garnet BLE advertisements."""
22
import logging
33

44
from .helpers import to_mac, to_unformatted_mac
55

66
_LOGGER = logging.getLogger(__name__)
77

8+
SENSOR_TYPE = {
9+
0: "fresh tank",
10+
1: "black tank",
11+
2: "grey tank",
12+
3: "lpg tank",
13+
4: "lpg tank 2",
14+
5: "galley tank",
15+
6: "galley tank 2",
16+
7: "temperature",
17+
8: "temperature probe 2",
18+
9: "temperature probe 3",
19+
10: "temperature probe 4",
20+
11: "chemical tank",
21+
12: "chemical tank 2",
22+
13: "voltage",
23+
}
24+
825

926
def parse_oras(self, data: bytes, mac: str):
10-
"""Parser for Oras toothbrush."""
27+
"""Parser for Oras toothbrush or Garnet tank."""
1128
msg_length = len(data)
12-
firmware = "Oras"
13-
result = {"firmware": firmware}
14-
if msg_length == 22:
29+
result = {"mac": to_unformatted_mac(mac)}
30+
31+
if msg_length == 18:
32+
firmware = "Garnet"
33+
device_type = "SeeLevel II 709-BTP3"
34+
35+
sensor_id = data[7]
36+
try:
37+
sensor_type = SENSOR_TYPE[sensor_id]
38+
except ValueError:
39+
return None
40+
41+
try:
42+
sensor_data = int(data[8:11].decode("ASCII"))
43+
except ValueError:
44+
error_code = data[8:11].decode("ASCII")
45+
_LOGGER.error(
46+
"Garnet SeeLevel II 709-BTP3 is reporting error %s for sensor %s",
47+
error_code,
48+
sensor_type
49+
)
50+
return None
51+
52+
if sensor_id == 13:
53+
sensor_data /= 10
54+
55+
sensor_volume = data[11:14].decode("ASCII")
56+
sensor_total = data[14:17].decode("ASCII")
57+
sensor_alarm = int(chr(data[17]))
58+
59+
result.update({
60+
sensor_type: sensor_data,
61+
"problem": sensor_alarm,
62+
})
63+
_LOGGER.debug(
64+
"BLE ADV from Garnet DEVICE: %s, result: %s, not implemented volume %s, total %s",
65+
device_type,
66+
result,
67+
sensor_volume,
68+
sensor_total
69+
)
70+
elif msg_length == 22:
71+
firmware = "Oras"
1572
device_type = "Electra Washbasin Faucet"
1673
battery = data[5]
1774
result.update({"battery": battery})
1875
else:
19-
if self.report_unknown == "Oras":
76+
if self.report_unknown in ["Oras", "Garnet"]:
2077
_LOGGER.info(
21-
"BLE ADV from UNKNOWN Oras DEVICE: MAC: %s, ADV: %s",
78+
"BLE ADV from UNKNOWN Oras/Garnet DEVICE: MAC: %s, ADV: %s",
2279
to_mac(mac),
2380
data.hex()
2481
)
2582
return None
2683

2784
result.update({
28-
"mac": to_unformatted_mac(mac),
2985
"type": device_type,
3086
"packet": "no packet id",
3187
"firmware": firmware,

custom_components/ble_monitor/const.py

+119
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,114 @@ class BLEMonitorBinarySensorEntityDescription(
10381038
suggested_display_precision=1,
10391039
state_class=SensorStateClass.MEASUREMENT,
10401040
),
1041+
BLEMonitorSensorEntityDescription(
1042+
key="fresh tank",
1043+
sensor_class="MeasuringSensor",
1044+
update_behavior="Averaging",
1045+
name="fresh tank",
1046+
unique_id="fresh_tank_",
1047+
icon="mdi:storage-tank-outline",
1048+
native_unit_of_measurement=PERCENTAGE,
1049+
device_class=None,
1050+
suggested_display_precision=0,
1051+
state_class=SensorStateClass.MEASUREMENT,
1052+
),
1053+
BLEMonitorSensorEntityDescription(
1054+
key="black tank",
1055+
sensor_class="MeasuringSensor",
1056+
update_behavior="Averaging",
1057+
name="black tank",
1058+
unique_id="black_tank_",
1059+
icon="mdi:storage-tank-outline",
1060+
native_unit_of_measurement=PERCENTAGE,
1061+
device_class=None,
1062+
suggested_display_precision=0,
1063+
state_class=SensorStateClass.MEASUREMENT,
1064+
),
1065+
BLEMonitorSensorEntityDescription(
1066+
key="grey tank",
1067+
sensor_class="MeasuringSensor",
1068+
update_behavior="Averaging",
1069+
name="grey tank",
1070+
unique_id="grey_tank_",
1071+
icon="mdi:storage-tank-outline",
1072+
native_unit_of_measurement=PERCENTAGE,
1073+
device_class=None,
1074+
suggested_display_precision=0,
1075+
state_class=SensorStateClass.MEASUREMENT,
1076+
),
1077+
BLEMonitorSensorEntityDescription(
1078+
key="chemical tank",
1079+
sensor_class="MeasuringSensor",
1080+
update_behavior="Averaging",
1081+
name="chemical tank",
1082+
unique_id="chemical_tank_",
1083+
icon="mdi:chemical-weapon",
1084+
native_unit_of_measurement=PERCENTAGE,
1085+
device_class=None,
1086+
suggested_display_precision=0,
1087+
state_class=SensorStateClass.MEASUREMENT,
1088+
),
1089+
BLEMonitorSensorEntityDescription(
1090+
key="chemical tank 2",
1091+
sensor_class="MeasuringSensor",
1092+
update_behavior="Averaging",
1093+
name="chemical tank 2",
1094+
unique_id="chemical_tank_2_",
1095+
icon="mdi:chemical-weapon",
1096+
native_unit_of_measurement=PERCENTAGE,
1097+
device_class=None,
1098+
suggested_display_precision=0,
1099+
state_class=SensorStateClass.MEASUREMENT,
1100+
),
1101+
BLEMonitorSensorEntityDescription(
1102+
key="lpg tank",
1103+
sensor_class="MeasuringSensor",
1104+
update_behavior="Averaging",
1105+
name="lpg tank",
1106+
unique_id="lpg_tank_",
1107+
icon="mdi:gas-cylinder",
1108+
native_unit_of_measurement=PERCENTAGE,
1109+
device_class=None,
1110+
suggested_display_precision=0,
1111+
state_class=SensorStateClass.MEASUREMENT,
1112+
),
1113+
BLEMonitorSensorEntityDescription(
1114+
key="lpg tank 2",
1115+
sensor_class="MeasuringSensor",
1116+
update_behavior="Averaging",
1117+
name="lpg tank 2",
1118+
unique_id="lpg_tank_2_",
1119+
icon="mdi:gas-cylinder",
1120+
native_unit_of_measurement=PERCENTAGE,
1121+
device_class=None,
1122+
suggested_display_precision=0,
1123+
state_class=SensorStateClass.MEASUREMENT,
1124+
),
1125+
BLEMonitorSensorEntityDescription(
1126+
key="galley tank",
1127+
sensor_class="MeasuringSensor",
1128+
update_behavior="Averaging",
1129+
name="galley tank",
1130+
unique_id="galley_tank_",
1131+
icon="mdi:storage-tank-outline",
1132+
native_unit_of_measurement=PERCENTAGE,
1133+
device_class=None,
1134+
suggested_display_precision=0,
1135+
state_class=SensorStateClass.MEASUREMENT,
1136+
),
1137+
BLEMonitorSensorEntityDescription(
1138+
key="galley tank 2",
1139+
sensor_class="MeasuringSensor",
1140+
update_behavior="Averaging",
1141+
name="galley tank 2",
1142+
unique_id="galley_tank_2_",
1143+
icon="mdi:storage-tank-outline",
1144+
native_unit_of_measurement=PERCENTAGE,
1145+
device_class=None,
1146+
suggested_display_precision=0,
1147+
state_class=SensorStateClass.MEASUREMENT,
1148+
),
10411149
BLEMonitorSensorEntityDescription(
10421150
key="aqi",
10431151
sensor_class="MeasuringSensor",
@@ -1922,6 +2030,7 @@ class BLEMonitorBinarySensorEntityDescription(
19222030
'Blustream' : 'Blustream',
19232031
'BTHome' : 'BTHome',
19242032
'CQ60' : 'Chef iQ',
2033+
'SeeLevel II 709-BTP3' : 'Garnet Instruments Ltd',
19252034
'MI401' : 'Grundfos',
19262035
'HHCCJCY10' : 'HHCC',
19272036
'HolyIOT BLE tracker' : 'HolyIOT',
@@ -1999,7 +2108,10 @@ class BLEMonitorBinarySensorEntityDescription(
19992108
"acceleration",
20002109
"ambient temperature",
20012110
"battery",
2111+
"black tank",
20022112
"button",
2113+
"chemical tank",
2114+
"chemical tank 2",
20032115
"co2",
20042116
"conductivity",
20052117
"count",
@@ -2011,12 +2123,18 @@ class BLEMonitorBinarySensorEntityDescription(
20112123
"duration",
20122124
"energy",
20132125
"flow",
2126+
"fresh tank",
2127+
"galley tank",
2128+
"galley tank 2",
20142129
"gas",
20152130
"gravity",
2131+
"grey tank",
20162132
"gyroscope",
20172133
"humidity",
20182134
"illuminance",
20192135
"impedance",
2136+
"lpg tank",
2137+
"lpg tank 2",
20202138
"meat temperature",
20212139
"moisture",
20222140
"movement counter",
@@ -2067,6 +2185,7 @@ class BLEMonitorBinarySensorEntityDescription(
20672185
"Blustream",
20682186
"BTHome",
20692187
'Chef iQ',
2188+
"Garnet",
20702189
"Govee",
20712190
"Grundfos",
20722191
"HHCC",

custom_components/ble_monitor/manifest.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414
"btsocket>=0.2.0",
1515
"pyric>=0.1.6.3"
1616
],
17-
"version": "12.9.3"
17+
"version": "12.10.0"
1818
}

custom_components/ble_monitor/sensor.py

+1
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ class BaseSensor(RestoreSensor, SensorEntity):
337337
# | |**flow
338338
# | |**gas
339339
# | |**water
340+
# | |**fresh/grey/black/lpg/galley/chemical tank
340341
# |--InstantUpdateSensor (Class)
341342
# | |**consumable
342343
# | |**heart rate

custom_components/ble_monitor/test/test_oras.py

+34
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,37 @@ def test_oras_faucet(self):
1919
assert sensor_msg["data"]
2020
assert sensor_msg["battery"] == 100
2121
assert sensor_msg["rssi"] == -52
22+
23+
def test_garnet_battery(self):
24+
"""Test Oras parser for Garnet 709BT battery sensor."""
25+
data_string = "043E2102010000adb9a538c1a41502010611ff31010c464e0d31333230303030303030CC"
26+
data = bytes(bytearray.fromhex(data_string))
27+
# pylint: disable=unused-variable
28+
ble_parser = BleParser()
29+
sensor_msg, tracker_msg = ble_parser.parse_raw_data(data)
30+
31+
assert sensor_msg["firmware"] == "Garnet"
32+
assert sensor_msg["type"] == "SeeLevel II 709-BTP3"
33+
assert sensor_msg["mac"] == "A4C138A5B9AD"
34+
assert sensor_msg["packet"] == "no packet id"
35+
assert sensor_msg["data"]
36+
assert sensor_msg["voltage"] == 13.2
37+
assert sensor_msg["problem"] == 0
38+
assert sensor_msg["rssi"] == -52
39+
40+
def test_garnet_black_tank(self):
41+
"""Test Oras parser for Garnet 709BT black tank sensor."""
42+
data_string = "043E2102010000adb9a538c1a41502010611ff31010c464e0120373130303030303030CC"
43+
data = bytes(bytearray.fromhex(data_string))
44+
# pylint: disable=unused-variable
45+
ble_parser = BleParser()
46+
sensor_msg, tracker_msg = ble_parser.parse_raw_data(data)
47+
48+
assert sensor_msg["firmware"] == "Garnet"
49+
assert sensor_msg["type"] == "SeeLevel II 709-BTP3"
50+
assert sensor_msg["mac"] == "A4C138A5B9AD"
51+
assert sensor_msg["packet"] == "no packet id"
52+
assert sensor_msg["data"]
53+
assert sensor_msg["black tank"] == 71
54+
assert sensor_msg["problem"] == 0
55+
assert sensor_msg["rssi"] == -52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
manufacturer: Garnet
3+
name: SeeLeveL II 709-BTP3 Tank Monitor
4+
model: SeeLevel II 709-BTP3
5+
image: Garnet_Seelevel_709-BTP3.png
6+
physical_description: Tank Monitor
7+
broadcasted_properties:
8+
- tank
9+
- temperature
10+
- voltage
11+
- rssi
12+
broadcasted_property_notes:
13+
- property: tank
14+
note: Level of the tank in percentage of the total volume
15+
broadcast_rate:
16+
active_scan:
17+
encryption_key: false
18+
custom_firmware:
19+
notes:
20+
- The sensor also broadcasts volume and a total (per tank). These sensors are currently not implemented, as in tests these values stay 0 all the time. If you want to debug these sensors, you can make them visible by enabling debug logging. The values will be logged in the HA logs. Please report back here if these sensors actually report anything, such that we can implement them.
21+
---
31.9 KB
Loading

0 commit comments

Comments
 (0)