diff --git a/homeassistant/components/lovelace/__init__.py b/homeassistant/components/lovelace/__init__.py index 9102830e82f..e81af9e0d27 100644 --- a/homeassistant/components/lovelace/__init__.py +++ b/homeassistant/components/lovelace/__init__.py @@ -28,6 +28,7 @@ WS_TYPE_GET_CARD = 'lovelace/config/card/get' WS_TYPE_UPDATE_CARD = 'lovelace/config/card/update' WS_TYPE_ADD_CARD = 'lovelace/config/card/add' WS_TYPE_MOVE_CARD = 'lovelace/config/card/move' +WS_TYPE_DELETE_CARD = 'lovelace/config/card/delete' SCHEMA_GET_LOVELACE_UI = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ vol.Required('type'): vol.Any(WS_TYPE_GET_LOVELACE_UI, @@ -65,6 +66,11 @@ SCHEMA_MOVE_CARD = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ vol.Optional('new_view_id'): str, }) +SCHEMA_DELETE_CARD = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({ + vol.Required('type'): WS_TYPE_DELETE_CARD, + vol.Required('card_id'): str, +}) + class WriteError(HomeAssistantError): """Error writing the data.""" @@ -312,6 +318,22 @@ def move_card_view(fname: str, card_id: str, view_id: str, save_yaml(fname, config) +def delete_card(fname: str, card_id: str, position: int = None): + """Delete a card from view.""" + config = load_yaml(fname) + for view in config.get('views', []): + for card in view.get('cards', []): + if str(card.get('id')) != card_id: + continue + cards = view.get('cards') + cards.pop(cards.index(card)) + save_yaml(fname, config) + return + + raise CardNotFoundError( + "Card with ID: {} was not found in {}.".format(card_id, fname)) + + async def async_setup(hass, config): """Set up the Lovelace commands.""" # Backwards compat. Added in 0.80. Remove after 0.85 @@ -339,6 +361,10 @@ async def async_setup(hass, config): WS_TYPE_MOVE_CARD, websocket_lovelace_move_card, SCHEMA_MOVE_CARD) + hass.components.websocket_api.async_register_command( + WS_TYPE_DELETE_CARD, websocket_lovelace_delete_card, + SCHEMA_DELETE_CARD) + return True @@ -481,3 +507,30 @@ async def websocket_lovelace_move_card(hass, connection, msg): message = websocket_api.error_message(msg['id'], *error) connection.send_message(message) + + +@websocket_api.async_response +async def websocket_lovelace_delete_card(hass, connection, msg): + """Delete card from lovelace over websocket and save.""" + error = None + try: + await hass.async_add_executor_job( + delete_card, hass.config.path(LOVELACE_CONFIG_FILE), + msg['card_id']) + message = websocket_api.result_message( + msg['id'] + ) + except FileNotFoundError: + error = ('file_not_found', + 'Could not find ui-lovelace.yaml in your config dir.') + except UnsupportedYamlError as err: + error = 'unsupported_error', str(err) + except CardNotFoundError as err: + error = 'card_not_found', str(err) + except HomeAssistantError as err: + error = 'save_error', str(err) + + if error is not None: + message = websocket_api.error_message(msg['id'], *error) + + connection.send_message(message) diff --git a/tests/components/lovelace/test_init.py b/tests/components/lovelace/test_init.py index 5e486e295fe..21362a32193 100644 --- a/tests/components/lovelace/test_init.py +++ b/tests/components/lovelace/test_init.py @@ -498,7 +498,7 @@ async def test_lovelace_add_card_position(hass, hass_ws_client): async def test_lovelace_move_card_position(hass, hass_ws_client): - """Test add_card command.""" + """Test move_card command.""" await async_setup_component(hass, 'lovelace') client = await hass_ws_client(hass) yaml = YAML(typ='rt') @@ -524,7 +524,7 @@ async def test_lovelace_move_card_position(hass, hass_ws_client): async def test_lovelace_move_card_view(hass, hass_ws_client): - """Test add_card command.""" + """Test move_card to view command.""" await async_setup_component(hass, 'lovelace') client = await hass_ws_client(hass) yaml = YAML(typ='rt') @@ -550,7 +550,7 @@ async def test_lovelace_move_card_view(hass, hass_ws_client): async def test_lovelace_move_card_view_position(hass, hass_ws_client): - """Test add_card command.""" + """Test move_card to view with position command.""" await async_setup_component(hass, 'lovelace') client = await hass_ws_client(hass) yaml = YAML(typ='rt') @@ -574,3 +574,29 @@ async def test_lovelace_move_card_view_position(hass, hass_ws_client): assert msg['id'] == 5 assert msg['type'] == TYPE_RESULT assert msg['success'] + + +async def test_lovelace_delete_card(hass, hass_ws_client): + """Test delete_card command.""" + await async_setup_component(hass, 'lovelace') + client = await hass_ws_client(hass) + yaml = YAML(typ='rt') + + with patch('homeassistant.components.lovelace.load_yaml', + return_value=yaml.load(TEST_YAML_A)), \ + patch('homeassistant.components.lovelace.save_yaml') \ + as save_yaml_mock: + await client.send_json({ + 'id': 5, + 'type': 'lovelace/config/card/delete', + 'card_id': 'test', + }) + msg = await client.receive_json() + + result = save_yaml_mock.call_args_list[0][0][1] + cards = result.mlget(['views', 1, 'cards'], list_ok=True) + assert len(cards) == 2 + assert cards[0]['title'] == 'Example' + assert msg['id'] == 5 + assert msg['type'] == TYPE_RESULT + assert msg['success']