Skip to content

Commit b9a1559

Browse files
authored
Merge pull request #10 from soldierkam/fix_ha_integration
Fix HA integration
2 parents b7f4f3c + e8a6559 commit b9a1559

File tree

1 file changed

+51
-68
lines changed

1 file changed

+51
-68
lines changed

cometblue_lite/cometblue.py

Lines changed: 51 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,30 @@
55
Parts were taken from the cometblue module by im-0
66
Port to bleak/asyncio is based on pySwitchbot
77
"""
8+
from __future__ import annotations
9+
10+
import asyncio
811
import logging
912
import struct
10-
import time
11-
from contextlib import contextmanager
1213

13-
import asyncio
14-
from bleak import BleakError, BleakScanner
14+
from bleak import BleakError
15+
from bleak import BleakClient
1516
from bleak.backends.device import BLEDevice
16-
from bleak.backends.service import BleakGATTCharacteristic, BleakGATTServiceCollection
17-
from bleak_retry_connector import (
18-
BleakClientWithServiceCache,
19-
BleakNotFoundError,
20-
ble_device_has_changed,
21-
establish_connection,
22-
)
17+
from bleak_retry_connector import establish_connection
2318

2419
_LOGGER = logging.getLogger(__name__)
2520

26-
2721
PASSWORD_CHAR = "47e9ee30-47e9-11e4-8939-164230d1df67"
2822
TEMPERATURE_CHAR = "47e9ee2b-47e9-11e4-8939-164230d1df67"
2923
BATTERY_CHAR = "47e9ee2c-47e9-11e4-8939-164230d1df67"
3024
STATUS_CHAR = "47e9ee2a-47e9-11e4-8939-164230d1df67"
3125
DATETIME_CHAR = "47e9ee01-47e9-11e4-8939-164230d1df67"
32-
SOFTWARE_REV = "00002a28-0000-1000-8000-00805f9b34fb" # software_revision (0.0.6-sygonix1)
33-
MODEL_CHAR = "00002a24-0000-1000-8000-00805f9b34fb" # model_number (Comet Blue)
26+
SOFTWARE_REV = "00002a28-0000-1000-8000-00805f9b34fb" # software_revision (0.0.6-sygonix1)
27+
MODEL_CHAR = "00002a24-0000-1000-8000-00805f9b34fb" # model_number (Comet Blue)
3428
MANUFACTURER_CHAR = "00002a29-0000-1000-8000-00805f9b34fb" # manufacturer_name (EUROtronic GmbH)
35-
#FIRMWARE_CHAR = "00002a26-0000-1000-8000-00805f9b34fb"
29+
# FIRMWARE_CHAR = "00002a26-0000-1000-8000-00805f9b34fb"
3630

37-
FIRMWARE_CHAR = "47e9ee2d-47e9-11e4-8939-164230d1df67" # firmware_revision2 (COBL0126)
31+
FIRMWARE_CHAR = "47e9ee2d-47e9-11e4-8939-164230d1df67" # firmware_revision2 (COBL0126)
3832
_PIN_STRUCT_PACKING = '<I'
3933
_DATETIME_STRUCT_PACKING = '<BBBBB'
4034
_DAY_STRUCT_PACKING = '<BBBBBBBB'
@@ -55,7 +49,7 @@ def _encode_datetime(dt):
5549

5650
class CometBlueStates:
5751
"""CometBlue Thermostat States"""
58-
TEMPERATURE_OFF = 7.5 # special temperature, valve fully closed
52+
TEMPERATURE_OFF = 7.5 # special temperature, valve fully closed
5953
_TEMPERATURES_STRUCT_PACKING = '<bbbbbbb'
6054
_STATUS_STRUCT_PACKING = '<BBB'
6155

@@ -82,7 +76,12 @@ def __init__(self):
8276
self._status = dict()
8377
self._battery_level = None
8478
self._current_temp = None
85-
self.clear_temperatures()
79+
self.target_temperature = None
80+
self.target_temp_l = None
81+
self.target_temp_h = None
82+
self.offset_temperature = None
83+
self.window_open_detection = None
84+
self.window_open_minutes = None
8685

8786
def clear_temperatures(self):
8887
self.target_temperature = None
@@ -170,7 +169,6 @@ def encode_status(value):
170169
@status_code.setter
171170
def status_code(self, val):
172171
def decode_status(value):
173-
state_bytes = struct.unpack(CometBlueStates._STATUS_STRUCT_PACKING, value)
174172
state_dword = struct.unpack('<I', value + b'\x00')[0]
175173

176174
report = {}
@@ -245,18 +243,17 @@ def temperatures(self, value):
245243
@property
246244
def all_temperatures_none(self):
247245
"""True if any of the temperature properties is not None"""
248-
values = set((self.target_temperature, self.target_temp_l, self.target_temp_h, self.offset_temperature, self.window_open_detection, self.window_open_minutes))
246+
values = {self.target_temperature, self.target_temp_l, self.target_temp_h, self.offset_temperature, self.window_open_detection,
247+
self.window_open_minutes}
249248
values.remove(None)
250249
return len(values) == 0
251250

252251

253-
254252
class CometBlue:
255253
"""CometBlue Thermostat """
256254

257-
def __init__(self, address, pin):
255+
def __init__(self, pin):
258256
super(CometBlue, self).__init__()
259-
self._address = address
260257
self._device: BLEDevice | None = None
261258
self._pin = pin
262259
self.available = False
@@ -265,20 +262,18 @@ def __init__(self, address, pin):
265262
self._target = CometBlueStates()
266263
self._connect_lock = asyncio.Lock()
267264
self._operation_lock = asyncio.Lock()
268-
self._client: BleakClientWithServiceCache | None = None
269-
self._cached_services: BleakGATTServiceCollection | None = None
270-
self._read_char: BleakGATTCharacteristic | None = None
271-
self._write_char: BleakGATTCharacteristic | None = None
265+
self._client: BleakClient | None = None
272266
self._disconnect_timer: asyncio.TimerHandle | None = None
273267
self._expected_disconnect = False
274-
self.loop = asyncio.get_event_loop()
268+
self._loop = asyncio.get_event_loop()
275269
# btle.Debugging = True
276-
async def _ensure_connected(self):
270+
271+
async def _ensure_connected(self, device: BLEDevice):
277272
"""Ensure connection to device is established."""
278273
if self._connect_lock.locked():
279274
_LOGGER.debug(
280275
"%s: Connection already in progress, waiting for it to complete;",
281-
self._address,
276+
device.address,
282277
)
283278
if self._client and self._client.is_connected:
284279
self._reset_disconnect_timer()
@@ -288,54 +283,44 @@ async def _ensure_connected(self):
288283
if self._client and self._client.is_connected:
289284
self._reset_disconnect_timer()
290285
return
291-
_LOGGER.debug("%s: Connecting; ", self._address)
292-
if self._device is None:
293-
self._device = await BleakScanner.find_device_by_address(self._address, 60.0)
294-
if self._device is None:
295-
self._device = await BleakScanner.find_device_by_address(self._address, 60.0)
296-
if self._device is None:
297-
raise Exception("could not discover device")
298-
299-
client = await establish_connection(
300-
BleakClientWithServiceCache,
286+
_LOGGER.debug("%s: Connecting; ", device.address)
287+
self._device = device
288+
self._client = await establish_connection(
289+
BleakClient,
301290
self._device,
302-
self._address,
303-
self._disconnected,
304-
cached_services=self._cached_services,
291+
self._device.address,
292+
self._disconnected
305293
)
306-
self._cached_services = client.services
307-
_LOGGER.debug("%s: Connected", self._address)
308-
services = client.services
309-
self._client = client
294+
_LOGGER.debug("%s: Connected", self._device.address)
310295
# authenticate with PIN and initialize static values
311296
self._reset_disconnect_timer()
312297

313298
data = struct.pack(_PIN_STRUCT_PACKING, self._pin)
314299
try:
315300
await self._client.write_gatt_char(PASSWORD_CHAR, data, response=True)
316301
except BleakError:
317-
_LOGGER.error("provided pin was not accepted by device %s" % client.address)
302+
_LOGGER.error("provided pin was not accepted by device %s" % self._client.address)
318303

319-
_LOGGER.debug("Connected and authenticated with device %s", self._address)
304+
_LOGGER.debug("Connected and authenticated with device %s", self._device.address)
320305

321-
322-
def _disconnected(self, client: BleakClientWithServiceCache) -> None:
306+
def _disconnected(self, _: BleakClient) -> None:
323307
"""Disconnected callback."""
324308
if self._expected_disconnect:
325309
_LOGGER.debug(
326-
"%s: Disconnected from device;", self._address
310+
"%s: Disconnected from device;", self._device.address
327311
)
328312
return
329313
_LOGGER.warning(
330314
"%s: Device unexpectedly disconnected",
331-
self._address
315+
self._device.address
332316
)
317+
333318
def _reset_disconnect_timer(self):
334319
"""Reset disconnect timer."""
335320
if self._disconnect_timer:
336321
self._disconnect_timer.cancel()
337322
self._expected_disconnect = False
338-
self._disconnect_timer = self.loop.call_later(
323+
self._disconnect_timer = self._loop.call_later(
339324
DISCONNECT_DELAY, self._disconnect
340325
)
341326

@@ -355,7 +340,6 @@ async def _execute_disconnect(self):
355340
if client and client.is_connected:
356341
await client.disconnect()
357342

358-
359343
def should_update(self):
360344
"""
361345
Signal necessity to call update() on next cycle because values need
@@ -480,13 +464,13 @@ def window_open(self):
480464
"""Return True if device detected opened window"""
481465
return self._current.window_open
482466

