Skip to content

Commit bb0ff94

Browse files
committed
feat: add a bunch of sensors
1 parent 2daa326 commit bb0ff94

File tree

5 files changed

+310
-28
lines changed

5 files changed

+310
-28
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
from homeassistant.components.binary_sensor import (
2+
BinarySensorEntity,
3+
BinarySensorEntityDescription,
4+
BinarySensorDeviceClass,
5+
)
6+
from homeassistant.config_entries import ConfigEntry
7+
from homeassistant.const import STATE_ON
8+
from homeassistant.core import HomeAssistant, callback
9+
from homeassistant.helpers.entity_platform import AddEntitiesCallback
10+
from homeassistant.helpers.restore_state import RestoreEntity
11+
12+
from .manager import AutoBackup
13+
from .const import (
14+
DATA_AUTO_BACKUP,
15+
EVENT_BACKUP_START,
16+
EVENT_BACKUP_SUCCESSFUL,
17+
EVENT_BACKUP_FAILED,
18+
)
19+
from .helpers import get_device_info
20+
21+
22+
async def async_setup_entry(
23+
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
24+
):
25+
"""Set up Auto Backup sensors based on a config entry."""
26+
auto_backup = hass.data[DATA_AUTO_BACKUP]
27+
async_add_entities(
28+
[
29+
AutoBackupStatusSensor(entry, auto_backup),
30+
AutoBackupProblemSensor(entry, auto_backup),
31+
]
32+
)
33+
34+
35+
class AutoBackupBaseBinarySensor(BinarySensorEntity):
36+
_attr_has_entity_name = True
37+
38+
def __init__(self, entry: ConfigEntry, auto_backup: AutoBackup):
39+
self._auto_backup = auto_backup
40+
self._attr_unique_id = self.entity_description.key
41+
self._attr_device_info = get_device_info(entry)
42+
43+
44+
class AutoBackupStatusSensor(AutoBackupBaseBinarySensor):
45+
entity_description = BinarySensorEntityDescription(
46+
key="status",
47+
name="Backup status",
48+
device_class=BinarySensorDeviceClass.RUNNING,
49+
)
50+
51+
async def async_added_to_hass(self):
52+
"""Run when entity about to be added."""
53+
await super().async_added_to_hass()
54+
55+
@callback
56+
def update(_):
57+
"""Update sensor on backup events."""
58+
self.async_schedule_update_ha_state(True)
59+
60+
for event in (EVENT_BACKUP_START, EVENT_BACKUP_SUCCESSFUL, EVENT_BACKUP_FAILED):
61+
self.async_on_remove(self.hass.bus.async_listen(event, update))
62+
63+
@property
64+
def is_on(self):
65+
"""Return the state of the entity."""
66+
return self._auto_backup.state > 0
67+
68+
69+
class AutoBackupProblemSensor(RestoreEntity, AutoBackupBaseBinarySensor):
70+
entity_description = BinarySensorEntityDescription(
71+
key="problem",
72+
name="Successful",
73+
device_class=BinarySensorDeviceClass.PROBLEM,
74+
entity_registry_enabled_default=False,
75+
)
76+
_attr_is_on = False
77+
78+
async def async_added_to_hass(self):
79+
"""Run when entity about to be added."""
80+
await super().async_added_to_hass()
81+
state = await self.async_get_last_state()
82+
if state:
83+
self._attr_is_on = state.state == STATE_ON
84+
85+
@callback
86+
def backup_success(_):
87+
"""Update sensor on backup events."""
88+
self._attr_is_on = False
89+
self.async_schedule_update_ha_state(True)
90+
91+
@callback
92+
def backup_failure(_):
93+
"""Update sensor on backup events."""
94+
self._attr_is_on = True
95+
self.async_schedule_update_ha_state(True)
96+
97+
self.async_on_remove(
98+
self.hass.bus.async_listen(EVENT_BACKUP_SUCCESSFUL, backup_success)
99+
)
100+
self.async_on_remove(
101+
self.hass.bus.async_listen(EVENT_BACKUP_FAILED, backup_failure)
102+
)

