Skip to content

Commit 247d84a

Browse files
authored
v3.5.0 (#99)
- Added a new sensor that countdown to payout day
2 parents ecc73f2 + 29afd1a commit 247d84a

File tree

3 files changed

+73
-12
lines changed

3 files changed

+73
-12
lines changed

custom_components/isitpayday/const.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,13 @@
9393
# Sensor and binary sensor entity names (fixed to required format)
9494
SENSOR_NEXT_PAYDAY = "sensor.payday_next"
9595
BINARY_SENSOR_IS_IT_PAYDAY = "binary_sensor.payday"
96+
SENSOR_DAYS_TO_PAYDAY = "sensor.days_to_payday"
9697

9798
# Sensor icons
9899
ICON_NEXT_PAYDAY = "mdi:calendar-clock"
99100
ICON_IS_IT_PAYDAY_TRUE = "mdi:cash-fast"
100101
ICON_IS_IT_PAYDAY_FALSE = "mdi:cash-clock"
102+
ICON_DAYS_TO = "mdi:calendar-end"
101103

102104
# Log messages (standardized to use across all files)
103105
LOG_INIT = "Initializing IsItPayday integration."

custom_components/isitpayday/payday_calculator.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,19 @@ async def async_calculate_next_payday(country: str, pay_frequency: str, pay_day=
3535
if pay_frequency == PAY_FREQ_MONTHLY:
3636
payday = await async_calculate_month_based(today, 1, pay_day, bank_offset, bank_holidays)
3737

38-
elif pay_frequency in (PAY_FREQ_28_DAYS, PAY_FREQ_14_DAYS, PAY_FREQ_BIMONTHLY, PAY_FREQ_QUARTERLY, PAY_FREQ_SEMIANNUAL, PAY_FREQ_ANNUAL):
38+
elif pay_frequency in (
39+
PAY_FREQ_28_DAYS,
40+
PAY_FREQ_14_DAYS,
41+
PAY_FREQ_BIMONTHLY,
42+
PAY_FREQ_QUARTERLY,
43+
PAY_FREQ_SEMIANNUAL,
44+
PAY_FREQ_ANNUAL,
45+
):
3946
interval_days = {
4047
PAY_FREQ_14_DAYS: 14,
4148
PAY_FREQ_28_DAYS: 28,
4249
PAY_FREQ_BIMONTHLY: 60,
43-
PAY_FREQ_QUARTERLY: 90,
50+
PAY_FREQ_QUARTERLY: 91,
4451
PAY_FREQ_SEMIANNUAL: 182,
4552
PAY_FREQ_ANNUAL: 365,
4653
}[pay_frequency]

custom_components/isitpayday/sensor.py

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
11
import logging
2-
from datetime import date, timedelta
3-
from homeassistant.components.sensor import SensorEntity
2+
from datetime import date, datetime, timedelta
3+
from math import ceil
4+
from homeassistant.components.sensor import SensorEntity, SensorDeviceClass
5+
from homeassistant.helpers.entity import EntityCategory
46
from homeassistant.helpers.update_coordinator import CoordinatorEntity, DataUpdateCoordinator
57
from .const import *
68

79
_LOGGER = logging.getLogger(__name__)
810

911
ICON = "mdi:calendar-clock"
12+
ICON_DAYS_TO = "mdi:calendar-end"
1013

1114
async def async_setup_entry(hass, entry, async_add_entities):
1215
data = hass.data[DOMAIN][entry.entry_id]
1316
coordinator: DataUpdateCoordinator = data["coordinator"]
1417
instance_name = data.get("name", "IsItPayday")
1518

16-
async_add_entities([IsItPaydayNextSensor(coordinator, entry.entry_id, instance_name)])
19+
async_add_entities([
20+
IsItPaydayNextSensor(coordinator, entry.entry_id, instance_name),
21+
IsItPaydayDaysToSensor(coordinator, entry.entry_id, instance_name)
22+
])
1723

1824

1925
class IsItPaydayNextSensor(CoordinatorEntity, SensorEntity):
@@ -35,20 +41,15 @@ def state(self) -> str:
3541

3642
today = date.today()
3743

38-
# Parse to date if it's a string
3944
if not isinstance(payday, date):
4045
try:
4146
payday = date.fromisoformat(payday)
4247
except (ValueError, TypeError):
4348
return "Unknown"
4449

45-
# If payday is today, we want to keep it until the day ends
46-
if payday > today:
50+
if payday >= today:
4751
return payday.strftime("%Y-%m-%d")
48-
elif payday == today:
49-
return payday.strftime("%Y-%m-%d")
50-
else:
51-
return "Unknown"
52+
return "Unknown"
5253

5354
@property
5455
def extra_state_attributes(self) -> dict:
@@ -65,3 +66,54 @@ def device_info(self):
6566
"manufacturer": CONF_MANUFACTURER,
6667
"model": CONF_MODEL,
6768
}
69+
70+
71+
class IsItPaydayDaysToSensor(CoordinatorEntity, SensorEntity):
72+
_attr_device_class = SensorDeviceClass.DURATION
73+
_attr_native_unit_of_measurement = "d"
74+
_attr_state_class = "measurement"
75+
_attr_entity_category = EntityCategory.DIAGNOSTIC
76+
77+
def __init__(self, coordinator: DataUpdateCoordinator, entry_id: str, instance_name: str):
78+
super().__init__(coordinator)
79+
self._attr_unique_id = f"{entry_id}_days_to"
80+
self._attr_name = f"{instance_name}: Days to"
81+
self._attr_icon = ICON_DAYS_TO
82+
self._instance_name = instance_name
83+
self._entry_id = entry_id
84+
85+
@property
86+
def native_value(self) -> int | None:
87+
payday = self.coordinator.data.get("payday_next")
88+
if not payday:
89+
return None
90+
91+
try:
92+
if isinstance(payday, str):
93+
payday = date.fromisoformat(payday)
94+
95+
now = datetime.now().date()
96+
if payday <= now:
97+
return 0
98+
99+
delta = payday - now
100+
return delta.days
101+
except Exception as e:
102+
_LOGGER.exception("Error calculating days to payday: %s", e)
103+
return None
104+
105+
@property
106+
def extra_state_attributes(self) -> dict:
107+
return {
108+
"source": "IsItPayday DataUpdateCoordinator",
109+
"raw_data": str(self.coordinator.data),
110+
}
111+
112+
@property
113+
def device_info(self):
114+
return {
115+
"identifiers": {(DOMAIN, self._entry_id)},
116+
"name": self._instance_name,
117+
"manufacturer": CONF_MANUFACTURER,
118+
"model": CONF_MODEL,
119+
}

0 commit comments

Comments
 (0)