import logging import voluptuous as vol from homeassistant.helpers import config_validation as cv from homeassistant.components import websocket_api from homeassistant.components.http import HomeAssistantView from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.core import callback from homeassistant.components.websocket_api import decorators, async_register_command from homeassistant.helpers.dispatcher import async_dispatcher_connect from . import const from .store import ScheduleEntry _LOGGER = logging.getLogger(__name__) class SchedulesListView(HomeAssistantView): """Login to Home Assistant cloud.""" url = "/api/{}/list".format(const.DOMAIN) name = "api:{}:list".format(const.DOMAIN) async def get(self, request): hass = request.app["hass"] coordinator = hass.data[const.DOMAIN]["coordinator"] schedules = coordinator.async_get_schedules() return self.json(schedules) class SchedulesAddView(HomeAssistantView): """Login to Home Assistant cloud.""" url = "/api/{}/add".format(const.DOMAIN) name = "api:{}:add".format(const.DOMAIN) @RequestDataValidator(const.ADD_SCHEDULE_SCHEMA) async def post(self, request, data): """Handle config update request.""" hass = request.app["hass"] coordinator = hass.data[const.DOMAIN]["coordinator"] coordinator.async_create_schedule(data) return self.json({"success": True}) class SchedulesEditView(HomeAssistantView): """Login to Home Assistant cloud.""" url = "/api/{}/edit".format(const.DOMAIN) name = "api:{}:edit".format(const.DOMAIN) @RequestDataValidator( const.EDIT_SCHEDULE_SCHEMA.extend( {vol.Required(const.ATTR_SCHEDULE_ID): cv.string} ) ) async def post(self, request, data): """Handle config update request.""" hass = request.app["hass"] coordinator = hass.data[const.DOMAIN]["coordinator"] schedule_id = data[const.ATTR_SCHEDULE_ID] del data[const.ATTR_SCHEDULE_ID] coordinator.async_edit_schedule(schedule_id, data) return self.json({"success": True}) class SchedulesRemoveView(HomeAssistantView): """Login to Home Assistant cloud.""" url = "/api/{}/remove".format(const.DOMAIN) name = "api:{}:remove".format(const.DOMAIN) @RequestDataValidator(vol.Schema({vol.Required(const.ATTR_SCHEDULE_ID): cv.string})) async def post(self, request, data): """Handle config update request.""" hass = request.app["hass"] coordinator = hass.data[const.DOMAIN]["coordinator"] coordinator.async_delete_schedule(data[const.ATTR_SCHEDULE_ID]) return self.json({"success": True}) @callback def websocket_get_schedules(hass, connection, msg): """Publish scheduler list data.""" coordinator = hass.data[const.DOMAIN]["coordinator"] schedules = coordinator.async_get_schedules() connection.send_result(msg["id"], schedules) @callback def websocket_get_schedule_item(hass, connection, msg): """Publish scheduler list data.""" coordinator = hass.data[const.DOMAIN]["coordinator"] item = msg[const.ATTR_SCHEDULE_ID] data = coordinator.async_get_schedule(item) connection.send_result(msg["id"], data) @callback def websocket_get_tags(hass, connection, msg): """Publish tag list data.""" coordinator = hass.data[const.DOMAIN]["coordinator"] tags = coordinator.async_get_tags() connection.send_result(msg["id"], tags) @callback @decorators.websocket_command( { vol.Required("type"): const.EVENT, } ) @decorators.async_response async def handle_subscribe_updates(hass, connection, msg): """subscribe listeners when frontend connection is opened""" listeners = [] @callback def async_handle_event_item_created(schedule: ScheduleEntry): """pass data to frontend when backend changes""" connection.send_message( { "id": msg["id"], "type": "event", "event": { # data to pass with event "event": const.EVENT_ITEM_CREATED, "schedule_id": schedule.schedule_id, }, } ) listeners.append( async_dispatcher_connect( hass, const.EVENT_ITEM_CREATED, async_handle_event_item_created ) ) @callback def async_handle_event_item_updated(schedule_id: str): """pass data to frontend when backend changes""" connection.send_message( { "id": msg["id"], "type": "event", "event": { # data to pass with event "event": const.EVENT_ITEM_UPDATED, "schedule_id": schedule_id, }, } ) listeners.append( async_dispatcher_connect( hass, const.EVENT_ITEM_UPDATED, async_handle_event_item_updated ) ) @callback def async_handle_event_item_removed(schedule_id: str): """pass data to frontend when backend changes""" connection.send_message( { "id": msg["id"], "type": "event", "event": { # data to pass with event "event": const.EVENT_ITEM_REMOVED, "schedule_id": schedule_id, }, } ) listeners.append( async_dispatcher_connect( hass, const.EVENT_ITEM_REMOVED, async_handle_event_item_removed ) ) @callback def async_handle_event_timer_updated(schedule_id: str): """pass data to frontend when backend changes""" connection.send_message( { "id": msg["id"], "type": "event", "event": { # data to pass with event "event": const.EVENT_TIMER_UPDATED, "schedule_id": schedule_id, }, } ) listeners.append( async_dispatcher_connect( hass, const.EVENT_TIMER_UPDATED, async_handle_event_timer_updated ) ) @callback def async_handle_event_timer_finished(schedule_id: str): """pass data to frontend when backend changes""" connection.send_message( { "id": msg["id"], "type": "event", "event": { # data to pass with event "event": const.EVENT_TIMER_FINISHED, "schedule_id": schedule_id, }, } ) listeners.append( async_dispatcher_connect( hass, const.EVENT_TIMER_FINISHED, async_handle_event_timer_finished ) ) def unsubscribe_listeners(): """unsubscribe listeners when frontend connection closes""" while len(listeners): listeners.pop()() connection.subscriptions[msg["id"]] = unsubscribe_listeners connection.send_result(msg["id"]) async def async_register_websockets(hass): # expose services hass.http.register_view(SchedulesAddView) hass.http.register_view(SchedulesEditView) hass.http.register_view(SchedulesRemoveView) hass.http.register_view(SchedulesListView) # pass list of schedules to frontend websocket_api.async_register_command( hass, const.DOMAIN, websocket_get_schedules, websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( { vol.Required("type"): const.DOMAIN, } ), ) # pass single schedule to frontend websocket_api.async_register_command( hass, "{}/item".format(const.DOMAIN), websocket_get_schedule_item, websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( { vol.Required("type"): "{}/item".format(const.DOMAIN), vol.Required(const.ATTR_SCHEDULE_ID): cv.string, } ), ) # pass list of tags to frontend websocket_api.async_register_command( hass, "{}/tags".format(const.DOMAIN), websocket_get_tags, websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( { vol.Required("type"): "{}/tags".format(const.DOMAIN), } ), ) # instantiate listener for sending event to frontend on backend change async_register_command(hass, handle_subscribe_updates)