Skip to content

Commit b7ebfa9

Browse files
Antonio ...claude
andcommitted
feat: Presence-based notification filtering (v1.3.19)
Priority Balancer notifications now filtered by car owner presence at home. Only sends notifications when configured person entity state is "home". Features: - Added car_owner person entity selector in config flow (required field) - Priority Balancer notifications check presence before sending - Smart Blocker and Night Charge notifications always sent (critical) - Backward compatible with fail-safe defaults - Comprehensive debug logging for troubleshooting Technical Implementation: - New CONF_CAR_OWNER constant in const.py - Person entity selector in both initial setup and options flow - MobileNotificationService._is_car_owner_home() method - All three components pass car owner to notification service - Updated strings.json with user-friendly descriptions Files Modified: - const.py: Added CONF_CAR_OWNER constant, updated VERSION to 1.3.19 - manifest.json: Updated version to 1.3.19 - config_flow.py: Added person entity selector (required) - strings.json: Added car_owner field with descriptions - utils/mobile_notification_service.py: Presence check logic - priority_balancer.py: Pass car owner entity - automations.py: Pass car owner entity - night_smart_charge.py: Pass car owner entity - CLAUDE.md: Comprehensive documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 032399e commit b7ebfa9

9 files changed

Lines changed: 151 additions & 13 deletions

File tree

‎CLAUDE.md‎

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
77
This is a **Home Assistant custom integration** for intelligent EV charging control. It manages EV charger automation based on solar production, time of day, battery levels, grid import protection, and intelligent priority balancing between EV and home battery charging.
88

99
**Domain:** `ev_smart_charger`
10-
**Current Version:** 1.3.17
10+
**Current Version:** 1.3.19
1111
**Installation:** HACS custom repository or manual installation to `custom_components/ev_smart_charger`
1212

1313
## Development Commands
@@ -753,6 +753,94 @@ async def _set_amperage(self, target_amperage: int):
753753

754754
## Version History
755755

