Skip to content

Commit 858c914

Browse files
202409 (#2273)
* Change service to new 2024.9 schema * Version bump to 2024.9 * Add a default battery last reported to now * Set default battery last reported in device setup * Default battery last replaced * Don't default last replaced on older than 2024.9 devices * Handle epoch * Update dev environment * Tidy up last replaced store update * Add battery replaced event * Remove import * Apply automatic changes * Lint error * Remove unused import --------- Co-authored-by: andrew-codechimp <[email protected]>
1 parent e12e7ac commit 858c914

File tree

11 files changed

+129
-31
lines changed

11 files changed

+129
-31
lines changed

.devcontainer/integration/devcontainer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "BN Integration",
3-
"image": "mcr.microsoft.com/vscode/devcontainers/python:3.12-bullseye",
3+
"image": "mcr.microsoft.com/devcontainers/python:3.12",
44
"postCreateCommand": "scripts/setup",
55
"runArgs": [
66
"--network=host"

.ruff.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# The contents of this file is based on https://github.com/home-assistant/core/blob/dev/pyproject.toml
22

3-
target-version = "py310"
3+
target-version = "py312"
44

55
[lint]
66
select = [

custom_components/battery_notes/__init__.py

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
DOMAIN,
5757
DOMAIN_CONFIG,
5858
EVENT_BATTERY_NOT_REPORTED,
59+
EVENT_BATTERY_REPLACED,
5960
EVENT_BATTERY_THRESHOLD,
6061
MIN_HA_VERSION,
6162
PLATFORMS,
@@ -306,11 +307,7 @@ async def handle_battery_replaced(call):
306307
hass.data[DOMAIN][DATA].devices[config_entry_id].coordinator
307308
)
308309

309-
entry = {"battery_last_replaced": datetime_replaced}
310-
311-
coordinator.async_update_entity_config(
312-
entity_id=source_entity_id, data=entry
313-
)
310+
coordinator.last_replaced =datetime_replaced
314311
await coordinator.async_request_refresh()
315312

316313
_LOGGER.debug(
@@ -319,6 +316,24 @@ async def handle_battery_replaced(call):
319316
str(datetime_replaced),
320317
)
321318

319+
hass.bus.async_fire(
320+
EVENT_BATTERY_REPLACED,
321+
{
322+
ATTR_DEVICE_ID: coordinator.device_id or "",
323+
ATTR_SOURCE_ENTITY_ID: coordinator.source_entity_id
324+
or "",
325+
ATTR_DEVICE_NAME: coordinator.device_name,
326+
ATTR_BATTERY_TYPE_AND_QUANTITY: coordinator.battery_type_and_quantity,
327+
ATTR_BATTERY_TYPE: coordinator.battery_type,
328+
ATTR_BATTERY_QUANTITY: coordinator.battery_quantity,
329+
},
330+
)
331+
332+
_LOGGER.debug(
333+
"Raised event battery replaced %s",
334+
coordinator.device_id,
335+
)
336+
322337
return
323338

324339
_LOGGER.error("Entity %s not configured in Battery Notes", source_entity_id)
@@ -340,11 +355,7 @@ async def handle_battery_replaced(call):
340355
hass.data[DOMAIN][DATA].devices[entry.entry_id].coordinator
341356
)
342357

343-
device_entry = {"battery_last_replaced": datetime_replaced}
344-
345-
coordinator.async_update_device_config(
346-
device_id=device_id, data=device_entry
347-
)
358+
coordinator.last_replaced =datetime_replaced
348359

349360
await coordinator.async_request_refresh()
350361

@@ -354,6 +365,24 @@ async def handle_battery_replaced(call):
354365
str(datetime_replaced),
355366
)
356367

368+
hass.bus.async_fire(
369+
EVENT_BATTERY_REPLACED,
370+
{
371+
ATTR_DEVICE_ID: coordinator.device_id or "",
372+
ATTR_SOURCE_ENTITY_ID: coordinator.source_entity_id
373+
or "",
374+
ATTR_DEVICE_NAME: coordinator.device_name,
375+
ATTR_BATTERY_TYPE_AND_QUANTITY: coordinator.battery_type_and_quantity,
376+
ATTR_BATTERY_TYPE: coordinator.battery_type,
377+
ATTR_BATTERY_QUANTITY: coordinator.battery_quantity,
378+
},
379+
)
380+
381+
_LOGGER.debug(
382+
"Raised event battery replaced %s",
383+
coordinator.device_id,
384+
)
385+
357386
# Found and dealt with, exit
358387
return
359388

