diff --git a/README.md b/README.md index 4d63a66..6711ca6 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,8 @@ sensor: - platform: airthings_wave scan_interval: 120 elevation: 998 + voltage_100: 3.2 + voltage_0: 2.2 ``` ### Optional Configuration Variables @@ -58,20 +60,28 @@ sensor: (float)(Optional) The current elevation in meters. Used to correct the pressure sensor to sea level conditions. +**voltage_100** + + (float)(Optional) The voltage for 100% battery, calculated linearly between voltage_0 and voltage_100 (on supported device), default is 3.2 + +**voltage_0** + + (float)(Optional) The voltage for 0% battery, calculated linearly between voltage_0 and voltage_100 (on supported device), default is 2.2 ## Limitations -It may be possible that the Wave must be connected to the official app at least -once before you can use this program, so you will probably not get around -registering an account with Airthings. +Users has reported that it is possible to get data without first registering with the official app, +so it should be possible to use the sensor with this integration without registering. The radon level history stored on the Wave itself cannot be accessed with this component. To get around this, it connects regularly to the radon detector. -Make sure you install the latest firmware on the device using the official app +It might be beneficial to install the latest firmware on the device using the official app first. +Battery level only works for the Airthings wave pluss device. + ## Known Issues * Not yet able to specify the `monitored_conditions` configuration diff --git a/custom_components/airthings_wave/airthings.py b/custom_components/airthings_wave/airthings.py index 98ca30f..693ac9b 100644 --- a/custom_components/airthings_wave/airthings.py +++ b/custom_components/airthings_wave/airthings.py @@ -12,6 +12,7 @@ _LOGGER = logging.getLogger(__name__) # Use full UUID since we do not use UUID from bluepy.btle +CHAR_UUID_CCCD = btle.UUID('2902') # Client Characteristic Configuration Descriptor (CCCD) CHAR_UUID_MANUFACTURER_NAME = UUID('00002a29-0000-1000-8000-00805f9b34fb') CHAR_UUID_SERIAL_NUMBER_STRING = UUID('00002a25-0000-1000-8000-00805f9b34fb') CHAR_UUID_MODEL_NUMBER_STRING = UUID('00002a24-0000-1000-8000-00805f9b34fb') @@ -25,6 +26,7 @@ CHAR_UUID_WAVE_PLUS_DATA = UUID('b42e2a68-ade7-11e4-89d3-123b93f75cba') CHAR_UUID_WAVE_2_DATA = UUID('b42e4dcc-ade7-11e4-89d3-123b93f75cba') CHAR_UUID_WAVEMINI_DATA = UUID('b42e3b98-ade7-11e4-89d3-123b93f75cba') +COMMAND_UUID = UUID('b42e2d06-ade7-11e4-89d3-123b93f75cba') # "Access Control Point" Characteristic Characteristic = namedtuple('Characteristic', ['uuid', 'name', 'format']) @@ -48,7 +50,8 @@ def __str__(self): sensors_characteristics_uuid = [CHAR_UUID_DATETIME, CHAR_UUID_TEMPERATURE, CHAR_UUID_HUMIDITY, CHAR_UUID_RADON_1DAYAVG, CHAR_UUID_RADON_LONG_TERM_AVG, CHAR_UUID_ILLUMINANCE_ACCELEROMETER, - CHAR_UUID_WAVE_PLUS_DATA,CHAR_UUID_WAVE_2_DATA,CHAR_UUID_WAVEMINI_DATA] + CHAR_UUID_WAVE_PLUS_DATA,CHAR_UUID_WAVE_2_DATA,CHAR_UUID_WAVEMINI_DATA, + COMMAND_UUID] sensors_characteristics_uuid_str = [str(x) for x in sensors_characteristics_uuid] @@ -127,6 +130,47 @@ def decode_data(self, raw_data): return data +class CommandDecode: + def __init__(self, name, format_type, cmd): + self.name = name + self.format_type = format_type + self.cmd = cmd + + def decode_data(self, raw_data): + if raw_data is None: + return {} + cmd = raw_data[0:1] + if cmd != self.cmd: + _LOGGER.warning("Result for Wrong command received, expected {} got {}".format(self.cmd.hex(), cmd.hex())) + return {} + + if len(raw_data[2:]) != struct.calcsize(self.format_type): + _LOGGER.debug("Wrong length data received ({}) verses expected ({})".format(len(cmd), struct.calcsize(self.format_type))) + return {} + val = struct.unpack( + self.format_type, + raw_data[2:]) + res = {} + res['illuminance'] = val[2] + #res['measurement_periods'] = val[5] + res['battery'] = val[17] / 1000.0 + + return res + + +class MyDelegate(btle.DefaultDelegate): + def __init__(self): + btle.DefaultDelegate.__init__(self) + # ... initialise here + self.data = None + + def handleNotification(self, cHandle, data): + if self.data is None: + self.data = data + else: + self.data = self.data + data + + sensor_decoders = {str(CHAR_UUID_WAVE_PLUS_DATA):WavePlussDecode(name="Pluss", format_type='BBBBHHHHHHHH', scale=0), str(CHAR_UUID_DATETIME):WaveDecodeDate(name="date_time", format_type='HBBBBB', scale=0), str(CHAR_UUID_HUMIDITY):BaseDecode(name="humidity", format_type='H', scale=1.0/100.0), @@ -137,6 +181,8 @@ def decode_data(self, raw_data): str(CHAR_UUID_WAVE_2_DATA):Wave2Decode(name="Wave2", format_type='<4B8H', scale=1.0), str(CHAR_UUID_WAVEMINI_DATA):WaveMiniDecode(name="WaveMini", format_type='