Skip to content

Commit

Permalink
Merge pull request #154 from basbruss/sunset-default-time
Browse files Browse the repository at this point in the history
Toggle position change at end time
  • Loading branch information
basbruss authored May 16, 2024
2 parents 967bece + 83c9c02 commit 6db6e57
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 17 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ Tilted blinds will only defect from the above approach if the inside temperature
| Manual Override reset Timer | False | | Resets duration timer each time the position changes while the manual control status is active |
| End Time | `"00:00:00"` | | Latest time a cover can be adjusted each day |
| End Time Entity | None | | The latest moment a cover may be changed . *Overrides the `end_time` value* |
| Adjust at end time | `False` | | Make sure to always update the position to the default setting at the end time. |

### Climate

Expand Down
19 changes: 18 additions & 1 deletion custom_components/adaptive_cover/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,22 @@
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.event import (
async_track_point_in_time,
async_track_state_change_event,
)

from .const import (
CONF_END_ENTITY,
CONF_END_TIME,
CONF_ENTITIES,
CONF_PRESENCE_ENTITY,
CONF_RETURN_SUNSET,
CONF_TEMP_ENTITY,
CONF_WEATHER_ENTITY,
DOMAIN,
)
from .coordinator import AdaptiveDataUpdateCoordinator
from .helpers import get_datetime_from_str

PLATFORMS = [Platform.SENSOR, Platform.SWITCH, Platform.BINARY_SENSOR, Platform.BUTTON]
CONF_SUN = ["sun.sun"]
Expand All @@ -37,7 +42,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data.setdefault(DOMAIN, {})

coordinator = AdaptiveDataUpdateCoordinator(hass)

end_time=None
_temp_entity = entry.options.get(CONF_TEMP_ENTITY)
_presence_entity = entry.options.get(CONF_PRESENCE_ENTITY)
_weather_entity = entry.options.get(CONF_WEATHER_ENTITY)
Expand All @@ -46,6 +51,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
for entity in [_temp_entity, _presence_entity, _weather_entity]:
if entity is not None:
_entities.append(entity)
_track_end_time = entry.options.get(CONF_RETURN_SUNSET)
_end_time = entry.options.get(CONF_END_TIME)
_end_time_entity = entry.options.get(CONF_END_ENTITY)
if _end_time is not None:
end_time = get_datetime_from_str(_end_time)
if _end_time_entity is not None:
end_time = get_datetime_from_str(_end_time_entity)

entry.async_on_unload(
async_track_state_change_event(
Expand All @@ -63,6 +75,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)
)

if _track_end_time and end_time is not None:
entry.async_on_unload(
async_track_point_in_time(hass, coordinator.async_timed_refresh, end_time)
)

await coordinator.async_config_entry_first_refresh()
hass.data[DOMAIN][entry.entry_id] = coordinator

Expand Down
2 changes: 2 additions & 0 deletions custom_components/adaptive_cover/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
CONF_MODE,
CONF_OUTSIDETEMP_ENTITY,
CONF_PRESENCE_ENTITY,
CONF_RETURN_SUNSET,
CONF_SENSOR_TYPE,
CONF_START_ENTITY,
CONF_START_TIME,
Expand Down Expand Up @@ -284,6 +285,7 @@
vol.Optional(CONF_END_ENTITY): selector.EntitySelector(
selector.EntitySelectorConfig(domain=["sensor", "input_datetime"])
),
vol.Optional(CONF_RETURN_SUNSET, default=False): bool
}
)

Expand Down
1 change: 1 addition & 0 deletions custom_components/adaptive_cover/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
CONF_START_ENTITY = "start_entity"
CONF_END_TIME = "end_time"
CONF_END_ENTITY = "end_entity"
CONF_RETURN_SUNSET = "return_sunset"
CONF_MANUAL_OVERRIDE_DURATION = "manual_override_duration"
CONF_MANUAL_OVERRIDE_RESET = "manual_override_reset"

Expand Down
40 changes: 33 additions & 7 deletions custom_components/adaptive_cover/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ def __init__(self, hass: HomeAssistant) -> None: # noqa: D107
self.state_change = False
self.cover_state_change = False
self.first_refresh = False
self.timed_refresh = False
self.state_change_data: StateChangedData | None = None
self.manager = AdaptiveCoverManager(self.manual_duration)
self.wait_for_target = {}
Expand All @@ -122,6 +123,21 @@ async def async_config_entry_first_refresh(self) -> None:
await super().async_config_entry_first_refresh()
_LOGGER.debug("Config entry first refresh")