756+
### v1.3.19 (2025-11-06)
757+
**Presence-Based Notification Filtering - Smart Priority Balancer Alerts**
758+
759+
**Feature Overview**:
760+
Priority Balancer notifications are now filtered based on car owner presence at home. Notifications are only sent when the configured car owner person entity state is "home", preventing unnecessary alerts when away.
761+
762+
**Problem Solved**:
763+
- **Previous Behavior** (v1.3.18): Priority Balancer sent notifications on every state change (EV/Home/EV_Free), regardless of whether anyone was home to receive them
764+
- **User Impact**: Notification spam when away from home, where alerts about EV/Home battery priority changes are irrelevant
765+
- **New Behavior** (v1.3.19): Priority Balancer notifications only sent when car owner is home
766+
767+
**New Configuration Field**:
768+
- **`car_owner`**: Person entity selector in config flow (notifications step)
769+
- Domain: `person`
770+
- Required field (mandatory)
771+
- Example: `person.john`
772+
- Appears alongside `notify_services` selection
773+
- Available in both initial setup and options flow reconfiguration
774+
775+
**Logic Changes**:
776+
777+
**MobileNotificationService Updates**:
778+
- Added `car_owner_entity` parameter to constructor (optional for backward compatibility)
779+
- New method: `_is_car_owner_home()` - checks if person.state == "home"
780+
- `send_priority_change_notification()` now checks presence before sending
781+
- If car owner not home: notification skipped with debug log entry
782+
- If car owner entity not configured: notifications always sent (backward compatible)
783+
- If car owner entity unavailable: notifications always sent (fail-safe default)
784+
785+
**Notification Filtering Behavior**:
786+
- ✅ **Priority Balancer**: Filtered by car owner presence (NEW)
787+
- ❌ **Smart Blocker**: NOT filtered (charger blocking is critical regardless of presence)
788+
- ❌ **Night Smart Charge**: NOT filtered (useful to know charging started even when away)
789+
790+
**Component Updates**:
791+
All three components updated to pass car owner entity to MobileNotificationService:
792+
1. `priority_balancer.py`: Updated constructor call with `config.get(CONF_CAR_OWNER)`
793+
2. `automations.py` (Smart Blocker): Updated constructor call (no filtering logic)
794+
3. `night_smart_charge.py`: Updated constructor call (no filtering logic)
795+
796+
**Files Modified**:
797+
- `const.py`: Added `CONF_CAR_OWNER = "car_owner"` constant
798+
- `config_flow.py`:
799+
- Imported `CONF_CAR_OWNER`
800+
- Added person entity selector to notifications step (both initial and options flow)
801+
- Field set as `vol.Required` (mandatory)
802+
- `strings.json`:
803+
- Added `car_owner` field to notifications step data/descriptions
804+
- Updated descriptions to mention smart filtering feature
805+
- Added helpful tooltips explaining presence-based filtering
806+
- `utils/mobile_notification_service.py`:
807+
- Added `car_owner_entity` parameter to `__init__` (optional, defaults to None)
808+
- New method: `_is_car_owner_home()` with comprehensive logging
809+
- Updated `send_priority_change_notification()` to check presence
810+
- Backward compatible: returns True if entity not configured or unavailable
811+
- `priority_balancer.py`: Updated MobileNotificationService instantiation
812+
- `automations.py`: Updated MobileNotificationService instantiation
813+
- `night_smart_charge.py`: Updated MobileNotificationService instantiation
814+
815+
**User Experience**:
816+
- **Setup**: Users select car owner person entity during notifications configuration
817+
- **Reconfiguration**: Options flow allows updating car owner selection
818+
- **UI Labels**: Clear descriptions explain the smart filtering feature
819+
- **Logging**: Debug logs show presence check results for troubleshooting
820+
- **Fail-Safe**: If person entity unavailable, notifications still sent (prevents silent failures)
821+
822+
**Technical Implementation**:
823+
```python
824+
def _is_car_owner_home(self) -> bool:
825+
"""Check if car owner is home."""
826+
if not self.car_owner_entity:
827+
return True # Backward compatibility
828+
829+
state = self.hass.states.get(self.car_owner_entity)
830+
if not state:
831+
return True # Fail-safe
832+
833+
return state.state == "home"
834+
```
835+
836+
**Benefits**:
837+
- ✅ Reduces notification noise when away from home
838+
- ✅ Makes Priority Balancer notifications contextually relevant
839+
- ✅ Required field ensures feature always configured for new users
840+
- ✅ Backward compatible (optional parameter with safe defaults)
841+
- ✅ User-controlled via person entity (leverages HA's presence detection)
842+
- ✅ Easy to extend to other notification types in future
843+
756844
### v1.3.18 (2025-11-06)
757845
**Car Ready Time Support - Extend Charging Past Sunrise When Needed**
758846

‎custom_components/ev_smart_charger/automations.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
CONF_EV_CHARGER_SWITCH,
1414
CONF_EV_CHARGER_STATUS,
1515
CONF_NOTIFY_SERVICES,
16+
CONF_CAR_OWNER,
1617
CHARGER_STATUS_CHARGING,
1718
HELPER_NIGHT_CHARGE_TIME_SUFFIX,
1819
SMART_BLOCKER_ENFORCEMENT_TIMEOUT,
@@ -65,7 +66,7 @@ def __init__(
6566
self._registry_service = EntityRegistryService(hass, entry_id)
6667
self._notification_service = NotificationService(hass)
6768
self._mobile_notifier = MobileNotificationService(
68-
hass, config.get(CONF_NOTIFY_SERVICES, []), entry_id
69+
hass, config.get(CONF_NOTIFY_SERVICES, []), entry_id, config.get(CONF_CAR_OWNER)
6970
)
7071
self._astral_service = AstralTimeService(hass)
7172

‎custom_components/ev_smart_charger/config_flow.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
CONF_GRID_IMPORT,
1919
CONF_PV_FORECAST,
2020
CONF_NOTIFY_SERVICES,
21+
CONF_CAR_OWNER,
2122
)
2223

2324
class EVSCConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
@@ -165,6 +166,9 @@ async def async_step_notifications(self, user_input: dict[str, Any] | None = Non
165166
mode=selector.SelectSelectorMode.DROPDOWN
166167
)
167168
),
169+
vol.Required(CONF_CAR_OWNER): selector.EntitySelector(
170+
selector.EntitySelectorConfig(domain="person")
171+
),
168172
})
169173