483-
async def update(self):
467+
async def update(self, device: BLEDevice):
484468
"""Communicate with device, first try to write new values, then read from device"""
485469
current = self._current
486470
target = self._target
487471

488-
#with self.btle_connection() as conn:
489-
await self._ensure_connected()
472+
# with self.btle_connection() as conn:
473+
await self._ensure_connected(device)
490474

491475
conn = self._client
492476
device_infos = [
@@ -496,30 +480,29 @@ async def update(self):
496480
current.software_rev
497481
]
498482
if None in device_infos:
499-
_LOGGER.debug("Fetching hardware information for device %s", self._address)
483+
_LOGGER.debug("Fetching hardware information for device %s", self._device.address)
500484
current.model = (await conn.read_gatt_char(MODEL_CHAR)).decode()
501485
current.firmware_rev = (await conn.read_gatt_char(FIRMWARE_CHAR)).decode()
502486
current.manufacturer = (await conn.read_gatt_char(MANUFACTURER_CHAR)).decode()
503-
current.software_rev = (await conn.read_gatt_char(SOFTWARE_REV)).decode()
487+
current.software_rev = (await conn.read_gatt_char(SOFTWARE_REV)).decode()
504488
_LOGGER.debug("Sucessfully fetched hardware information")
505489

506490
if not target.all_temperatures_none:
507491
await conn.write_gatt_char(TEMPERATURE_CHAR,
508-
target.temperatures,
509-
response=True)
492+
target.temperatures,
493+
response=True)
510494
target.clear_temperatures()
511-
_LOGGER.debug("Successfully updated Temperatures for device %s", self._address)
495+
_LOGGER.debug("Successfully updated Temperatures for device %s", self._device.address)
512496

513497
if target.status_code is not None:
514498
await conn.write_gatt_char(STATUS_CHAR,
515-
target.status_code,
516-
response=True)
499+
target.status_code,
500+
response=True)
517501
target.status_code = None
518-
_LOGGER.debug("Successfully updated status for device %s", self._address)
502+
_LOGGER.debug("Successfully updated status for device %s", self._device.address)
519503

520504
current.temperatures = await conn.read_gatt_char(TEMPERATURE_CHAR)
521505
current.status_code = await conn.read_gatt_char(STATUS_CHAR)
522506
current.battery_level = await conn.read_gatt_char(BATTERY_CHAR)
523-
_LOGGER.debug("Successfully fetched new readings for device %s", self._address)
507+
_LOGGER.debug("Successfully fetched new readings for device %s", self._device.address)
524508
self.available = True
525-

0 commit comments

Comments
 (0)