Skip to content

Commit

Permalink
voltage_bm2: initial support
Browse files Browse the repository at this point in the history
  • Loading branch information
devbis committed May 21, 2023
1 parent 933ea1b commit 98cad3d
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 0 deletions.
1 change: 1 addition & 0 deletions ble2mqtt/devices/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .presence import Presence # noqa: F401
from .qingping_cgdk2 import QingpingTempRHMonitorLite # noqa: F401
from .thermostat_ensto import EnstoThermostat # noqa: F401
from .voltage_bm2 import VoltageTesterBM2 # noqa: F401
from .vson_air_wp6003 import VsonWP6003 # noqa: F401
from .xiaomi_ht import XiaomiHumidityTemperatureV1 # noqa: F401
from .xiaomi_lywsd03 import XiaomiHumidityTemperatureLYWSD # noqa: F401
Expand Down
56 changes: 56 additions & 0 deletions ble2mqtt/devices/voltage_bm2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from dataclasses import dataclass

from ..devices.base import SENSOR_DOMAIN, Sensor, SubscribeAndSetDataMixin, ConnectionMode

UUID_KEY_READ = "0000fff4-0000-1000-8000-00805f9b34fb"
KEY = b"\x6c\x65\x61\x67\x65\x6e\x64\xff\xfe\x31\x38\x38\x32\x34\x36\x36"


def create_aes():
try:
from Crypto.Cipher import AES
except ImportError:
raise ImportError(
"Please install pycryptodome to setup BM2 Voltage meter",
) from None

return AES.new(KEY, AES.MODE_CBC, bytes([0] * 16))


@dataclass
class SensorState:
voltage: float

@classmethod
def from_data(cls, decrypted_data: bytes):
voltage = (int.from_bytes(
decrypted_data[1:1 + 2],
byteorder='big',
) >> 4) / 100
return cls(voltage=round(voltage, 2))


class VoltageTesterBM2(SubscribeAndSetDataMixin, Sensor):
NAME = 'voltage_bm2'
DATA_CHAR = UUID_KEY_READ
MANUFACTURER = 'BM2'
SENSOR_CLASS = SensorState
REQUIRED_VALUES = ('voltage', )
ACTIVE_CONNECTION_MODE = ConnectionMode.ACTIVE_POLL_WITH_DISCONNECT

@property
def entities(self):
return {
SENSOR_DOMAIN: [
{
'name': 'voltage',
'device_class': 'voltage',
'unit_of_measurement': 'V',
},
],
}

def process_data(self, data: bytearray):
decrypted_data = create_aes().decrypt(data)
if decrypted_data[0] == 0xf5:
self._state = self.SENSOR_CLASS.from_data(decrypted_data)
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
aio-mqtt-mod>=0.2.0
bleak>=0.12.0
pycryptodome
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
'aio-mqtt-mod>=0.3.0',
'bleak>=0.12.0',
],
extras_require={
'full': ['pycryptodome']
},
classifiers=[
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
Expand Down

0 comments on commit 98cad3d

Please sign in to comment.