@@ -362,6 +391,7 @@ async def handle_battery_replaced(call):
362391
device_id,
363392
)
364393

394+
365395
async def handle_battery_last_reported(call):
366396
"""Handle the service call."""
367397
days_last_reported = call.data.get(SERVICE_DATA_DAYS_LAST_REPORTED)

custom_components/battery_notes/button.py

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
import logging
56
from dataclasses import dataclass
67
from datetime import datetime
78

@@ -35,18 +36,27 @@
3536

3637
from . import PLATFORMS
3738
from .const import (
39+
ATTR_BATTERY_QUANTITY,
40+
ATTR_BATTERY_TYPE,
41+
ATTR_BATTERY_TYPE_AND_QUANTITY,
42+
ATTR_DEVICE_ID,
43+
ATTR_DEVICE_NAME,
44+
ATTR_SOURCE_ENTITY_ID,
3845
CONF_ENABLE_REPLACED,
3946
CONF_SOURCE_ENTITY_ID,
4047
DATA,
4148
DOMAIN,
4249
DOMAIN_CONFIG,
50+
EVENT_BATTERY_REPLACED,
4351
)
4452
from .coordinator import BatteryNotesCoordinator
4553
from .device import BatteryNotesDevice
4654
from .entity import (
4755
BatteryNotesEntityDescription,
4856
)
4957

58+
_LOGGER = logging.getLogger(__name__)
59+
5060

5161
@dataclass
5262
class BatteryNotesButtonEntityDescription(
@@ -241,15 +251,24 @@ async def async_added_to_hass(self) -> None:
241251

242252
async def async_press(self) -> None:
243253
"""Press the button."""
244-
device_id = self._device_id
245-
246-
entry = {"battery_last_replaced": datetime.utcnow()}
254+
self.coordinator.last_replaced = datetime.utcnow()
255+
256+
self.hass.bus.async_fire(
257+
EVENT_BATTERY_REPLACED,
258+
{
259+
ATTR_DEVICE_ID: self.coordinator.device_id or "",
260+
ATTR_SOURCE_ENTITY_ID: self.coordinator.source_entity_id
261+
or "",
262+
ATTR_DEVICE_NAME: self.coordinator.device_name,
263+
ATTR_BATTERY_TYPE_AND_QUANTITY: self.coordinator.battery_type_and_quantity,
264+
ATTR_BATTERY_TYPE: self.coordinator.battery_type,
265+
ATTR_BATTERY_QUANTITY: self.coordinator.battery_quantity,
266+
},
267+
)
247268

248-
if self._source_entity_id:
249-
self.coordinator.async_update_entity_config(
250-
entity_id=self.coordinator.source_entity_id, data=entry
251-
)
252-
else:
253-
self.coordinator.async_update_device_config(device_id=device_id, data=entry)
269+
_LOGGER.debug(
270+
"Raised event battery replaced %s",
271+
self.coordinator.device_id,
272+
)
254273

255274
await self.coordinator.async_request_refresh()

custom_components/battery_notes/const.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
LOGGER: Logger = getLogger(__package__)
1313

14-
MIN_HA_VERSION = "2024.7"
14+
MIN_HA_VERSION = "2024.9"
1515

1616
manifestfile = Path(__file__).parent / "manifest.json"
1717
with open(file=manifestfile, encoding="UTF-8") as json_file:
@@ -71,6 +71,7 @@
7171
EVENT_BATTERY_THRESHOLD = "battery_notes_battery_threshold"
7272
EVENT_BATTERY_INCREASED = "battery_notes_battery_increased"
7373
EVENT_BATTERY_NOT_REPORTED = "battery_notes_battery_not_reported"
74+
EVENT_BATTERY_REPLACED = "battery_notes_battery_replaced"
7475

7576
ATTR_DEVICE_ID = "device_id"
7677
ATTR_SOURCE_ENTITY_ID = "source_entity_id"

custom_components/battery_notes/coordinator.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,16 @@ def last_replaced(self) -> datetime | None:
241241
return last_replaced_date
242242
return None
243243

244+
@last_replaced.setter
245+
def last_replaced(self, value):
246+
"""Set the last replaced datetime and store it."""
247+
entry = {LAST_REPLACED: value}
248+
249+
if self.source_entity_id:
250+
self.async_update_entity_config(entity_id=self.source_entity_id, data=entry)
251+
else:
252+
self.async_update_device_config(device_id=self.device_id, data=entry)
253+
244254
@property
245255
def last_reported(self) -> datetime | None:
246256
"""Get the last reported datetime."""
@@ -257,12 +267,13 @@ def last_reported(self) -> datetime | None:
257267
str(entry[LAST_REPORTED]) + "+00:00"
258268
)
259269
return last_reported_date
270+
260271
return None
261272