async def async_timed_refresh(self, event) -> None:
"""Control state at end time."""

if self.end_time is not None:
time = self.end_time
if self.end_time_entity is not None:
time = get_safe_state(self.hass, self.end_time_entity)
time_check = dt.datetime.now() + dt.timedelta(hours=2) - get_datetime_from_str(time)
if time is not None and (time_check <= dt.timedelta(seconds=1)) :
self.timed_refresh = True
_LOGGER.debug("Timed refresh triggered")
await self.async_refresh()
else:
_LOGGER.debug("Time not equal to end time")

async def async_check_entity_state_change(
self, event: Event[EventStateChangedData]
) -> None:
Expand Down Expand Up @@ -222,7 +238,7 @@ async def _async_update_data(self) -> AdaptiveCoverData:

await self.manager.reset_if_needed()

if self.control_toggle and self.state_change:
if self.state_change and self.control_toggle:
for cover in self.entities:
await self.async_handle_call_service(cover)
self.state_change = False
Expand All @@ -237,6 +253,12 @@ async def _async_update_data(self) -> AdaptiveCoverData:
await self.async_set_position(cover)
self.first_refresh = False

if self.timed_refresh and self.control_toggle:
for cover in self.entities:
await self.async_set_manual_position(cover, self.config_entry.options.get(CONF_SUNSET_POS))
self.timed_refresh = False


