Add save command to lovelace
diff --git a/homeassistant/components/lovelace/__init__.py b/homeassistant/components/lovelace/__init__.py
index 39644bd047b3..e40cb18a2b25 100644
--- a/homeassistant/components/lovelace/__init__.py
+++ b/homeassistant/components/lovelace/__init__.py
@@ -31,6 +31,7 @@
 OLD_WS_TYPE_GET_LOVELACE_UI = 'frontend/lovelace_config'
 WS_TYPE_GET_LOVELACE_UI = 'lovelace/config'
 WS_TYPE_MIGRATE_CONFIG = 'lovelace/config/migrate'
+WS_TYPE_SAVE_CONFIG = 'lovelace/config/save'
 
 WS_TYPE_GET_CARD = 'lovelace/config/card/get'
 WS_TYPE_UPDATE_CARD = 'lovelace/config/card/update'
@@ -53,6 +54,13 @@
     vol.Required('type'): WS_TYPE_MIGRATE_CONFIG,
 })
 
+SCHEMA_SAVE_CONFIG = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
+    vol.Required('type'): WS_TYPE_SAVE_CONFIG,
+    vol.Required('config'): vol.Any(str, Dict),
+    vol.Optional('format', default=FORMAT_JSON):
+        vol.Any(FORMAT_JSON, FORMAT_YAML),
+})
+
 SCHEMA_GET_CARD = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
     vol.Required('type'): WS_TYPE_GET_CARD,
     vol.Required('card_id'): str,
@@ -204,6 +212,13 @@ def migrate_config(fname: str) -> None:
         yaml.save_yaml(fname, config)
 
 
+def save_config(fname: str, config, data_format: str = FORMAT_JSON) -> None:
+    """Save config to file."""
+    if data_format == FORMAT_YAML:
+        config = yaml.yaml_to_object(config)
+    yaml.save_yaml(fname, config)
+
+
 def get_card(fname: str, card_id: str, data_format: str = FORMAT_YAML)\
         -> JSON_TYPE:
     """Load a specific card config for id."""
@@ -422,13 +437,17 @@ async def async_setup(hass, config):
         OLD_WS_TYPE_GET_LOVELACE_UI, websocket_lovelace_config,
         SCHEMA_GET_LOVELACE_UI)
 
+    hass.components.websocket_api.async_register_command(
+        WS_TYPE_GET_LOVELACE_UI, websocket_lovelace_config,
+        SCHEMA_GET_LOVELACE_UI)
+
     hass.components.websocket_api.async_register_command(
         WS_TYPE_MIGRATE_CONFIG, websocket_lovelace_migrate_config,
         SCHEMA_MIGRATE_CONFIG)
 
     hass.components.websocket_api.async_register_command(
-        WS_TYPE_GET_LOVELACE_UI, websocket_lovelace_config,
-        SCHEMA_GET_LOVELACE_UI)
+        WS_TYPE_SAVE_CONFIG, websocket_lovelace_save_config,
+        SCHEMA_SAVE_CONFIG)
 
     hass.components.websocket_api.async_register_command(
         WS_TYPE_GET_CARD, websocket_lovelace_get_card, SCHEMA_GET_CARD)
@@ -516,6 +535,15 @@ async def websocket_lovelace_migrate_config(hass, connection, msg):
         migrate_config, hass.config.path(LOVELACE_CONFIG_FILE))
 
 
+@websocket_api.async_response
+@handle_yaml_errors
+async def websocket_lovelace_save_config(hass, connection, msg):
+    """Save Lovelace UI configuration."""
+    return await hass.async_add_executor_job(
+        save_config, hass.config.path(LOVELACE_CONFIG_FILE), msg['config'],
+        msg.get('format', FORMAT_JSON))
+
+
 @websocket_api.async_response
 @handle_yaml_errors
 async def websocket_lovelace_get_card(hass, connection, msg):
diff --git a/homeassistant/util/ruamel_yaml.py b/homeassistant/util/ruamel_yaml.py
index 8211252a516d..0659e3d80544 100644
--- a/homeassistant/util/ruamel_yaml.py
+++ b/homeassistant/util/ruamel_yaml.py
@@ -1,7 +1,7 @@
 """ruamel.yaml utility functions."""
 import logging
 import os
-from os import O_CREAT, O_TRUNC, O_WRONLY
+from os import O_CREAT, O_TRUNC, O_WRONLY, stat_result
 from collections import OrderedDict
 from typing import Union, List, Dict
 
@@ -104,13 +104,17 @@ def save_yaml(fname: str, data: JSON_TYPE) -> None:
     yaml.indent(sequence=4, offset=2)
     tmp_fname = fname + "__TEMP__"
     try:
-        file_stat = os.stat(fname)
+        try:
+            file_stat = os.stat(fname)
+        except OSError:
+            file_stat = stat_result(
+                (0o644, -1, -1, -1, -1, -1, -1, -1, -1, -1))
         with open(os.open(tmp_fname, O_WRONLY | O_CREAT | O_TRUNC,
                           file_stat.st_mode), 'w', encoding='utf-8') \
                 as temp_file:
             yaml.dump(data, temp_file)
         os.replace(tmp_fname, fname)
-        if hasattr(os, 'chown'):
+        if hasattr(os, 'chown') and file_stat.st_ctime > -1:
             try:
                 os.chown(fname, file_stat.st_uid, file_stat.st_gid)
             except OSError:

## Description:
Adds save command to save the automatically created config to file.

https://github.com/home-assistant/home-assistant-polymer/pull/2091

**Related issue (if applicable):** fixes #<home-assistant issue number goes here>

**Pull request in [home-assistant.io](https://github.com/home-assistant/home-assistant.io) with documentation (if applicable):** home-assistant/home-assistant.io#<home-assistant.io PR number goes here>

## Example entry for `configuration.yaml` (if applicable):
```yaml

```

## Checklist:
  - [ ] The code change is tested and works locally.
  - [ ] Local tests pass with `tox`. **Your PR cannot be merged unless tests pass**
  - [ ] There is no commented out code in this PR.

If user exposed functionality or configuration variables are added/changed:
  - [ ] Documentation added/updated in [home-assistant.io](https://github.com/home-assistant/home-assistant.io)

If the code communicates with devices, web services, or third-party tools:
  - [ ] New dependencies have been added to the `REQUIREMENTS` variable ([example][ex-requir]).
  - [ ] New dependencies are only imported inside functions that use them ([example][ex-import]).
  - [ ] New or updated dependencies have been added to `requirements_all.txt` by running `script/gen_requirements_all.py`.
  - [ ] New files were added to `.coveragerc`.

If the code does not interact with devices:
  - [ ] Tests have been added to verify that the new code works.

[ex-requir]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard.py#L14
[ex-import]: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/keyboard.py#L54
