-
-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #26 from MislavMandaric/feat/schedule-backend
Support for scheduler-card
- Loading branch information
Showing
6 changed files
with
213 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
from datetime import datetime, timedelta | ||
|
||
from vaillant_netatmo_api.thermostat import Program, TimeSlot | ||
|
||
|
||
def map_program_to_schedule( | ||
schedule_entity_id: str, profile_entity_id: str, program: Program | ||
) -> dict: | ||
daily_slots = program.get_timeslots_for_today() | ||
|
||
return { | ||
"schedule_id": program.id, | ||
"weekdays": ["daily"], | ||
"timeslots": map_timetable_to_timeslots(profile_entity_id, daily_slots), | ||
"repeat_type": "repeat", | ||
"name": program.name, | ||
"enabled": program.selected, | ||
"next_entries": map_timetable_to_next_entries(daily_slots), | ||
"timestamps": map_timetable_to_timestamps(daily_slots), | ||
"entity_id": schedule_entity_id, | ||
"tags": [], | ||
} | ||
|
||
|
||
def map_timetable_to_timestamps(timetable: list[TimeSlot]) -> list[str]: | ||
timestamps = [] | ||
|
||
today = datetime.now().date() | ||
tomorrow = today + timedelta(days=1) | ||
|
||
for time_slot in timetable: | ||
d = datetime.combine(today, time_slot.time) | ||
|
||
if time_slot.is_already_started: | ||
d = datetime.combine(tomorrow, time_slot.time) | ||
|
||
timestamps.append(d.isoformat()) | ||
|
||
return timestamps | ||
|
||
|
||
def map_timetable_to_next_entries(timetable: list[TimeSlot]) -> list[int]: | ||
previous_entries = [] | ||
next_entries = [] | ||
|
||
for i, time_slot in enumerate(timetable): | ||
if time_slot.is_already_started: | ||
previous_entries.append(i) | ||
else: | ||
next_entries.append(i) | ||
|
||
return next_entries + previous_entries | ||
|
||
|
||
def map_timetable_to_timeslots( | ||
profile_entity_id: str, timetable: list[TimeSlot] | ||
) -> list[dict]: | ||
r = [] | ||
|
||
for i, time_slot in enumerate(timetable): | ||
next_time_slot = timetable[(i + 1) % len(timetable)] | ||
r.append( | ||
{ | ||
"start": format_time(time_slot), | ||
"stop": format_time(next_time_slot), | ||
"actions": [ | ||
{ | ||
"service": "select.select_option", | ||
"entity_id": profile_entity_id, | ||
"service_data": {"option": time_slot.id.name}, | ||
} | ||
], | ||
} | ||
) | ||
|
||
return r | ||
|
||
|
||
def format_time(time_slot: TimeSlot) -> str: | ||
"""Returns a formatted start time for the time slot.""" | ||
|
||
return time_slot.time.isoformat() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import logging | ||
|
||
import voluptuous as vol | ||
from homeassistant.helpers import config_validation as cv | ||
from homeassistant.components.http import HomeAssistantView | ||
from homeassistant.components.http.data_validator import RequestDataValidator | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.components.websocket_api import ( | ||
decorators, | ||
ActiveConnection, | ||
) | ||
from homeassistant.helpers.dispatcher import async_dispatcher_connect | ||
from homeassistant.helpers.entity_registry import EntityRegistry | ||
|
||
from vaillant_netatmo_api.thermostat import Program | ||
|
||
from .const import DOMAIN, SWITCH, SELECT | ||
from .entity import VaillantCoordinator | ||
from .schedule import map_program_to_schedule | ||
|
||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
@decorators.websocket_command( | ||
{ | ||
vol.Required("type"): f"{DOMAIN}", | ||
} | ||
) | ||
@decorators.async_response | ||
async def websocket_get_schedules( | ||
hass: HomeAssistant, | ||
connection: ActiveConnection, | ||
msg: dict, | ||
) -> None: | ||
"""Publish scheduler list data.""" | ||
|
||
er: EntityRegistry = await hass.helpers.entity_registry.async_get_registry() | ||
|
||
schedule: list[dict] = [] | ||
for entry_id in hass.data[DOMAIN].keys(): | ||
coordinator: VaillantCoordinator = hass.data[DOMAIN][entry_id] | ||
for program in coordinator.data.programs.values(): | ||
schedule_entity_id = er.async_get_entity_id(SWITCH, DOMAIN, program.id) | ||
profile_entity_id = er.async_get_entity_id(SELECT, DOMAIN, program.id) | ||
schedule_item = map_program_to_schedule( | ||
schedule_entity_id, profile_entity_id, program | ||
) | ||
schedule.append(schedule_item) | ||
|
||
connection.send_result(msg["id"], schedule) | ||
|
||
|
||
@decorators.websocket_command( | ||
{ | ||
vol.Required("type"): f"{DOMAIN}/item", | ||
vol.Required("schedule_id"): cv.string, | ||
} | ||
) | ||
@decorators.async_response | ||
async def websocket_get_schedule_item( | ||
hass: HomeAssistant, | ||
connection: ActiveConnection, | ||
msg: dict, | ||
) -> None: | ||
"""Publish scheduler list data.""" | ||
|
||
er: EntityRegistry = await hass.helpers.entity_registry.async_get_registry() | ||
|
||
for entry_id in hass.data[DOMAIN].keys(): | ||
coordinator: VaillantCoordinator = hass.data[DOMAIN][entry_id] | ||
|
||
program = coordinator.data.programs.get(msg["schedule_id"]) | ||
if program is not None: | ||
schedule_entity_id = er.async_get_entity_id(SWITCH, DOMAIN, program.id) | ||
profile_entity_id = er.async_get_entity_id(SELECT, DOMAIN, program.id) | ||
schedule_item = map_program_to_schedule( | ||
schedule_entity_id, profile_entity_id, program | ||
) | ||
connection.send_result(msg["id"], schedule_item) | ||
break | ||
|
||
|
||
@decorators.websocket_command( | ||
{ | ||
vol.Required("type"): f"{DOMAIN}_updated", | ||
} | ||
) | ||
@decorators.async_response | ||
async def websocket_schedule_item_updated( | ||
hass: HomeAssistant, | ||
connection: ActiveConnection, | ||
msg: dict, | ||
) -> None: | ||
"""Publish scheduler list data.""" | ||
|
||
connection.send_result(msg["id"]) | ||
|
||
|
||
@decorators.websocket_command( | ||
{ | ||
vol.Required("type"): f"{DOMAIN}/tags", | ||
} | ||
) | ||
@decorators.async_response | ||
async def websocket_get_tags( | ||
hass: HomeAssistant, | ||
connection: ActiveConnection, | ||
msg: dict, | ||
) -> None: | ||
"""Publish tag list data.""" | ||
|
||
connection.send_result(msg["id"], []) | ||
|
||
|
||
async def async_register_websockets(hass: HomeAssistant) -> None: | ||
hass.components.websocket_api.async_register_command(websocket_get_schedules) | ||
hass.components.websocket_api.async_register_command(websocket_get_schedule_item) | ||
hass.components.websocket_api.async_register_command( | ||
websocket_schedule_item_updated | ||
) | ||
hass.components.websocket_api.async_register_command(websocket_get_tags) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters