Repository: Bluetooth-Devices/oralb-ble (language: python)

Issue:
Genius 8000 Support
As per https://github.com/home-assistant/core/issues/81594

I have a Genius 8000, which is not on the supported list according to the documentation, however, I can confirm it works with some missing info. It's also showing as a smarter series 7000.

**AdvertisementData**
`2022-11-06 08:02:34.946 DEBUG (MainThread) [homeassistant.components.bluetooth.manager] 00:1A:7D:DA:71:15: 58:7A:62:41:59:7F AdvertisementData(local_name='Oral-B Toothbrush', manufacturer_data={220: b'\x03"\x0c\x02 \x00\x00\x01\x01\x00\x04'}, rssi=-77) connectable: True match: {'oralb'} rssi: -77`

`2022-11-06 08:03:17.402 DEBUG (MainThread) [homeassistant.components.bluetooth.manager] 00:1A:7D:DA:71:15: 58:7A:62:41:59:7F AdvertisementData(local_name='Oral-B Toothbrush', manufacturer_data={220: b'\x03"\x0c\x02 \x00\r\x03\x01+\x04'}, rssi=-76) connectable: True match: set() rssi: -76`

`2022-11-06 11:20:05.828 DEBUG (MainThread) [homeassistant.components.bluetooth.manager] 00:1A:7D:DA:71:15: 58:7A:62:41:59:7F AdvertisementData(local_name='Oral-B Toothbrush', manufacturer_data={220: b'\x03"\x0c\x02 \x00\x00\x03\x01\x00\x04'}, rssi=-73) connectable: True match: set() rssi: -73`

`2022-11-06 11:20:28.476 DEBUG (MainThread) [homeassistant.components.bluetooth.manager] 00:1A:7D:DA:71:15: 58:7A:62:41:59:7F AdvertisementData(local_name='Oral-B Toothbrush', manufacturer_data={220: b'\x03"\x0c\x03\x00\x00\x0b\x07\x01$\x04'}, rssi=-80) connectable: True match: set() rssi: -80`

`2022-11-06 11:21:10.786 DEBUG (MainThread) [homeassistant.components.bluetooth.manager] 00:1A:7D:DA:71:15: 58:7A:62:41:59:7F AdvertisementData(local_name='Oral-B Toothbrush', manufacturer_data={220: b'\x03"\x0c\x03\x00\x005\x07\x02L\x04'}, rssi=-76) connectable: True match: set() rssi: -76`

`2022-11-06 11:22:32.132 DEBUG (MainThread) [homeassistant.components.bluetooth.manager] 00:1A:7D:DA:71:15: 58:7A:62:41:59:7F AdvertisementData(local_name='Oral-B Toothbrush', manufacturer_data={220: b'\x03"\x0c\x02 \x02\x03\x07\x0f\n\x04'}, rssi=-76) connectable: True match: set() rssi: -76`




Discussion / hints:
What value does pressure report when you have normal and high pressure ?
I'm struggling to figure out how to identify this to be honest.

This was when I turned it on, so perhaps normal?
`2022-11-06 08:02:34.946 DEBUG (MainThread) [homeassistant.components.bluetooth.manager] 00:1A:7D:DA:71:15: 58:7A:62:41:59:7F AdvertisementData(local_name='Oral-B Toothbrush', manufacturer_data={220: b'\x03"\x0c\x02 \x00\x00\x01\x01\x00\x04'}, rssi=-77) connectable: True match: {'oralb'} rssi: -77`

After that I was pushing down, so I would assume this is high pressure.  
`2022-11-06 08:03:17.402 DEBUG (MainThread) [homeassistant.components.bluetooth.manager] 00:1A:7D:DA:71:15: 58:7A:62:41:59:7F AdvertisementData(local_name='Oral-B Toothbrush', manufacturer_data={220: b'\x03"\x0c\x02 \x00\r\x03\x01+\x04'}, rssi=-76) connectable: True match: set() rssi: -76`



Fix patch:
diff --git a/src/oralb_ble/parser.py b/src/oralb_ble/parser.py
index ceb866b..b4781f1 100644
--- a/src/oralb_ble/parser.py
+++ b/src/oralb_ble/parser.py
@@ -45,6 +45,7 @@ class Models(Enum):
     SmartSeries4000 = auto()
     SmartSeries6000 = auto()
     SmartSeries7000 = auto()
+    SmartSeries8000 = auto()
     SmartSeries9000 = auto()
 
 
@@ -109,6 +110,10 @@ class ModelDescription:
         device_type="Smart Series 7000",
         modes=SMART_SERIES_MODES,
     ),
+    Models.SmartSeries8000: ModelDescription(
+        device_type="Smart Series 8000",
+        modes=SMART_SERIES_MODES,
+    ),
     Models.SmartSeries9000: ModelDescription(
         device_type="Smart Series 9000",
         modes=SMART_SERIES_MODES,
@@ -159,6 +164,7 @@ class ModelDescription:
     b"\x074\x0c": Models.IOSeries4,
     b"\x03V\x04": Models.SmartSeries4000,
     b"\x04'\r": Models.SmartSeries6000,
+    b'\x03"\x0c': Models.SmartSeries8000,
     b"\x03!\x0c": Models.SmartSeries9000,
     b"\x061\x16": Models.IOSeries9,
     b"\x02\x02\x06": Models.TriumphV2,


Test patch:
diff --git a/tests/test_parser.py b/tests/test_parser.py
index eac01d4..73b4d2f 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -152,6 +152,24 @@
     service_data={},
     source="local",
 )
+GENIUS_8000 = BluetoothServiceInfo(
+    address="78:DB:2F:C2:48:BE",
+    rssi=-63,
+    name="GENIUS8000",
+    manufacturer_data={220: b'\x03"\x0c\x02 \x00\x00\x07\x0f\x00\x04'},
+    service_uuids=[],
+    service_data={},
+    source="local",
+)
+GENIUS_8000_HIGH_PRESSURE = BluetoothServiceInfo(
+    address="78:DB:2F:C2:48:BE",
+    rssi=-63,
+    name="GENIUS8000",
+    manufacturer_data={220: b'\x03"\x0c\x03\xc0\x010\x07\x04<\x04'},
+    service_uuids=[],
+    service_data={},
+    source="local",
+)
 
 
 def test_can_create():
@@ -1861,3 +1879,233 @@ def test_smart_series_6000_daily_clean_mode_normal_pressure():
         },
         events={},
     )
+
+
+def test_genius_8000():
+    parser = OralBBluetoothDeviceData()
+    service_info = GENIUS_8000
+    result = parser.update(service_info)
+    assert result == SensorUpdate(
+        title="Smart Series 8000 48BE",
+        devices={
+            None: SensorDeviceInfo(
+                name="Smart Series 8000 48BE",
+                model="Smart Series 8000",
+                manufacturer="Oral-B",
+                sw_version=None,
+                hw_version=None,
+            )
+        },
+        entity_descriptions={
+            DeviceKey(key="number_of_sectors", device_id=None): SensorDescription(
+                device_key=DeviceKey(key="number_of_sectors", device_id=None),
+                device_class=None,
+                native_unit_of_measurement=None,
+            ),
+            DeviceKey(key="signal_strength", device_id=None): SensorDescription(
+                device_key=DeviceKey(key="signal_strength", device_id=None),
+                device_class=SensorDeviceClass.SIGNAL_STRENGTH,
+                native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
+            ),
+            DeviceKey(key="sector_timer", device_id=None): SensorDescription(
+                device_key=DeviceKey(key="sector_timer", device_id=None),
+                device_class=None,
+                native_unit_of_measurement=None,
+            ),
+            DeviceKey(key="time", device_id=None): SensorDescription(
+                device_key=DeviceKey(key="time", device_id=None),
+                device_class=None,
+                native_unit_of_measurement=None,
+            ),
+            DeviceKey(key="toothbrush_state", device_id=None): SensorDescription(
+                device_key=DeviceKey(key="toothbrush_state", device_id=None),
+                device_class=None,
+                native_unit_of_measurement=None,
+            ),
+            DeviceKey(key="mode", device_id=None): SensorDescription(
+                device_key=DeviceKey(key="mode", device_id=None),
+                device_class=None,
+                native_unit_of_measurement=None,
+            ),
+            DeviceKey(key="pressure", device_id=None): SensorDescription(
+                device_key=DeviceKey(key="pressure", device_id=None),
+                device_class=None,
+                native_unit_of_measurement=None,
+            ),
+            DeviceKey(key="sector", device_id=None): SensorDescription(
+                device_key=DeviceKey(key="sector", device_id=None),
+                device_class=None,
+                native_unit_of_measurement=None,
+            ),
+        },
+        entity_values={
+            DeviceKey(key="number_of_sectors", device_id=None): SensorValue(
+                device_key=DeviceKey(key="number_of_sectors", device_id=None),
+                name="Number " "of " "sectors",
+                native_value=4,
+            ),
+            DeviceKey(key="signal_strength", device_id=None): SensorValue(
+                device_key=DeviceKey(key="signal_strength", device_id=None),
+                name="Signal " "Strength",
+                native_value=-63,
+            ),
+            DeviceKey(key="sector_timer", device_id=None): SensorValue(
+                device_key=DeviceKey(key="sector_timer", device_id=None),
+                name="Sector " "Timer",
+                native_value=0,
+            ),
+            DeviceKey(key="time", device_id=None): SensorValue(
+                device_key=DeviceKey(key="time", device_id=None),
+                name="Time",
+                native_value=0,
+            ),
+            DeviceKey(key="toothbrush_state", device_id=None): SensorValue(
+                device_key=DeviceKey(key="toothbrush_state", device_id=None),
+                name="Toothbrush " "State",
+                native_value="idle",
+            ),
+            DeviceKey(key="mode", device_id=None): SensorValue(
+                device_key=DeviceKey(key="mode", device_id=None),
+                name="Mode",
+                native_value="turbo",
+            ),
+            DeviceKey(key="pressure", device_id=None): SensorValue(
+                device_key=DeviceKey(key="pressure", device_id=None),
+                name="Pressure",
+                native_value="normal",
+            ),
+            DeviceKey(key="sector", device_id=None): SensorValue(
+                device_key=DeviceKey(key="sector", device_id=None),
+                name="Sector",
+                native_value="sector " "15",
+            ),
+        },
+        binary_entity_descriptions={
+            DeviceKey(key="brushing", device_id=None): BinarySensorDescription(
+                device_key=DeviceKey(key="brushing", device_id=None), device_class=None
+            )
+        },
+        binary_entity_values={
+            DeviceKey(key="brushing", device_id=None): BinarySensorValue(
+                device_key=DeviceKey(key="brushing", device_id=None),
+                name="Brushing",
+                native_value=False,
+            )
+        },
+        events={},
+    )
+
+
+def test_genius_8000_high_pressure():
+    parser = OralBBluetoothDeviceData()
+    service_info = GENIUS_8000_HIGH_PRESSURE
+    result = parser.update(service_info)
+    assert result == SensorUpdate(
+        title="Smart Series 8000 48BE",
+        devices={
+            None: SensorDeviceInfo(
+                name="Smart Series 8000 48BE",
+                model="Smart Series 8000",
+                manufacturer="Oral-B",
+                sw_version=None,
+                hw_version=None,
+            )
+        },
+        entity_descriptions={
+            DeviceKey(key="pressure", device_id=None): SensorDescription(
+                device_key=DeviceKey(key="pressure", device_id=None),
+                device_class=None,
+                native_unit_of_measurement=None,
+            ),
+            DeviceKey(key="toothbrush_state", device_id=None): SensorDescription(
+                device_key=DeviceKey(key="toothbrush_state", device_id=None),
+                device_class=None,
+                native_unit_of_measurement=None,
+            ),
+            DeviceKey(key="signal_strength", device_id=None): SensorDescription(
+                device_key=DeviceKey(key="signal_strength", device_id=None),
+                device_class=SensorDeviceClass.SIGNAL_STRENGTH,
+                native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
+            ),
+            DeviceKey(key="sector_timer", device_id=None): SensorDescription(
+                device_key=DeviceKey(key="sector_timer", device_id=None),
+                device_class=None,
+                native_unit_of_measurement=None,
+            ),
+            DeviceKey(key="mode", device_id=None): SensorDescription(
+                device_key=DeviceKey(key="mode", device_id=None),
+                device_class=None,
+                native_unit_of_measurement=None,
+            ),
+            DeviceKey(key="sector", device_id=None): SensorDescription(
+                device_key=DeviceKey(key="sector", device_id=None),
+                device_class=None,
+                native_unit_of_measurement=None,
+            ),
+            DeviceKey(key="time", device_id=None): SensorDescription(
+                device_key=DeviceKey(key="time", device_id=None),
+                device_class=None,
+                native_unit_of_measurement=None,
+            ),
+            DeviceKey(key="number_of_sectors", device_id=None): SensorDescription(
+                device_key=DeviceKey(key="number_of_sectors", device_id=None),
+                device_class=None,
+                native_unit_of_measurement=None,
+            ),
+        },
+        entity_values={
+            DeviceKey(key="pressure", device_id=None): SensorValue(
+                device_key=DeviceKey(key="pressure", device_id=None),
+                name="Pressure",
+                native_value="high",
+            ),
+            DeviceKey(key="toothbrush_state", device_id=None): SensorValue(
+                device_key=DeviceKey(key="toothbrush_state", device_id=None),
+                name="Toothbrush " "State",
+                native_value="running",
+            ),
+            DeviceKey(key="signal_strength", device_id=None): SensorValue(
+                device_key=DeviceKey(key="signal_strength", device_id=None),
+                name="Signal " "Strength",
+                native_value=-63,
+            ),
+            DeviceKey(key="sector_timer", device_id=None): SensorValue(
+                device_key=DeviceKey(key="sector_timer", device_id=None),
+                name="Sector " "Timer",
+                native_value=60,
+            ),
+            DeviceKey(key="mode", device_id=None): SensorValue(
+                device_key=DeviceKey(key="mode", device_id=None),
+                name="Mode",
+                native_value="turbo",
+            ),
+            DeviceKey(key="sector", device_id=None): SensorValue(
+                device_key=DeviceKey(key="sector", device_id=None),
+                name="Sector",
+                native_value="sector " "4",
+            ),
+            DeviceKey(key="time", device_id=None): SensorValue(
+                device_key=DeviceKey(key="time", device_id=None),
+                name="Time",
+                native_value=108,
+            ),
+            DeviceKey(key="number_of_sectors", device_id=None): SensorValue(
+                device_key=DeviceKey(key="number_of_sectors", device_id=None),
+                name="Number " "of " "sectors",
+                native_value=4,
+            ),
+        },
+        binary_entity_descriptions={
+            DeviceKey(key="brushing", device_id=None): BinarySensorDescription(
+                device_key=DeviceKey(key="brushing", device_id=None), device_class=None
+            )
+        },
+        binary_entity_values={
+            DeviceKey(key="brushing", device_id=None): BinarySensorValue(
+                device_key=DeviceKey(key="brushing", device_id=None),
+                name="Brushing",
+                native_value=True,
+            )
+        },
+        events={},
+    )