170174
return self.async_show_form(
@@ -352,6 +356,12 @@ async def async_step_notifications(self, user_input: dict[str, Any] | None = Non
352356
mode=selector.SelectSelectorMode.DROPDOWN
353357
)
354358
),
359+
vol.Required(
360+
CONF_CAR_OWNER,
361+
default=current_data.get(CONF_CAR_OWNER)
362+
): selector.EntitySelector(
363+
selector.EntitySelectorConfig(domain="person")
364+
),
355365
})
356366

357367
return self.async_show_form(

‎custom_components/ev_smart_charger/const.py‎

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

33
# ========== INTEGRATION METADATA ==========
44
DOMAIN = "ev_smart_charger"
5-
VERSION = "1.3.18"
5+
VERSION = "1.3.19"
66
DEFAULT_NAME = "EV Smart Charger"
77

88
# ========== PLATFORMS ==========
@@ -61,6 +61,7 @@
6161

6262
# Mobile Notifications
6363
CONF_NOTIFY_SERVICES = "notify_services"
64+
CONF_CAR_OWNER = "car_owner" # Person entity for car owner (v1.3.19+)
6465

6566
# ========== HELPER ENTITY SUFFIXES ==========
6667

‎custom_components/ev_smart_charger/manifest.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"domain": "ev_smart_charger",
33
"name": "EV Smart Charger",
4-
"version": "1.3.18",
4+
"version": "1.3.19",
55
"documentation": "https://github.com/antbald/ha-ev-smart-charger",
66
"issue_tracker": "https://github.com/antbald/ha-ev-smart-charger/issues",
77
"codeowners": ["@antbald"],

‎custom_components/ev_smart_charger/night_smart_charge.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
CONF_SOC_HOME,
1313
CONF_PV_FORECAST,
1414
CONF_NOTIFY_SERVICES,
15+
CONF_CAR_OWNER,
1516
CHARGER_STATUS_FREE,
1617
NIGHT_CHARGE_MODE_BATTERY,
1718
NIGHT_CHARGE_MODE_GRID,
@@ -67,7 +68,7 @@ def __init__(
6768
self.logger = EVSCLogger("NIGHT SMART CHARGE")
6869
self._astral_service = AstralTimeService(hass)
6970
self._mobile_notifier = MobileNotificationService(
70-
hass, config.get(CONF_NOTIFY_SERVICES, []), entry_id
71+
hass, config.get(CONF_NOTIFY_SERVICES, []), entry_id, config.get(CONF_CAR_OWNER)
7172
)
7273

7374
# User-configured entities

‎custom_components/ev_smart_charger/priority_balancer.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
CONF_SOC_CAR,
77
CONF_SOC_HOME,
88
CONF_NOTIFY_SERVICES,
9+
CONF_CAR_OWNER,
910
DEFAULT_EV_MIN_SOC_WEEKDAY,
1011
DEFAULT_EV_MIN_SOC_WEEKEND,
1112
DEFAULT_HOME_MIN_SOC,
@@ -44,7 +45,7 @@ def __init__(self, hass: HomeAssistant, entry_id: str, config: dict):
4445

4546
# Mobile notification service
4647
self._mobile_notifier = MobileNotificationService(
47-
hass, config.get(CONF_NOTIFY_SERVICES, []), entry_id
48+
hass, config.get(CONF_NOTIFY_SERVICES, []), entry_id, config.get(CONF_CAR_OWNER)
4849
)
4950

5051
# Cached state

‎custom_components/ev_smart_charger/strings.json‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,14 @@
5555
},
5656
"notifications": {
5757
"title": "📱 Mobile Notifications - Step {step}/{total_steps}",
58-
"description": "**🔔 Stay Informed with Smart Notifications**\n\n**Get instant alerts on your mobile devices for:**\n\n🚫 **Smart Blocker Events**\n• Charging blocked outside permitted window\n• Includes reason (nighttime, insufficient solar)\n• Continuous monitoring active\n\n⚖️ **Priority Balancer Changes**\n• Priority switched: EV ↔ Home ↔ EV_Free\n• Current SOC levels (EV & Home)\n• Daily targets status\n\n🌙 **Night Smart Charge**\n• Charging started (battery/grid mode)\n• Solar forecast information\n• Target SOC progress\n\n---\n\n💡 **How it works:**\n• Select one or more mobile devices below\n• All notifications use title **\"BORGO\"** for easy filtering\n• After setup, use dashboard switches to enable/disable per feature\n• Notifications are **enabled by default**, disable if not needed\n\n⚠️ **Optional:** Leave empty to skip mobile notifications (can configure later)",
58+
"description": "**🔔 Stay Informed with Smart Notifications**\n\n**Get instant alerts on your mobile devices for:**\n\n🚫 **Smart Blocker Events**\n• Charging blocked outside permitted window\n• Includes reason (nighttime, insufficient solar)\n• Continuous monitoring active\n\n⚖️ **Priority Balancer Changes**\n• Priority switched: EV ↔ Home ↔ EV_Free\n• Current SOC levels (EV & Home)\n• Daily targets status\n• **Smart:** Only notified when car owner is home\n\n🌙 **Night Smart Charge**\n• Charging started (battery/grid mode)\n• Solar forecast information\n• Target SOC progress\n\n---\n\n💡 **How it works:**\n• Select one or more mobile devices below\n• Select the car owner (for presence-based filtering)\n• All notifications use title **\"BORGO\"** for easy filtering\n• After setup, use dashboard switches to enable/disable per feature\n• Notifications are **enabled by default**, disable if not needed\n\n⚠️ **Optional:** Leave notify_services empty to skip notifications (can configure later)",
5959
"data": {
60-
"notify_services": "📱 Select Mobile Devices"
60+
"notify_services": "📱 Select Mobile Devices",
61+
"car_owner": "👤 Car Owner"
6162
},
6263
"data_description": {
63-
"notify_services": "**Available:** All Home Assistant Mobile App devices\n**Examples:** mobile_app_johns_iphone, mobile_app_pixel_8\n\n✅ **Pro Tip:** Select all household devices for comprehensive monitoring"
64+
"notify_services": "**Available:** All Home Assistant Mobile App devices\n**Examples:** mobile_app_johns_iphone, mobile_app_pixel_8\n\n✅ **Pro Tip:** Select all household devices for comprehensive monitoring",
65+
"car_owner": "**Required:** Select the person entity representing the car owner\n**Domain:** person\n**Example:** person.john\n\n💡 **Smart Filtering:** Priority Balancer notifications will only be sent when this person is home. Prevents unnecessary alerts when away."
6466
}
6567
}
6668
},
@@ -114,12 +116,14 @@
114116
},
115117
"notifications": {
116118
"title": "📱 Update Mobile Notifications - Step {step}/{total_steps}",
117-
"description": "**🔔 Update Notification Devices**\n\n**Current notification types:**\n• 🚫 Smart Blocker alerts\n• ⚖️ Priority Balancer changes\n• 🌙 Night Smart Charge status\n\n💡 **Notification title:** BORGO (for filtering)\n💡 **Dashboard control:** Use switches to enable/disable per feature\n\n⚠️ **Leave empty** to disable all mobile notifications\n\n---",
119+
"description": "**🔔 Update Notification Devices**\n\n**Current notification types:**\n• 🚫 Smart Blocker alerts\n• ⚖️ Priority Balancer changes (only when car owner is home)\n• 🌙 Night Smart Charge status\n\n💡 **Notification title:** BORGO (for filtering)\n💡 **Dashboard control:** Use switches to enable/disable per feature\n\n⚠️ **Leave notify_services empty** to disable all mobile notifications\n\n---",
118120
"data": {
119-
"notify_services": "📱 Select Mobile Devices"
121+
"notify_services": "📱 Select Mobile Devices",
122+
"car_owner": "👤 Car Owner"
120123
},
121124
"data_description": {
122-
"notify_services": "**Available:** All Home Assistant Mobile App devices\n**Note:** Changes apply immediately to new notifications"
125+
"notify_services": "**Available:** All Home Assistant Mobile App devices\n**Note:** Changes apply immediately to new notifications",
126+
"car_owner": "**Required:** Person entity for the car owner\n**Domain:** person\n**Smart Filtering:** Priority Balancer notifications only sent when this person is home"
123127
}
124128
}
125129
},