262273
@last_reported.setter
263274
def last_reported(self, value):
264275
"""Set the last reported datetime and store it."""
265-
entry = {"battery_last_reported": value}
276+
entry = {LAST_REPORTED: value}
266277

267278
if self.source_entity_id:
268279
self.async_update_entity_config(entity_id=self.source_entity_id, data=entry)

custom_components/battery_notes/device.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Battery Notes device, contains device level details."""
22

33
import logging
4+
from datetime import datetime
45

56
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
67
from homeassistant.components.sensor import (
@@ -186,6 +187,37 @@ async def async_setup(self) -> bool:
186187
self.coordinator.battery_low_threshold,
187188
)
188189

190+
# If there is not a last replaced, set to device created date if not epoch
191+
if not self.coordinator.last_replaced:
192+
last_replaced = None
193+
if entity.device_id:
194+
device_entry = device_registry.async_get(entity.device_id)
195+
196+
if device_entry.created_at.year > 1970:
197+
last_replaced = device_entry.created_at.strftime("%Y-%m-%dT%H:%M:%S:%f")
198+
else:
199+
entity = entity_registry.async_get(source_entity_id)
200+
if entity.created_at.year > 1970:
201+
last_replaced = entity.created_at.strftime("%Y-%m-%dT%H:%M:%S:%f")
202+
203+
_LOGGER.debug(
204+
"Defaulting %s battery last replaced to %s",
205+
source_entity_id or device_id,
206+
last_replaced,
207+
)
208+
209+
self.coordinator.last_replaced = last_replaced
210+
211+
# If there is not a last_reported set to now
212+
if not self.coordinator.last_reported:
213+
last_reported = datetime.utcnow()
214+
_LOGGER.debug(
215+
"Defaulting %s battery last reported to %s",
216+
source_entity_id or device_id,
217+
last_replaced,
218+
)
219+
self.coordinator.last_reported = last_reported
220+
189221
self.hass.data[DOMAIN][DATA].devices[config.entry_id] = self
190222
self.reset_jobs.append(config.add_update_listener(self.async_update))
191223

custom_components/battery_notes/icons.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,14 @@
2020
}
2121
},
2222
"services": {
23-
"set_battery_replaced": "mdi:battery-sync",
24-
"check_battery_last_reported": "mdi:battery-unknown",
25-
"check_battery_low": "mdi:battery-alert"
23+
"set_battery_replaced": {
24+
"service": "mdi:battery-sync"
25+
},
26+
"check_battery_last_reported": {
27+
"service": "mdi:battery-unknown"
28+
},
29+
"check_battery_low": {
30+
"service": "mdi:battery-alert"
31+
}
2632
}
2733
}

custom_components/battery_notes/library_updater.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from __future__ import annotations
44

5-
import asyncio
65
import json
76
import logging
87
import os
@@ -172,7 +171,7 @@ async def _api_wrapper(
172171
# response.raise_for_status()
173172
return await response.text()
174173

175-
except asyncio.TimeoutError as exception:
174+
except TimeoutError as exception:
176175
raise LibraryUpdaterClientCommunicationError(
177176
"Timeout error fetching information",
178177
) from exception

hacs.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "Battery Notes",
33
"filename": "battery_notes.zip",
44
"hide_default_branch": true,
5-
"homeassistant": "2024.7.0",
5+
"homeassistant": "2024.9.0",
66
"render_readme": true,
77
"zip_release": true,
88
"persistent_directory": "data"

0 commit comments

Comments
 (0)