custom_components/auto_backup/button.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ class AutoBackupPurgeButton(ButtonEntity):
2222
name="Purge backups",
2323
icon="mdi:close-circle",
2424
)
25-
2625
_attr_has_entity_name = True
2726

2827
def __init__(self, entry: ConfigEntry, auto_backup: AutoBackup):

custom_components/auto_backup/const.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@
3737
ATTR_COMPRESSED = "compressed"
3838
ATTR_LOCATION = "location"
3939

40+
ATTR_LAST_FAILURE = "last_failure"
41+
ATTR_PURGEABLE = "purgeable_backups"
42+
ATTR_MONITORED = "monitored_backups"
43+
ATTR_ERROR = "error"
44+
ATTR_SLUG = "slug"
45+
4046
DEFAULT_BACKUP_FOLDERS = {
4147
"ssl": "ssl",
4248
"share": "share",

custom_components/auto_backup/manager.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,15 @@ def purgeable(self):
7878
def state(self):
7979
return self._state
8080

81-
def get_next_expiry(self):
81+
def get_next_expiry(self) -> datetime | None:
8282
"""Return the next snapshot expiry date that has not expired"""
8383
return min(
84-
expiry
85-
for expiry in self._snapshots.values()
86-
if expiry > datetime.now().astimezone()
84+
(
85+
expiry
86+
for expiry in self._snapshots.values()
87+
if expiry > datetime.now().astimezone()
88+
),
89+
default=None,
8790
)
8891

8992
@classmethod

custom_components/auto_backup/sensor.py

Lines changed: 195 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,71 @@
1-
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
1+
from datetime import datetime
2+
from homeassistant.components.sensor import (
3+
SensorEntity,
4+
SensorEntityDescription,
5+
SensorStateClass,
6+
SensorDeviceClass,
7+
RestoreSensor,
8+
)
29
from homeassistant.config_entries import ConfigEntry
310
from homeassistant.const import ATTR_NAME
411
from homeassistant.core import HomeAssistant, callback, Event
5-
from homeassistant.helpers.device_registry import DeviceEntryType
6-
from homeassistant.helpers.entity import DeviceInfo
712
from homeassistant.helpers.entity_platform import AddEntitiesCallback
813

9-
from . import AutoBackup
1014
from .const import (
11-
DOMAIN,
1215
EVENT_BACKUPS_PURGED,
1316
EVENT_BACKUP_SUCCESSFUL,
1417
EVENT_BACKUP_FAILED,
1518
EVENT_BACKUP_START,
1619
DATA_AUTO_BACKUP,
20+
ATTR_LAST_FAILURE,
21+
ATTR_MONITORED,
22+
ATTR_PURGEABLE,
23+
ATTR_ERROR,
24+
ATTR_SLUG,
1725
)
18-
19-
ATTR_LAST_FAILURE = "last_failure"
20-
ATTR_PURGEABLE = "purgeable_backups"
21-
ATTR_MONITORED = "monitored_backups"
26+
from .helpers import get_device_info
27+
from .manager import AutoBackup
2228

2329

2430
async def async_setup_entry(
2531
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
2632
):
2733
"""Set up Auto Backup sensors based on a config entry."""
28-
auto_backup = hass.data[DOMAIN][DATA_AUTO_BACKUP]
29-
async_add_entities([AutoBackupSensor(entry, auto_backup)])
34+
auto_backup = hass.data[DATA_AUTO_BACKUP]
35+
async_add_entities(
36+
[
37+
AutoBackupSensor(entry, auto_backup),
38+
AutoBackupMonitoredSensor(entry, auto_backup),
39+
AutoBackupPurgeableSensor(entry, auto_backup),
40+
AutoBackupLastFailureSensor(entry, auto_backup),
41+
AutoBackupLastSuccessSensor(entry, auto_backup),
42+
AutoBackupNextExpirySensor(entry, auto_backup),
43+
]
44+
)
45+
46+
47+
class AutoBackupBaseSensor(SensorEntity):
48+
_attr_has_entity_name = True
49+
50+
def __init__(self, entry: ConfigEntry, auto_backup: AutoBackup):
51+
self._auto_backup = auto_backup
52+
self._attr_unique_id = self.entity_description.key
53+
self._attr_device_info = get_device_info(entry)
3054

3155

32-
class AutoBackupSensor(SensorEntity):
56+
class AutoBackupSensor(AutoBackupBaseSensor):
3357
entity_description = SensorEntityDescription(
3458
key="backups",
35-
icon="mdi:package-variant-closed",
3659
name="Auto Backup",
60+
icon="mdi:package-variant-closed",
3761
native_unit_of_measurement="pending backup(s)",
3862
)
39-
40-
_attr_unique_id = "sensor-auto-backup"
4163
_attr_extra_state_attributes = {}
64+
_attr_has_entity_name = False
4265

4366
def __init__(self, entry: ConfigEntry, auto_backup: AutoBackup):
44-
self._auto_backup = auto_backup
45-
self._attr_device_info = DeviceInfo(
46-
entry_type=DeviceEntryType.SERVICE,
47-
configuration_url="https://jcwillox.github.io/hass-auto-backup",
48-
manufacturer="Auto Backup",
49-
identifiers={(DOMAIN, entry.entry_id)},
50-
name=entry.title,
51-
)
67+
super().__init__(entry, auto_backup)
68+
self._attr_unique_id = "sensor-auto-backup"
5269

5370
async def async_added_to_hass(self):
5471
"""Run when entity about to be added."""
@@ -85,3 +102,158 @@ def native_value(self):
85102
async def async_update(self):
86103
self._attr_extra_state_attributes[ATTR_MONITORED] = self._auto_backup.monitored
87104
self._attr_extra_state_attributes[ATTR_PURGEABLE] = self._auto_backup.purgeable
105+
106+
107+
class AutoBackupMonitoredSensor(AutoBackupBaseSensor):
108+
entity_description = SensorEntityDescription(
109+
key="monitored",
110+
name="Monitored backups",
111+
icon="mdi:package-variant-closed-check",
112+
state_class=SensorStateClass.MEASUREMENT,
113+
has_entity_name=True,
114+
)
115+
116+
@property
117+
def native_value(self):
118+
"""Return the state of the entity."""
119+
return self._auto_backup.monitored
120+
121+
async def async_added_to_hass(self):
122+
"""Run when entity about to be added."""
123+
await super().async_added_to_hass()
124+
125+
@callback
126+
def update(_):
127+
"""Update sensor on backup events."""
128+
self.async_schedule_update_ha_state(True)
129+
130+
for event in (EVENT_BACKUP_SUCCESSFUL, EVENT_BACKUPS_PURGED):
131+
self.async_on_remove(self.hass.bus.async_listen(event, update))
132+
133+
134+
class AutoBackupPurgeableSensor(AutoBackupBaseSensor):
135+
entity_description = SensorEntityDescription(
136+
key="purgeable",
137+
name="Purgeable backups",
138+
icon="mdi:package-variant-closed-remove",
139+
state_class=SensorStateClass.MEASUREMENT,
140+
)
141+
142+
async def async_added_to_hass(self):
143+
"""Run when entity about to be added."""
144+
await super().async_added_to_hass()
145+
146+
@callback
147+
def update(_):
148+
"""Update sensor on backup events."""
149+
self.async_schedule_update_ha_state(True)
150+
151+
for event in (EVENT_BACKUP_SUCCESSFUL, EVENT_BACKUPS_PURGED):
152+
self.async_on_remove(self.hass.bus.async_listen(event, update))
153+
154+
@property
155+
def native_value(self):
156+
"""Return the state of the entity."""
157+
return self._auto_backup.purgeable
158+
159+
160+
class AutoBackupLastFailureSensor(RestoreSensor, AutoBackupBaseSensor):
161+
entity_description = SensorEntityDescription(
162+
key="last-failure",
163+
name="Last failure",
164+
device_class=SensorDeviceClass.TIMESTAMP,
165+
)
166+
_attr_extra_state_attributes = {}
167+
_attr_should_poll = False
168+
169+
async def async_added_to_hass(self):
170+
"""Run when entity about to be added."""
171+
await super().async_added_to_hass()
172+
state = await self.async_get_last_state()
173+
data = await self.async_get_last_sensor_data()
174+
if state and data:
175+
self._attr_native_value = data.native_value
176+
self._attr_extra_state_attributes[ATTR_NAME] = state.attributes.get(
177+
ATTR_NAME
178+
)
179+
self._attr_extra_state_attributes[ATTR_ERROR] = state.attributes.get(
180+
ATTR_ERROR
181+
)
182+
183+
@callback
184+
def backup_failed(event_: Event):
185+
"""Store last failed and update sensor"""
186+
self._attr_native_value = datetime.now().astimezone()
187+
self._attr_extra_state_attributes[ATTR_NAME] = event_.data.get(ATTR_NAME)
188+
self._attr_extra_state_attributes[ATTR_ERROR] = event_.data.get(ATTR_ERROR)
189+
self.async_schedule_update_ha_state(True)
190+
191+
self.async_on_remove(
192+
self.hass.bus.async_listen(EVENT_BACKUP_FAILED, backup_failed)
193+
)
194+
195+
196+
class AutoBackupLastSuccessSensor(RestoreSensor, AutoBackupBaseSensor):
197+
entity_description = SensorEntityDescription(
198+
key="last-success",
199+
name="Last success",
200+
device_class=SensorDeviceClass.TIMESTAMP,
201+
)
202+
_attr_extra_state_attributes = {}
203+
_attr_should_poll = False
204+
205+
async def async_added_to_hass(self):
206+
"""Run when entity about to be added."""
207+
await super().async_added_to_hass()
208+
state = await self.async_get_last_state()
209+
data = await self.async_get_last_sensor_data()
210+
if state and data:
211+
self._attr_native_value = data.native_value
212+
self._attr_extra_state_attributes[ATTR_NAME] = state.attributes.get(
213+
ATTR_NAME
214+
)
215+
self._attr_extra_state_attributes[ATTR_SLUG] = state.attributes.get(
216+
ATTR_SLUG
217+
)
218+
219+
@callback
220+
def backup_success(event_: Event):
221+
"""Store last success and update sensor"""
222+
self._attr_native_value = datetime.now().astimezone()
223+
self._attr_extra_state_attributes[ATTR_NAME] = event_.data.get(ATTR_NAME)
224+
self._attr_extra_state_attributes[ATTR_SLUG] = event_.data.get(ATTR_SLUG)
225+
self.async_schedule_update_ha_state(True)
226+
227+
self.async_on_remove(
228+
self.hass.bus.async_listen(EVENT_BACKUP_SUCCESSFUL, backup_success)
229+
)
230+
231+
232+
class AutoBackupNextExpirySensor(RestoreSensor, AutoBackupBaseSensor):
233+
entity_description = SensorEntityDescription(
234+
key="next-expiration",
235+
name="Next expiration",
236+
device_class=SensorDeviceClass.TIMESTAMP,
237+
entity_registry_enabled_default=False,
238+
)
239+
_attr_extra_state_attributes = {}
240+
241+
async def async_added_to_hass(self):
242+
"""Run when entity about to be added."""
243+
await super().async_added_to_hass()
244+
data = await self.async_get_last_sensor_data()
245+
if data:
246+
self._attr_native_value = data.native_value
247+
248+
@callback
249+
def backup_success(_):
250+
"""Next expiry might have changes so update sensor"""
251+
self.async_schedule_update_ha_state(True)
252+
253+
for event in (EVENT_BACKUP_SUCCESSFUL, EVENT_BACKUPS_PURGED):
254+
self.async_on_remove(self.hass.bus.async_listen(event, backup_success))
255+
256+
@property
257+
def native_value(self):
258+
"""Return next expiry datetime."""
259+
return self._auto_backup.get_next_expiry()

0 commit comments

Comments
 (0)