‎custom_components/ev_smart_charger/utils/mobile_notification_service.py‎

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ def __init__(
2525
self,
2626
hass: HomeAssistant,
2727
notify_services: list[str],
28-
entry_id: str
28+
entry_id: str,
29+
car_owner_entity: str = None
2930
):
3031
"""
3132
Initialize Mobile Notification Service.
@@ -34,10 +35,12 @@ def __init__(
3435
hass: Home Assistant instance
3536
notify_services: List of notify service names (e.g., ["mobile_app_pixel_8"])
3637
entry_id: Config entry ID for finding enable/disable switches
38+
car_owner_entity: Person entity ID for car owner (v1.3.19+)
3739
"""
3840
self.hass = hass
3941
self.notify_services = notify_services or []
4042
self.entry_id = entry_id
43+
self.car_owner_entity = car_owner_entity
4144
self._registry_service = EntityRegistryService(hass, entry_id)
4245

4346
async def send_smart_blocker_notification(self, reason: str) -> None:
@@ -87,6 +90,11 @@ async def send_priority_change_notification(
8790
_LOGGER.debug("Priority Balancer notifications disabled, skipping")
8891
return
8992

93+
# Check if car owner is home before sending notification (v1.3.19+)
94+
if not self._is_car_owner_home():
95+
_LOGGER.debug("Car owner not home, skipping Priority Balancer notification")
96+
return
97+
9098
# Map priority to Italian description
9199
priority_map = {
92100
"EV": "EV",
@@ -183,6 +191,30 @@ def _is_notification_enabled(self, suffix: str) -> bool:
183191

184192
return state.state == STATE_ON
185193

194+
def _is_car_owner_home(self) -> bool:
195+
"""
196+
Check if car owner is home (v1.3.19+).
197+
198+
Returns:
199+
True if car owner is home or entity not configured, False otherwise
200+
"""
201+
if not self.car_owner_entity:
202+
_LOGGER.debug("Car owner entity not configured, defaulting to enabled")
203+
return True # Default to enabled if not configured (backward compatibility)
204+
205+
state = self.hass.states.get(self.car_owner_entity)
206+
if not state:
207+
_LOGGER.warning(
208+
f"Car owner entity {self.car_owner_entity} not found, defaulting to enabled"
209+
)
210+
return True # Default to enabled if state unavailable
211+
212+
is_home = state.state == "home"
213+
_LOGGER.debug(
214+
f"Car owner ({self.car_owner_entity}) state: {state.state}, is_home: {is_home}"
215+
)
216+
return is_home
217+
186218
async def _send_notification(
187219
self,
188220
message: str,

0 commit comments

Comments
 (0)