Skip to content

Commit e9e3604

Browse files
authored
refactor: connectivity_sensor (#13)
1 parent 999286c commit e9e3604

5 files changed

Lines changed: 75 additions & 62 deletions

File tree

custom_components/gicisky/__init__.py

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -99,40 +99,28 @@ async def async_setup_entry(hass: HomeAssistant, entry: GiciskyConfigEntry) -> b
9999
entry=entry,
100100
)
101101

102-
async def _async_poll_data(hass: HomeAssistant, entry: GiciskyConfigEntry,) -> SensorUpdate:
103-
try:
104-
coordinator = entry.runtime_data
105-
return await coordinator.device_data.async_poll()
106-
except Exception as err:
107-
raise UpdateFailed(f"polling error: {err}") from err
108-
109-
poll_coordinator = DataUpdateCoordinator[SensorUpdate](
102+
image_coordinator: DataUpdateCoordinator[bytes] = DataUpdateCoordinator(
110103
hass,
111104
_LOGGER,
112105
name=DOMAIN,
113-
update_method=partial(_async_poll_data, hass, entry),
114-
update_interval=timedelta(hours=24),
115106
)
116-
117-
image_coordinator: DataUpdateCoordinator[bytes] = DataUpdateCoordinator(
107+
preview_coordinator: DataUpdateCoordinator[bytes] = DataUpdateCoordinator(
118108
hass,
119109
_LOGGER,
120110
name=DOMAIN,
121111
)
122-
preview_coordinator: DataUpdateCoordinator[bytes] = DataUpdateCoordinator(
112+
connectivity_coordinator: DataUpdateCoordinator[bool] = DataUpdateCoordinator(
123113
hass,
124114
_LOGGER,
125115
name=DOMAIN,
126116
)
127117
entry.runtime_data = bt_coordinator
128-
entry.runtime_data.poll_coordinator = poll_coordinator
129-
hass.data[DOMAIN][entry.entry_id]['poll_coordinator'] = poll_coordinator
130118
hass.data[DOMAIN][entry.entry_id]['image_coordinator'] = image_coordinator
131119
hass.data[DOMAIN][entry.entry_id]['preview_coordinator'] = preview_coordinator
132-
await poll_coordinator.async_config_entry_first_refresh()
120+
hass.data[DOMAIN][entry.entry_id]['connectivity_coordinator'] = connectivity_coordinator
121+
connectivity_coordinator.async_set_updated_data(False)
133122
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
134123

135-
136124
@callback
137125
# callback for the draw custom service
138126
async def writeservice(service: ServiceCall) -> None:
@@ -149,24 +137,22 @@ async def writeservice(service: ServiceCall) -> None:
149137
entry_id = await get_entry_id_from_device(hass, device_id)
150138
address = hass.data[DOMAIN][entry_id]['address']
151139
data = hass.data[DOMAIN][entry_id]['data']
152-
coordinator = hass.data[DOMAIN][entry_id]['poll_coordinator']
153140
image_coordinator = hass.data[DOMAIN][entry_id]['image_coordinator']
154141
preview_coordinator = hass.data[DOMAIN][entry_id]['preview_coordinator']
142+
connectivity_coordinator = hass.data[DOMAIN][entry_id]['connectivity_coordinator']
155143
ble_device = async_ble_device_from_address(hass, address)
156144
threshold = int(service.data.get("threshold", 128))
157145
red_threshold = int(service.data.get("red_threshold", 128))
158146
image = await hass.async_add_executor_job(customimage, entry_id, data.device, service, hass)
159147
image_bytes = BytesIO()
160148
image.save(image_bytes, "PNG")
161149
preview_coordinator.async_set_updated_data(image_bytes.getvalue())
162-
163150
# If dry_run is True, skip sending to the actual device
164151
if dry_run:
165152
continue
166153

167154
max_retries = 3
168-
await data.set_connected(True)
169-
await coordinator.async_refresh()
155+
connectivity_coordinator.async_set_updated_data(True)
170156
for attempt in range(1, max_retries + 1):
171157
success = await update_image(ble_device, data.device, image, threshold, red_threshold)
172158
if success:
@@ -177,11 +163,9 @@ async def writeservice(service: ServiceCall) -> None:
177163
if attempt < max_retries:
178164
await sleep(1)
179165
else:
180-
await data.set_connected(False)
181-
await coordinator.async_refresh()
166+
connectivity_coordinator.async_set_updated_data(False)
182167
raise HomeAssistantError(f"Failed to write to {address} after {max_retries} attempts")
183-
await data.set_connected(False)
184-
await coordinator.async_refresh()
168+
connectivity_coordinator.async_set_updated_data(False)
185169

186170
# register the services
187171
hass.services.async_register(DOMAIN, "write", writeservice)

custom_components/gicisky/binary_sensor.py

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Support for Gicisky binary sensors."""
22

33
from __future__ import annotations
4-
from functools import partial
4+
import logging
55
from .gicisky_ble import (
66
BinarySensorDeviceClass as GiciskyBinarySensorDeviceClass,
77
SensorUpdate,
@@ -16,13 +16,22 @@
1616
PassiveBluetoothDataUpdate,
1717
PassiveBluetoothProcessorEntity,
1818
)
19-
from homeassistant.core import HomeAssistant
19+
from homeassistant.core import HomeAssistant, callback
2020
from homeassistant.helpers.entity_platform import AddEntitiesCallback
2121
from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info
22-
22+
from homeassistant.helpers.device_registry import DeviceInfo, CONNECTION_BLUETOOTH
23+
from homeassistant.helpers.update_coordinator import CoordinatorEntity
24+
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
25+
from homeassistant.config_entries import ConfigEntry
26+
from propcache.api import cached_property
2327
from .coordinator import GiciskyPassiveBluetoothDataProcessor
2428
from .device import device_key_to_bluetooth_entity_key
2529
from .types import GiciskyConfigEntry
30+
from .const import (
31+
DOMAIN
32+
)
33+
34+
_LOGGER = logging.getLogger(__name__)
2635

2736
BINARY_SENSOR_DESCRIPTIONS = {
2837
GiciskyBinarySensorDeviceClass.BATTERY: BinarySensorEntityDescription(
@@ -185,6 +194,8 @@ async def async_setup_entry(
185194
coordinator.async_register_processor(processor, BinarySensorEntityDescription)
186195
)
187196

197+
connectivity_coordinator = hass.data[DOMAIN][entry.entry_id]["connectivity_coordinator"]
198+
async_add_entities([GiciskyBluetoothConnectivitySensorEntity(hass, entry, connectivity_coordinator)])
188199

189200
class GiciskyBluetoothBinarySensorEntity(
190201
PassiveBluetoothProcessorEntity[GiciskyPassiveBluetoothDataProcessor[bool | None]],
@@ -202,8 +213,55 @@ def available(self) -> bool:
202213
"""Return True if entity is available."""
203214
return super().available
204215

205-
async def async_added_to_hass(self) -> None:
206-
await super().async_added_to_hass()
207-
poll_coordinator = self.processor.coordinator.poll_coordinator
208-
remove = poll_coordinator.async_add_listener(partial(self.processor.async_handle_update, poll_coordinator.data))
209-
self.async_on_remove(remove)
216+
class GiciskyBluetoothConnectivitySensorEntity(
217+
CoordinatorEntity[DataUpdateCoordinator[bool]],
218+
BinarySensorEntity,
219+
):
220+
_attr_device_class = BinarySensorDeviceClass.CONNECTIVITY
221+
222+
def __init__(self, hass: HomeAssistant, entry: ConfigEntry, coordinator: DataUpdateCoordinator[bool]):
223+
CoordinatorEntity.__init__(self, coordinator)
224+
# self.hass = hass
225+
address = hass.data[DOMAIN][entry.entry_id]['address']
226+
self._address = address
227+
self._identifier = address.replace(":", "")[-8:]
228+
self._attr_name = f"Gicisky {self._identifier} Connectivity"
229+
self._attr_unique_id = f"gicisky_{self._identifier}_connectivity"
230+
self._is_on = False
231+
232+
"""Representation of a Gicisky binary sensor."""
233+
@property
234+
def is_on(self) -> bool | None:
235+
"""Return the native value."""
236+
return self._is_on
237+
238+
@property
239+
def device_info(self) -> DeviceInfo:
240+
return DeviceInfo (
241+
connections = {
242+
(
243+
CONNECTION_BLUETOOTH,
244+
self._address,
245+
)
246+
},
247+
name = f"Gicisky {self._identifier}",
248+
manufacturer = "Gicisky",
249+
)
250+
251+
@cached_property
252+
def available(self) -> bool:
253+
"""Entity always either data or empty."""
254+
return True
255+
256+
@property
257+
def data(self) -> bytes:
258+
"""Return coordinator data for this entity."""
259+
return self.coordinator.data
260+
261+
262+
@callback
263+
def _handle_coordinator_update(self) -> None:
264+
"""Handle updated data from the coordinator."""
265+
_LOGGER.debug("Updated binary data")
266+
self._is_on = self.data
267+
super()._handle_coordinator_update()

custom_components/gicisky/gicisky_ble/parser.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
11
from __future__ import annotations
22

33
import logging
4-
from typing import Any
5-
from datetime import datetime, timezone
6-
from bleak.backends.device import BLEDevice
74
from bluetooth_sensor_state_data import BluetoothData
8-
from cryptography.hazmat.primitives.ciphers.aead import AESCCM
95
from home_assistant_bluetooth import BluetoothServiceInfoBleak
106
from sensor_state_data import (
117
SensorLibrary,
12-
BinarySensorDeviceClass,
13-
SensorDeviceInfo,
14-
SensorUpdate
158
)
169

1710
from .devices import DEVICE_TYPES, DeviceEntry
@@ -32,7 +25,6 @@ def __init__(self) -> None:
3225
self.last_service_info: BluetoothServiceInfoBleak | None = None
3326

3427
self.device: DeviceEntry | None = None
35-
self.is_connected: bool = False
3628

3729
def supported(self, data: BluetoothServiceInfoBleak) -> bool:
3830
if not super().supported(data):
@@ -89,12 +81,3 @@ def _parse_gicisky(
8981
)
9082
return True
9183

92-
async def set_connected(self, connected: bool):
93-
self.is_connected = connected
94-
95-
async def async_poll(self) -> SensorUpdate:
96-
self._events_updates.clear()
97-
self.update_predefined_binary_sensor(
98-
BinarySensorDeviceClass.CONNECTIVITY, self.is_connected
99-
)
100-
return self._finish_update()

custom_components/gicisky/imagegen.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
11
import io
22
import logging
33
import os
4-
import pprint
54
import math
65
import json
7-
from math import radians
86

97
import requests
108
import qrcode
11-
import shutil
129
from io import BytesIO
1310
import base64
1411

1512
import urllib
16-
from .const import DOMAIN
17-
from .util import get_image_folder, get_image_path
1813
from PIL import Image, ImageDraw, ImageFont
1914
import barcode
2015
from barcode.writer import ImageWriter

custom_components/gicisky/sensor.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from __future__ import annotations
44
from datetime import datetime
55
from typing import cast
6-
from functools import partial
76
from .gicisky_ble import SensorDeviceClass as GiciskySensorDeviceClass, SensorUpdate, Units
87
from .gicisky_ble.const import (
98
ExtendedSensorDeviceClass as GiciskyExtendedSensorDeviceClass,
@@ -467,9 +466,3 @@ def native_value(self) -> int | float | datetime | None:
467466
def available(self) -> bool:
468467
"""Return True if entity is available."""
469468
return super().available
470-
471-
async def async_added_to_hass(self) -> None:
472-
await super().async_added_to_hass()
473-
poll_coordinator = self.processor.coordinator.poll_coordinator
474-
remove = poll_coordinator.async_add_listener(partial(self.processor.async_handle_update, poll_coordinator.data))
475-
self.async_on_remove(remove)

0 commit comments

Comments
 (0)