return AdaptiveCoverData(
climate_mode_toggle=self.switch_mode,
states={
Expand Down Expand Up @@ -271,19 +293,23 @@ async def async_handle_call_service(self, entity):
await self.async_set_position(entity)

async def async_set_position(self, entity):
"""Call service to set cover position."""
await self.async_set_manual_position(entity, self.state)

async def async_set_manual_position(self, entity, position):
"""Call service to set cover position."""
service = SERVICE_SET_COVER_POSITION
service_data = {}
service_data[ATTR_ENTITY_ID] = entity

if self._cover_type == "cover_tilt":
service = SERVICE_SET_COVER_TILT_POSITION
service_data[ATTR_TILT_POSITION] = self.state
service_data[ATTR_TILT_POSITION] = position
else:
service_data[ATTR_POSITION] = self.state
service_data[ATTR_POSITION] = position

self.wait_for_target[entity] = True
self.target_call[entity] = self.state
self.target_call[entity] = position
await self.hass.services.async_call(COVER_DOMAIN, service, service_data)
_LOGGER.debug("Run %s with data %s", service, service_data)

Expand Down Expand Up @@ -334,12 +360,12 @@ def before_end_time(self):
time = get_datetime_from_str(
get_safe_state(self.hass, self.end_time_entity)
)
now = dt.datetime.now(dt.UTC)
return now <= time
now = dt.datetime.now()
return now < time
if self.end_time is not None:
time = get_datetime_from_str(self.end_time).time()
now = dt.datetime.now().time()
return now <= time
return now < time
return True

def check_position(self, entity):
Expand Down
8 changes: 5 additions & 3 deletions custom_components/adaptive_cover/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"manual_override_duration": "Duration of manual override",
"manual_override_reset": "Reset Manual override duration",
"end_time": "End time",
"end_entity": "Entity indicating ending time"
"end_entity": "Entity indicating ending time",
"return_sunset": "Always adjust position to sunset default at end time; Useful if end time is before actual sunset"
},
"data_description": {
"delta_position": "Minimum change in position required before adjusting the cover's position",
Expand Down Expand Up @@ -185,7 +186,8 @@
"manual_override_duration": "Duration of manual override",
"manual_override_reset": "Reset Manual override duration",
"end_time": "End time",
"end_entity": "Entity indicating ending time"
"end_entity": "Entity indicating ending time",
"return_sunset": "Always adjust position to sunset default at end time; Useful if end time is before actual sunset"
},
"data_description": {
"delta_position": "Minimum change in position required before adjusting the cover's position",
Expand All @@ -195,7 +197,7 @@
"manual_override_duration": "The duration of manual control before resetting to automatic control",
"manual_override_reset": "Option to reset the manual override duration after each manual adjustment; if disabled, the duration only applies to the first manual adjustment",
"end_time": "Ending time for each day; after this time, the cover will remain stationary",
"end_entity": "Entity representing ending time state, overriding the fixed end time set above. Useful for automating the end time with an entity"
"end_entity": "Ensure that the position is consistently adjusted to the default sunset setting by the end time. This is particularly beneficial when the end time precedes the actual sunset."
}
},
"vertical": {
Expand Down
2 changes: 1 addition & 1 deletion custom_components/adaptive_cover/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ async def async_turn_on(self, **kwargs: Any) -> None:
setattr(self.coordinator, self._key, True)
if self._key == "control_toggle" and kwargs.get("added") is not True:
for entity in self.coordinator.entities:
if not self.coordinator.manager.is_cover_manual(entity) and self.coordinator.after_start_time:
if not self.coordinator.manager.is_cover_manual(entity) and self.coordinator.check_adaptive_time:
await self.coordinator.async_set_position(entity)
await self.coordinator.async_refresh()
self.schedule_update_ha_state()
Expand Down
8 changes: 5 additions & 3 deletions custom_components/adaptive_cover/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"manual_override_duration": "Duration of manual override",
"manual_override_reset": "Reset Manual override duration",
"end_time": "End time",
"end_entity": "Entity indicating ending time"
"end_entity": "Entity indicating ending time",
"return_sunset": "Always adjust position to sunset default at end time; Useful if end time is before actual sunset"
},
"data_description": {
"delta_position": "Minimum change in position required before adjusting the cover's position",
Expand Down Expand Up @@ -185,7 +186,8 @@
"manual_override_duration": "Duration of manual override",
"manual_override_reset": "Reset Manual override duration",
"end_time": "End time",
"end_entity": "Entity indicating ending time"
"end_entity": "Entity indicating ending time",
"return_sunset": "Always adjust position to sunset default at end time; Useful if end time is before actual sunset"
},
"data_description": {
"delta_position": "Minimum change in position required before adjusting the cover's position",
Expand All @@ -195,7 +197,7 @@
"manual_override_duration": "The duration of manual control before resetting to automatic control",
"manual_override_reset": "Option to reset the manual override duration after each manual adjustment; if disabled, the duration only applies to the first manual adjustment",
"end_time": "Ending time for each day; after this time, the cover will remain stationary",
"end_entity": "Entity representing ending time state, overriding the fixed end time set above. Useful for automating the end time with an entity"
"end_entity": "Ensure that the position is consistently adjusted to the default sunset setting by the end time. This is particularly beneficial when the end time precedes the actual sunset."
}
},
"vertical": {
Expand Down
8 changes: 6 additions & 2 deletions custom_components/adaptive_cover/translations/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"manual_override_duration": "Duur van handmatige overschrijving",
"manual_override_reset": "Reset handmatige overschrijvingsduur",
"end_time": "Eindtijd",
"end_entity": "Entiteit die eindtijd aangeeft"
"end_entity": "Entiteit die eindtijd aangeeft",
"return_sunset": "Zorg ervoor dat de positie altijd wordt aangepast naar de standaard zonsonderganginstelling tegen het eindtijdstip. Dit is vooral handig wanneer het eindtijdstip voor de daadwerkelijke zonsondergang valt."
},
"data_description": {
"delta_position": "Minimale verandering in positie vereist voordat de positie van de bekleding wordt aangepast",
Expand Down Expand Up @@ -190,7 +191,10 @@
"start_time": "Starttijd voor elke dag; voor deze tijd blijft de bekleding stationair",
"start_entity": "Entiteit die de status van de starttijd vertegenwoordigt, die de hierboven ingestelde statische starttijd overschrijft. Handig voor het automatiseren van de starttijd met een entiteit",
"manual_override_duration": "De duur van handmatige bediening voordat wordt teruggekeerd naar automatische bediening",
"manual_override_reset": "Optie om de duur van handmatige overschrijving na elke handmatige aanpassing te resetten; als dit is uitgeschakeld, geldt de duur alleen voor de eerste handmatige aanpassing"
"manual_override_reset": "Optie om de duur van handmatige overschrijving na elke handmatige aanpassing te resetten; als dit is uitgeschakeld, geldt de duur alleen voor de eerste handmatige aanpassing",
"end_time": "Eindtijd",
"end_entity": "Entiteit die eindtijd aangeeft",
"return_sunset": "Zorg ervoor dat de positie altijd wordt aangepast naar de standaard zonsonderganginstelling tegen het eindtijdstip. Dit is vooral handig wanneer het eindtijdstip voor de daadwerkelijke zonsondergang valt."
}
},
"vertical": {
Expand Down

0 comments on commit 6db6e57

Please sign in to comment.