libweave: Split "base" trait into "device" and "privet".

The original "base" trait has been split into two and those traits
have slightly different definitions (e.g. now device name, description
and location are part of "device" trait's state, instead of the
global device resource).

Updated tests to reflect the new traits.

BUG: None
Change-Id: I9a1a1bfc3c01d67dd0ac519106c5d20c25a62f38
Reviewed-on: https://weave-review.googlesource.com/3146
Reviewed-by: Alex Vakulenko <avakulenko@google.com>
diff --git a/examples/provider/file_config_store.cc b/examples/provider/file_config_store.cc
index af3f047..a9b78b5 100644
--- a/examples/provider/file_config_store.cc
+++ b/examples/provider/file_config_store.cc
@@ -43,6 +43,7 @@
   uname(&uname_data);
 
   settings->firmware_version = uname_data.sysname;
+  settings->serial_number = "";
   settings->oem_name = "Unknown";
   settings->model_name = "Unknown";
   settings->model_id = model_id_;
diff --git a/include/weave/device.h b/include/weave/device.h
index 5d455de..c0c3584 100644
--- a/include/weave/device.h
+++ b/include/weave/device.h
@@ -152,7 +152,7 @@
   // |component| is the name of the component for which commands should be
   // handled.
   // |command_name| is the full command name of the command to handle. e.g.
-  // "base.reboot". Each command can have no more than one handler.
+  // "device.setConfig". Each command can have no more than one handler.
   // Empty |component| and |command_name| sets default handler for all unhanded
   // commands.
   // No new command handlers can be set after default handler was set.
diff --git a/include/weave/provider/test/mock_config_store.h b/include/weave/provider/test/mock_config_store.h
index a7eb374..2a61fc7 100644
--- a/include/weave/provider/test/mock_config_store.h
+++ b/include/weave/provider/test/mock_config_store.h
@@ -28,6 +28,7 @@
     EXPECT_CALL(*this, LoadDefaults(_))
         .WillRepeatedly(testing::Invoke([](Settings* settings) {
           settings->firmware_version = "TEST_FIRMWARE";
+          settings->serial_number = "TEST_SERIAL_NUMBER";
           settings->oem_name = "TEST_OEM";
           settings->model_name = "TEST_MODEL";
           settings->model_id = "ABCDE";
@@ -39,7 +40,7 @@
         }));
     EXPECT_CALL(*this, LoadSettings())
         .WillRepeatedly(Return(R"({
-          "version": 1,
+          "version": 2,
           "device_id": "TEST_DEVICE_ID"
         })"));
     EXPECT_CALL(*this, LoadSettings(_)).WillRepeatedly(Return(""));
diff --git a/include/weave/settings.h b/include/weave/settings.h
index 2ba619c..2dceedb 100644
--- a/include/weave/settings.h
+++ b/include/weave/settings.h
@@ -30,6 +30,7 @@
 struct Settings {
   // Model specific information. Must be set by ConfigStore::LoadDefaults.
   std::string firmware_version;
+  std::string serial_number;
   std::string oem_name;
   std::string model_name;
   std::string model_id;
@@ -47,10 +48,8 @@
   // Options mirrored into "base" state.
   // Maximum role for local anonymous user.
   AuthScope local_anonymous_access_role{AuthScope::kViewer};
-  // If true, allows local discovery using DNS-SD.
-  bool local_discovery_enabled{true};
-  // If true, allows local pairing using Privet API.
-  bool local_pairing_enabled{true};
+  // If true, allows local pairing using Privet API, including mDNS.
+  bool local_access_enabled{true};
 
   // Set of pairing modes supported by device.
   std::set<PairingType> pairing_modes;
diff --git a/src/base_api_handler.cc b/src/base_api_handler.cc
index 312e20b..535fb40 100644
--- a/src/base_api_handler.cc
+++ b/src/base_api_handler.cc
@@ -13,115 +13,116 @@
 namespace weave {
 
 namespace {
-const char kBaseComponent[] = "base";
-const char kBaseTrait[] = "base";
-const char kBaseStateFirmwareVersion[] = "base.firmwareVersion";
-const char kBaseStateAnonymousAccessRole[] = "base.localAnonymousAccessMaxRole";
-const char kBaseStateDiscoveryEnabled[] = "base.localDiscoveryEnabled";
-const char kBaseStatePairingEnabled[] = "base.localPairingEnabled";
+const char kDeviceComponent[] = "device";
+const char kDeviceTrait[] = "device";
+const char kPrivetTrait[] = "privet";
 }  // namespace
 
 BaseApiHandler::BaseApiHandler(DeviceRegistrationInfo* device_info,
                                Device* device)
     : device_info_{device_info}, device_{device} {
   device_->AddTraitDefinitionsFromJson(R"({
-    "base": {
+    "device": {
       "commands": {
-        "updateBaseConfiguration": {
-          "minimalRole": "manager",
+        "setConfig": {
+          "minimalRole": "user",
           "parameters": {
-            "localAnonymousAccessMaxRole": {
-              "enum": [ "none", "viewer", "user" ],
+            "name": {
               "type": "string"
             },
-            "localDiscoveryEnabled": {
-              "type": "boolean"
-            },
-            "localPairingEnabled": {
-              "type": "boolean"
-            }
-          }
-        },
-        "updateDeviceInfo": {
-          "minimalRole": "manager",
-          "parameters": {
             "description": {
               "type": "string"
             },
             "location": {
               "type": "string"
-            },
-            "name": {
-              "type": "string"
             }
           }
-        },
-        "reboot": {
-          "minimalRole": "user",
-          "parameters": {},
-          "errors": ["notEnoughBattery"]
-        },
-        "identify": {
-          "minimalRole": "user",
-          "parameters": {}
         }
       },
       "state": {
-        "firmwareVersion": {
-          "type": "string",
-          "isRequired": true
-        },
-        "localDiscoveryEnabled": {
-          "type": "boolean",
-          "isRequired": true
-        },
-        "localAnonymousAccessMaxRole": {
-          "type": "string",
-          "enum": [ "none", "viewer", "user" ],
-        "isRequired": true
-        },
-        "localPairingEnabled": {
-          "type": "boolean",
-          "isRequired": true
-        },
-        "connectionStatus": {
+        "name": {
+          "isRequired": true,
           "type": "string"
         },
-        "network": {
-          "type": "object",
-          "additionalProperties": false,
-          "properties": {
-            "name": { "type": "string" }
+        "description": {
+          "isRequired": true,
+          "type": "string"
+        },
+        "location": {
+          "type": "string"
+        },
+        "hardwareId": {
+          "isRequired": true,
+          "type": "string"
+        },
+        "serialNumber": {
+          "isRequired": true,
+          "type": "string"
+        },
+        "firmwareVersion": {
+          "isRequired": true,
+          "type": "string"
+        }
+      }
+    },
+    "privet": {
+      "commands": {
+        "setConfig": {
+          "minimalRole": "manager",
+          "parameters": {
+            "isLocalAccessEnabled": {
+              "type": "boolean"
+            },
+            "maxRoleForAnonymousAccess": {
+              "type": "string",
+              "enum": [ "none", "viewer", "user", "manager" ]
+            }
           }
         }
+      },
+      "state": {
+        "apiVersion": {
+          "isRequired": true,
+          "type": "string"
+        },
+        "isLocalAccessEnabled": {
+          "isRequired": true,
+          "type": "boolean"
+        },
+        "maxRoleForAnonymousAccess": {
+          "isRequired": true,
+          "type": "string",
+          "enum": [ "none", "viewer", "user", "manager" ]
+        }
       }
     }
   })");
-  CHECK(device_->AddComponent(kBaseComponent, {kBaseTrait}, nullptr));
+  CHECK(device_->AddComponent(kDeviceComponent, {kDeviceTrait, kPrivetTrait},
+                              nullptr));
   OnConfigChanged(device_->GetSettings());
 
   const auto& settings = device_info_->GetSettings();
   base::DictionaryValue state;
-  state.SetString(kBaseStateFirmwareVersion, settings.firmware_version);
-  CHECK(device_->SetStateProperty(kBaseComponent, kBaseStateFirmwareVersion,
-                                  base::StringValue{settings.firmware_version},
-                                  nullptr));
+  state.SetString("device.firmwareVersion", settings.firmware_version);
+  state.SetString("device.hardwareId", settings.device_id);
+  state.SetString("device.serialNumber", settings.serial_number);
+  state.SetString("privet.apiVersion", "3");  // Presently Privet v3.
+  CHECK(device_->SetStateProperties(kDeviceComponent, state, nullptr));
 
   device_->AddCommandHandler(
-      kBaseComponent, "base.updateBaseConfiguration",
-      base::Bind(&BaseApiHandler::UpdateBaseConfiguration,
+      kDeviceComponent, "device.setConfig",
+      base::Bind(&BaseApiHandler::DeviceSetConfig,
                  weak_ptr_factory_.GetWeakPtr()));
 
-  device_->AddCommandHandler(kBaseComponent, "base.updateDeviceInfo",
-                             base::Bind(&BaseApiHandler::UpdateDeviceInfo,
+  device_->AddCommandHandler(kDeviceComponent, "privet.setConfig",
+                             base::Bind(&BaseApiHandler::PrivetSetConfig,
                                         weak_ptr_factory_.GetWeakPtr()));
 
   device_info_->GetMutableConfig()->AddOnChangedCallback(base::Bind(
       &BaseApiHandler::OnConfigChanged, weak_ptr_factory_.GetWeakPtr()));
 }
 
-void BaseApiHandler::UpdateBaseConfiguration(
-    const std::weak_ptr<Command>& cmd) {
+void BaseApiHandler::PrivetSetConfig(const std::weak_ptr<Command>& cmd) {
   auto command = cmd.lock();
   if (!command)
     return;
@@ -132,41 +133,40 @@
   const auto& settings = device_info_->GetSettings();
   std::string anonymous_access_role{
       EnumToString(settings.local_anonymous_access_role)};
-  bool discovery_enabled{settings.local_discovery_enabled};
-  bool pairing_enabled{settings.local_pairing_enabled};
+  bool local_access_enabled{settings.local_access_enabled};
 
   const auto& parameters = command->GetParameters();
-  parameters.GetString("localAnonymousAccessMaxRole", &anonymous_access_role);
-  parameters.GetBoolean("localDiscoveryEnabled", &discovery_enabled);
-  parameters.GetBoolean("localPairingEnabled", &pairing_enabled);
+  parameters.GetString("maxRoleForAnonymousAccess", &anonymous_access_role);
+  parameters.GetBoolean("isLocalAccessEnabled", &local_access_enabled);
 
   AuthScope auth_scope{AuthScope::kNone};
   if (!StringToEnum(anonymous_access_role, &auth_scope)) {
     ErrorPtr error;
     Error::AddToPrintf(&error, FROM_HERE, errors::commands::kInvalidPropValue,
-                       "Invalid localAnonymousAccessMaxRole value '%s'",
+                       "Invalid maxRoleForAnonymousAccess value '%s'",
                        anonymous_access_role.c_str());
     command->Abort(error.get(), nullptr);
     return;
   }
 
-  device_info_->UpdateBaseConfig(auth_scope, discovery_enabled,
-                                 pairing_enabled);
+  device_info_->UpdatePrivetConfig(auth_scope, local_access_enabled);
 
   command->Complete({}, nullptr);
 }
 
 void BaseApiHandler::OnConfigChanged(const Settings& settings) {
   base::DictionaryValue state;
-  state.SetString(kBaseStateAnonymousAccessRole,
+  state.SetString("privet.maxRoleForAnonymousAccess",
                   EnumToString(settings.local_anonymous_access_role));
-  state.SetBoolean(kBaseStateDiscoveryEnabled,
-                   settings.local_discovery_enabled);
-  state.SetBoolean(kBaseStatePairingEnabled, settings.local_pairing_enabled);
-  device_->SetStateProperties(kBaseComponent, state, nullptr);
+  state.SetBoolean("privet.isLocalAccessEnabled",
+                   settings.local_access_enabled);
+  state.SetString("device.name", settings.name);
+  state.SetString("device.location", settings.location);
+  state.SetString("device.description", settings.description);
+  device_->SetStateProperties(kDeviceComponent, state, nullptr);
 }
 
-void BaseApiHandler::UpdateDeviceInfo(const std::weak_ptr<Command>& cmd) {
+void BaseApiHandler::DeviceSetConfig(const std::weak_ptr<Command>& cmd) {
   auto command = cmd.lock();
   if (!command)
     return;
diff --git a/src/base_api_handler.h b/src/base_api_handler.h
index 6eebfca..b22d9ae 100644
--- a/src/base_api_handler.h
+++ b/src/base_api_handler.h
@@ -28,8 +28,8 @@
   BaseApiHandler(DeviceRegistrationInfo* device_info, Device* device);
 
  private:
-  void UpdateBaseConfiguration(const std::weak_ptr<Command>& command);
-  void UpdateDeviceInfo(const std::weak_ptr<Command>& command);
+  void DeviceSetConfig(const std::weak_ptr<Command>& command);
+  void PrivetSetConfig(const std::weak_ptr<Command>& command);
   void OnConfigChanged(const Settings& settings);
 
   DeviceRegistrationInfo* device_info_;
diff --git a/src/base_api_handler_unittest.cc b/src/base_api_handler_unittest.cc
index 12fb3b6..61262b3 100644
--- a/src/base_api_handler_unittest.cc
+++ b/src/base_api_handler_unittest.cc
@@ -49,8 +49,8 @@
         }));
 
     EXPECT_CALL(device_,
-                AddCommandHandler(_, AnyOf("base.updateBaseConfiguration",
-                                           "base.updateDeviceInfo"),
+                AddCommandHandler(_,
+                                  AnyOf("device.setConfig", "privet.setConfig"),
                                   _))
         .WillRepeatedly(
             Invoke(&component_manager_, &ComponentManager::AddCommandHandler));
@@ -76,14 +76,28 @@
               component_manager_.FindCommand(id)->GetState());
   }
 
-  std::unique_ptr<base::DictionaryValue> GetBaseState() {
+  std::unique_ptr<base::DictionaryValue> GetDeviceState() {
     std::unique_ptr<base::DictionaryValue> state;
-    std::string path = component_manager_.FindComponentWithTrait("base");
+    std::string path = component_manager_.FindComponentWithTrait("device");
     EXPECT_FALSE(path.empty());
     const auto* component = component_manager_.FindComponent(path, nullptr);
     CHECK(component);
     const base::DictionaryValue* base_state = nullptr;
-    if (component->GetDictionary("state.base", &base_state))
+    if (component->GetDictionary("state.device", &base_state))
+      state = base_state->CreateDeepCopy();
+    else
+      state.reset(new base::DictionaryValue);
+    return state;
+  }
+
+  std::unique_ptr<base::DictionaryValue> GetPrivetState() {
+    std::unique_ptr<base::DictionaryValue> state;
+    std::string path = component_manager_.FindComponentWithTrait("privet");
+    EXPECT_FALSE(path.empty());
+    const auto* component = component_manager_.FindComponent(path, nullptr);
+    CHECK(component);
+    const base::DictionaryValue* base_state = nullptr;
+    if (component->GetDictionary("state.privet", &base_state))
       state = base_state->CreateDeepCopy();
     else
       state.reset(new base::DictionaryValue);
@@ -102,143 +116,143 @@
 
 TEST_F(BaseApiHandlerTest, Initialization) {
   const base::DictionaryValue* trait = nullptr;
-  ASSERT_TRUE(component_manager_.GetTraits().GetDictionary("base", &trait));
+  ASSERT_TRUE(component_manager_.GetTraits().GetDictionary("device", &trait));
 
-  auto expected = R"({
+  auto expected1 = R"({
     "commands": {
-      "updateBaseConfiguration": {
-        "minimalRole": "manager",
+      "setConfig": {
+        "minimalRole": "user",
         "parameters": {
-          "localAnonymousAccessMaxRole": {
-            "enum": [ "none", "viewer", "user" ],
+          "name": {
             "type": "string"
           },
-          "localDiscoveryEnabled": {
-            "type": "boolean"
-          },
-          "localPairingEnabled": {
-            "type": "boolean"
-          }
-        }
-      },
-      "updateDeviceInfo": {
-        "minimalRole": "manager",
-        "parameters": {
           "description": {
             "type": "string"
           },
           "location": {
             "type": "string"
-          },
-          "name": {
-            "type": "string"
           }
         }
-      },
-      "reboot": {
-        "minimalRole": "user",
-        "parameters": {},
-        "errors": ["notEnoughBattery"]
-      },
-      "identify": {
-        "minimalRole": "user",
-        "parameters": {}
       }
     },
     "state": {
-      "firmwareVersion": {
-        "type": "string",
-        "isRequired": true
-      },
-      "localDiscoveryEnabled": {
-        "type": "boolean",
-        "isRequired": true
-      },
-      "localAnonymousAccessMaxRole": {
-        "type": "string",
-        "enum": [ "none", "viewer", "user" ],
-        "isRequired": true
-      },
-      "localPairingEnabled": {
-        "type": "boolean",
-        "isRequired": true
-      },
-      "connectionStatus": {
+      "name": {
+        "isRequired": true,
         "type": "string"
       },
-      "network": {
-        "type": "object",
-        "additionalProperties": false,
-        "properties": {
-          "name": { "type": "string" }
-        }
+      "description": {
+        "isRequired": true,
+        "type": "string"
+      },
+      "location": {
+        "type": "string"
+      },
+      "hardwareId": {
+        "isRequired": true,
+        "type": "string"
+      },
+      "serialNumber": {
+        "isRequired": true,
+        "type": "string"
+      },
+      "firmwareVersion": {
+        "isRequired": true,
+        "type": "string"
       }
     }
   })";
-  EXPECT_JSON_EQ(expected, *trait);
+  EXPECT_JSON_EQ(expected1, *trait);
+
+  ASSERT_TRUE(component_manager_.GetTraits().GetDictionary("privet", &trait));
+
+  auto expected2 = R"({
+    "commands": {
+      "setConfig": {
+        "minimalRole": "manager",
+        "parameters": {
+          "isLocalAccessEnabled": {
+            "type": "boolean"
+          },
+          "maxRoleForAnonymousAccess": {
+            "type": "string",
+            "enum": [ "none", "viewer", "user", "manager" ]
+          }
+        }
+      }
+    },
+    "state": {
+      "apiVersion": {
+        "isRequired": true,
+        "type": "string"
+      },
+      "isLocalAccessEnabled": {
+        "isRequired": true,
+        "type": "boolean"
+      },
+      "maxRoleForAnonymousAccess": {
+        "isRequired": true,
+        "type": "string",
+        "enum": [ "none", "viewer", "user", "manager" ]
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(expected2, *trait);
 }
 
-TEST_F(BaseApiHandlerTest, UpdateBaseConfiguration) {
+TEST_F(BaseApiHandlerTest, PrivetSetConfig) {
   const Settings& settings = dev_reg_->GetSettings();
 
   AddCommand(R"({
-    'name' : 'base.updateBaseConfiguration',
-    'component': 'base',
+    'name' : 'privet.setConfig',
+    'component': 'device',
     'parameters': {
-      'localDiscoveryEnabled': false,
-      'localAnonymousAccessMaxRole': 'none',
-      'localPairingEnabled': false
+      'isLocalAccessEnabled': false,
+      'maxRoleForAnonymousAccess': 'none'
     }
   })");
   EXPECT_EQ(AuthScope::kNone, settings.local_anonymous_access_role);
-  EXPECT_FALSE(settings.local_discovery_enabled);
-  EXPECT_FALSE(settings.local_pairing_enabled);
+  EXPECT_FALSE(settings.local_access_enabled);
 
   auto expected = R"({
-    'firmwareVersion': 'TEST_FIRMWARE',
-    'localAnonymousAccessMaxRole': 'none',
-    'localDiscoveryEnabled': false,
-    'localPairingEnabled': false
+    'apiVersion': '3',
+    'maxRoleForAnonymousAccess': 'none',
+    'isLocalAccessEnabled': false
   })";
-  EXPECT_JSON_EQ(expected, *GetBaseState());
+  EXPECT_JSON_EQ(expected, *GetPrivetState());
 
   AddCommand(R"({
-    'name' : 'base.updateBaseConfiguration',
-    'component': 'base',
+    'name' : 'privet.setConfig',
+    'component': 'device',
     'parameters': {
-      'localDiscoveryEnabled': true,
-      'localAnonymousAccessMaxRole': 'user',
-      'localPairingEnabled': true
+      'maxRoleForAnonymousAccess': 'user',
+      'isLocalAccessEnabled': true
     }
   })");
   EXPECT_EQ(AuthScope::kUser, settings.local_anonymous_access_role);
-  EXPECT_TRUE(settings.local_discovery_enabled);
-  EXPECT_TRUE(settings.local_pairing_enabled);
+  EXPECT_TRUE(settings.local_access_enabled);
   expected = R"({
-    'firmwareVersion': 'TEST_FIRMWARE',
-    'localAnonymousAccessMaxRole': 'user',
-    'localDiscoveryEnabled': true,
-    'localPairingEnabled': true
+    'apiVersion': '3',
+    'maxRoleForAnonymousAccess': 'user',
+    'isLocalAccessEnabled': true
   })";
-  EXPECT_JSON_EQ(expected, *GetBaseState());
+  EXPECT_JSON_EQ(expected, *GetPrivetState());
 
   {
     Config::Transaction change{dev_reg_->GetMutableConfig()};
     change.set_local_anonymous_access_role(AuthScope::kViewer);
   }
   expected = R"({
-    'firmwareVersion': 'TEST_FIRMWARE',
-    'localAnonymousAccessMaxRole': 'viewer',
-    'localDiscoveryEnabled': true,
-    'localPairingEnabled': true
+    'apiVersion': '3',
+    'maxRoleForAnonymousAccess': 'viewer',
+    'isLocalAccessEnabled': true
   })";
-  EXPECT_JSON_EQ(expected, *GetBaseState());
+  EXPECT_JSON_EQ(expected, *GetPrivetState());
 }
 
-TEST_F(BaseApiHandlerTest, UpdateDeviceInfo) {
+TEST_F(BaseApiHandlerTest, DeviceSetConfig) {
   AddCommand(R"({
-    'name' : 'base.updateDeviceInfo',
-    'component': 'base',
+    'name' : 'device.setConfig',
+    'component': 'device',
     'parameters': {
       'name': 'testName',
       'description': 'testDescription',
@@ -250,10 +264,19 @@
   EXPECT_EQ("testName", config.name);
   EXPECT_EQ("testDescription", config.description);
   EXPECT_EQ("testLocation", config.location);
+  auto expected = R"({
+    'name': 'testName',
+    'description': 'testDescription',
+    'location': 'testLocation',
+    'hardwareId': 'TEST_DEVICE_ID',
+    'serialNumber': 'TEST_SERIAL_NUMBER',
+    'firmwareVersion': 'TEST_FIRMWARE'
+  })";
+  EXPECT_JSON_EQ(expected, *GetDeviceState());
 
   AddCommand(R"({
-    'name' : 'base.updateDeviceInfo',
-    'component': 'base',
+    'name' : 'device.setConfig',
+    'component': 'device',
     'parameters': {
       'location': 'newLocation'
     }
@@ -262,6 +285,16 @@
   EXPECT_EQ("testName", config.name);
   EXPECT_EQ("testDescription", config.description);
   EXPECT_EQ("newLocation", config.location);
+
+  expected = R"({
+    'name': 'testName',
+    'description': 'testDescription',
+    'location': 'newLocation',
+    'hardwareId': 'TEST_DEVICE_ID',
+    'serialNumber': 'TEST_SERIAL_NUMBER',
+    'firmwareVersion': 'TEST_FIRMWARE'
+  })";
+  EXPECT_JSON_EQ(expected, *GetDeviceState());
 }
 
 }  // namespace weave
diff --git a/src/config.cc b/src/config.cc
index 108c6aa..fe5335f 100644
--- a/src/config.cc
+++ b/src/config.cc
@@ -40,8 +40,7 @@
 const char kDescription[] = "description";
 const char kLocation[] = "location";
 const char kLocalAnonymousAccessRole[] = "local_anonymous_access_role";
-const char kLocalDiscoveryEnabled[] = "local_discovery_enabled";
-const char kLocalPairingEnabled[] = "local_pairing_enabled";
+const char kLocalAccessEnabled[] = "local_access_enabled";
 const char kRefreshToken[] = "refresh_token";
 const char kCloudId[] = "cloud_id";
 const char kDeviceId[] = "device_id";
@@ -58,17 +57,28 @@
 
 namespace {
 
-const int kCurrentConfigVersion = 1;
+const int kCurrentConfigVersion = 2;
 
 void MigrateFromV0(base::DictionaryValue* dict) {
   std::string cloud_id;
   if (dict->GetString(config_keys::kCloudId, &cloud_id) && !cloud_id.empty())
     return;
-  scoped_ptr<base::Value> tmp;
+  std::unique_ptr<base::Value> tmp;
   if (dict->Remove(config_keys::kDeviceId, &tmp))
     dict->Set(config_keys::kCloudId, std::move(tmp));
 }
 
+void MigrateFromV1(base::DictionaryValue* dict) {
+  // "local_discovery_enabled" and "local_pairing_enabled" are merged into one
+  // setting: "local_access_enabled".
+  std::unique_ptr<base::Value> bool_val;
+  // Use the value of "local_discovery_enabled" for "local_access_enabled" and
+  // remove "local_pairing_enabled".
+  if (dict->Remove("local_discovery_enabled", &bool_val))
+    dict->Set(config_keys::kLocalAccessEnabled, std::move(bool_val));
+  dict->Remove("local_pairing_enabled", nullptr);
+}
+
 Config::Settings CreateDefaultSettings(provider::ConfigStore* config_store) {
   Config::Settings result;
   result.oauth_url = "https://accounts.google.com/o/oauth2/";
@@ -171,8 +181,13 @@
     save_ = true;
   }
 
-  if (loaded_version == 0) {
-    MigrateFromV0(dict);
+  switch (loaded_version) {
+    case 0:
+      MigrateFromV0(dict);
+      break;
+    case 1:
+      MigrateFromV1(dict);
+      break;
   }
 
   std::string tmp;
@@ -215,11 +230,8 @@
     set_local_anonymous_access_role(scope);
   }
 
-  if (dict->GetBoolean(config_keys::kLocalDiscoveryEnabled, &tmp_bool))
-    set_local_discovery_enabled(tmp_bool);
-
-  if (dict->GetBoolean(config_keys::kLocalPairingEnabled, &tmp_bool))
-    set_local_pairing_enabled(tmp_bool);
+  if (dict->GetBoolean(config_keys::kLocalAccessEnabled, &tmp_bool))
+    set_local_access_enabled(tmp_bool);
 
   if (dict->GetString(config_keys::kCloudId, &tmp))
     set_cloud_id(tmp);
@@ -274,10 +286,8 @@
   dict.SetString(config_keys::kLocation, settings_.location);
   dict.SetString(config_keys::kLocalAnonymousAccessRole,
                  EnumToString(settings_.local_anonymous_access_role));
-  dict.SetBoolean(config_keys::kLocalDiscoveryEnabled,
-                  settings_.local_discovery_enabled);
-  dict.SetBoolean(config_keys::kLocalPairingEnabled,
-                  settings_.local_pairing_enabled);
+  dict.SetBoolean(config_keys::kLocalAccessEnabled,
+                  settings_.local_access_enabled);
 
   std::string json_string;
   base::JSONWriter::WriteWithOptions(
diff --git a/src/config.h b/src/config.h
index 692d9c5..04ba78a 100644
--- a/src/config.h
+++ b/src/config.h
@@ -82,11 +82,8 @@
     void set_local_anonymous_access_role(AuthScope role) {
       settings_->local_anonymous_access_role = role;
     }
-    void set_local_discovery_enabled(bool enabled) {
-      settings_->local_discovery_enabled = enabled;
-    }
-    void set_local_pairing_enabled(bool enabled) {
-      settings_->local_pairing_enabled = enabled;
+    void set_local_access_enabled(bool enabled) {
+      settings_->local_access_enabled = enabled;
     }
     void set_cloud_id(const std::string& id) { settings_->cloud_id = id; }
     void set_refresh_token(const std::string& token) {
diff --git a/src/config_unittest.cc b/src/config_unittest.cc
index 9e8fc42..18d2aac 100644
--- a/src/config_unittest.cc
+++ b/src/config_unittest.cc
@@ -66,6 +66,7 @@
   EXPECT_EQ("", GetSettings().model_id);
   EXPECT_FALSE(GetSettings().device_id.empty());
   EXPECT_EQ("", GetSettings().firmware_version);
+  EXPECT_EQ("", GetSettings().serial_number);
   EXPECT_TRUE(GetSettings().wifi_auto_setup_enabled);
   EXPECT_EQ("", GetSettings().test_privet_ssid);
   EXPECT_EQ(std::set<PairingType>{PairingType::kPinCode},
@@ -75,8 +76,7 @@
   EXPECT_EQ("", GetSettings().description);
   EXPECT_EQ("", GetSettings().location);
   EXPECT_EQ(AuthScope::kViewer, GetSettings().local_anonymous_access_role);
-  EXPECT_TRUE(GetSettings().local_pairing_enabled);
-  EXPECT_TRUE(GetSettings().local_discovery_enabled);
+  EXPECT_TRUE(GetSettings().local_access_enabled);
   EXPECT_EQ("", GetSettings().cloud_id);
   EXPECT_EQ("", GetSettings().refresh_token);
   EXPECT_EQ("", GetSettings().robot_account);
@@ -127,7 +127,7 @@
 
 TEST_F(ConfigTest, LoadState) {
   auto state = R"({
-    "version": 1,
+    "version": 2,
     "api_key": "state_api_key",
     "client_id": "state_client_id",
     "client_secret": "state_client_secret",
@@ -137,8 +137,7 @@
     "last_configured_ssid": "state_last_configured_ssid",
     "local_anonymous_access_role": "user",
     "root_client_token_owner": "client",
-    "local_discovery_enabled": false,
-    "local_pairing_enabled": false,
+    "local_access_enabled": false,
     "location": "state_location",
     "name": "state_name",
     "oauth_url": "state_oauth_url",
@@ -172,8 +171,7 @@
   EXPECT_EQ("state_description", GetSettings().description);
   EXPECT_EQ("state_location", GetSettings().location);
   EXPECT_EQ(AuthScope::kUser, GetSettings().local_anonymous_access_role);
-  EXPECT_FALSE(GetSettings().local_pairing_enabled);
-  EXPECT_FALSE(GetSettings().local_discovery_enabled);
+  EXPECT_FALSE(GetSettings().local_access_enabled);
   EXPECT_EQ("state_cloud_id", GetSettings().cloud_id);
   EXPECT_EQ("state_refresh_token", GetSettings().refresh_token);
   EXPECT_EQ("state_robot_account", GetSettings().robot_account);
@@ -183,6 +181,44 @@
             GetSettings().root_client_token_owner);
 }
 
+TEST_F(ConfigTest, LoadStateV1) {
+  auto state = R"({
+    "version": 1,
+    "local_discovery_enabled": false,
+    "local_pairing_enabled": false
+  })";
+  EXPECT_CALL(config_store_, LoadSettings(kConfigName)).WillOnce(Return(state));
+  Reload();
+  EXPECT_FALSE(GetSettings().local_access_enabled);
+
+  state = R"({
+    "version": 1,
+    "local_discovery_enabled": true,
+    "local_pairing_enabled": false
+  })";
+  EXPECT_CALL(config_store_, LoadSettings(kConfigName)).WillOnce(Return(state));
+  Reload();
+  EXPECT_TRUE(GetSettings().local_access_enabled);
+
+  state = R"({
+    "version": 1,
+    "local_discovery_enabled": false,
+    "local_pairing_enabled": true
+  })";
+  EXPECT_CALL(config_store_, LoadSettings(kConfigName)).WillOnce(Return(state));
+  Reload();
+  EXPECT_FALSE(GetSettings().local_access_enabled);
+
+  state = R"({
+    "version": 1,
+    "local_discovery_enabled": true,
+    "local_pairing_enabled": true
+  })";
+  EXPECT_CALL(config_store_, LoadSettings(kConfigName)).WillOnce(Return(state));
+  Reload();
+  EXPECT_TRUE(GetSettings().local_access_enabled);
+}
+
 TEST_F(ConfigTest, Setters) {
   Config::Transaction change{config_.get()};
 
@@ -222,17 +258,11 @@
   change.set_local_anonymous_access_role(AuthScope::kUser);
   EXPECT_EQ(AuthScope::kUser, GetSettings().local_anonymous_access_role);
 
-  change.set_local_discovery_enabled(false);
-  EXPECT_FALSE(GetSettings().local_discovery_enabled);
+  change.set_local_access_enabled(false);
+  EXPECT_FALSE(GetSettings().local_access_enabled);
 
-  change.set_local_pairing_enabled(false);
-  EXPECT_FALSE(GetSettings().local_pairing_enabled);
-
-  change.set_local_discovery_enabled(true);
-  EXPECT_TRUE(GetSettings().local_discovery_enabled);
-
-  change.set_local_pairing_enabled(true);
-  EXPECT_TRUE(GetSettings().local_pairing_enabled);
+  change.set_local_access_enabled(true);
+  EXPECT_TRUE(GetSettings().local_access_enabled);
 
   change.set_cloud_id("set_cloud_id");
   EXPECT_EQ("set_cloud_id", GetSettings().cloud_id);
@@ -263,7 +293,7 @@
       .WillOnce(WithArgs<1, 2>(
           Invoke([](const std::string& json, const DoneCallback& callback) {
             auto expected = R"({
-              'version': 1,
+              'version': 2,
               'api_key': 'set_api_key',
               'client_id': 'set_client_id',
               'client_secret': 'set_client_secret',
@@ -273,8 +303,7 @@
               'last_configured_ssid': 'set_last_configured_ssid',
               'local_anonymous_access_role': 'user',
               'root_client_token_owner': 'cloud',
-              'local_discovery_enabled': true,
-              'local_pairing_enabled': true,
+              'local_access_enabled': true,
               'location': 'set_location',
               'name': 'set_name',
               'oauth_url': 'set_oauth_url',
diff --git a/src/device_manager.cc b/src/device_manager.cc
index 3f8e4d7..53a8d18 100644
--- a/src/device_manager.cc
+++ b/src/device_manager.cc
@@ -197,7 +197,7 @@
 }
 
 void DeviceManager::OnSettingsChanged(const Settings& settings) {
-  if (settings.local_discovery_enabled && http_server_) {
+  if (settings.local_access_enabled && http_server_) {
     StartPrivet();
   } else {
     StopPrivet();
diff --git a/src/device_registration_info.cc b/src/device_registration_info.cc
index 6353e12..20fd9c0 100644
--- a/src/device_registration_info.cc
+++ b/src/device_registration_info.cc
@@ -489,11 +489,6 @@
   std::unique_ptr<base::DictionaryValue> resource{new base::DictionaryValue};
   if (!GetSettings().cloud_id.empty())
     resource->SetString("id", GetSettings().cloud_id);
-  resource->SetString("name", GetSettings().name);
-  if (!GetSettings().description.empty())
-    resource->SetString("description", GetSettings().description);
-  if (!GetSettings().location.empty())
-    resource->SetString("location", GetSettings().location);
   resource->SetString("modelManifestId", GetSettings().model_id);
   std::unique_ptr<base::DictionaryValue> channel{new base::DictionaryValue};
   if (current_notification_channel_) {
@@ -877,19 +872,14 @@
   change.set_description(description);
   change.set_location(location);
   change.Commit();
-
-  if (HaveRegistrationCredentials()) {
-    UpdateDeviceResource(base::Bind(&IgnoreCloudError));
-  }
 }
 
-void DeviceRegistrationInfo::UpdateBaseConfig(AuthScope anonymous_access_role,
-                                              bool local_discovery_enabled,
-                                              bool local_pairing_enabled) {
+void DeviceRegistrationInfo::UpdatePrivetConfig(AuthScope anonymous_access_role,
+                                              bool local_access_enabled) {
   Config::Transaction change(config_);
   change.set_local_anonymous_access_role(anonymous_access_role);
-  change.set_local_discovery_enabled(local_discovery_enabled);
-  change.set_local_pairing_enabled(local_pairing_enabled);
+  change.set_local_access_enabled(local_access_enabled);
+  change.Commit();
 }
 
 void DeviceRegistrationInfo::UpdateCommand(
diff --git a/src/device_registration_info.h b/src/device_registration_info.h
index 5ecdcac..c7a26ee 100644
--- a/src/device_registration_info.h
+++ b/src/device_registration_info.h
@@ -71,9 +71,8 @@
   void UpdateDeviceInfo(const std::string& name,
                         const std::string& description,
                         const std::string& location);
-  void UpdateBaseConfig(AuthScope anonymous_access_role,
-                        bool local_discovery_enabled,
-                        bool local_pairing_enabled);
+  void UpdatePrivetConfig(AuthScope anonymous_access_role,
+                          bool local_access_enabled);
 
   void GetDeviceInfo(const CloudRequestDoneCallback& callback);
 
diff --git a/src/device_registration_info_unittest.cc b/src/device_registration_info_unittest.cc
index 757b1c9..ff32c3d 100644
--- a/src/device_registration_info_unittest.cc
+++ b/src/device_registration_info_unittest.cc
@@ -408,7 +408,7 @@
     const RegistrationData registration_data,
     const RegistrationData& expected_data) {
   auto json_traits = CreateDictionaryValue(R"({
-    'base': {
+    '_foo': {
       'commands': {
         'reboot': {
           'parameters': {'delay': {'minimum': 10, 'type': 'integer'}},
@@ -419,9 +419,9 @@
         'firmwareVersion': {'type': 'string'}
       }
     },
-    'robot': {
+    '_robot': {
       'commands': {
-        '_jump': {
+        'jump': {
           'parameters': {'_height': {'type': 'integer'}},
           'minimalRole': 'user'
         }
@@ -430,10 +430,10 @@
   })");
   EXPECT_TRUE(component_manager_.LoadTraits(*json_traits, nullptr));
   EXPECT_TRUE(
-      component_manager_.AddComponent("", "comp", {"base", "robot"}, nullptr));
+      component_manager_.AddComponent("", "comp", {"_foo", "_robot"}, nullptr));
   base::StringValue ver{"1.0"};
   EXPECT_TRUE(component_manager_.SetStateProperty(
-      "comp", "base.firmwareVersion", ver, nullptr));
+      "comp", "_foo.firmwareVersion", ver, nullptr));
 
   std::string ticket_url = expected_data.service_url + "registrationTickets/" +
                            expected_data.ticket_id;
@@ -454,20 +454,17 @@
             EXPECT_EQ("pull", value);
             EXPECT_TRUE(json->GetString("oauthClientId", &value));
             EXPECT_EQ(expected_data.client_id, value);
-            EXPECT_TRUE(json->GetString("deviceDraft.description", &value));
-            EXPECT_EQ("Easy to clean", value);
-            EXPECT_TRUE(json->GetString("deviceDraft.location", &value));
-            EXPECT_EQ("Kitchen", value);
+            EXPECT_FALSE(json->GetString("deviceDraft.description", &value));
+            EXPECT_FALSE(json->GetString("deviceDraft.location", &value));
             EXPECT_TRUE(json->GetString("deviceDraft.modelManifestId", &value));
             EXPECT_EQ("AAAAA", value);
-            EXPECT_TRUE(json->GetString("deviceDraft.name", &value));
-            EXPECT_EQ("Coffee Pot", value);
+            EXPECT_FALSE(json->GetString("deviceDraft.name", &value));
             base::DictionaryValue* dict = nullptr;
             EXPECT_FALSE(json->GetDictionary("deviceDraft.commandDefs", &dict));
             EXPECT_FALSE(json->GetDictionary("deviceDraft.state", &dict));
             EXPECT_TRUE(json->GetDictionary("deviceDraft.traits", &dict));
             auto expectedTraits = R"({
-              'base': {
+              '_foo': {
                 'commands': {
                   'reboot': {
                     'parameters': {'delay': {'minimum': 10, 'type': 'integer'}},
@@ -478,9 +475,9 @@
                   'firmwareVersion': {'type': 'string'}
                 }
               },
-              'robot': {
+              '_robot': {
                 'commands': {
-                  '_jump': {
+                  'jump': {
                     'parameters': {'_height': {'type': 'integer'}},
                     'minimalRole': 'user'
                   }
@@ -492,9 +489,9 @@
             EXPECT_TRUE(json->GetDictionary("deviceDraft.components", &dict));
             auto expectedComponents = R"({
               'comp': {
-                'traits': ['base', 'robot'],
+                'traits': ['_foo', '_robot'],
                 'state': {
-                  'base': { 'firmwareVersion': '1.0' }
+                  '_foo': { 'firmwareVersion': '1.0' }
                 }
               }
             })";
diff --git a/src/privet/privet_handler_unittest.cc b/src/privet/privet_handler_unittest.cc
index 0c9f158..f0846f9 100644
--- a/src/privet/privet_handler_unittest.cc
+++ b/src/privet/privet_handler_unittest.cc
@@ -77,9 +77,8 @@
   auto result = value.CreateDeepCopy();
   base::DictionaryValue* error_dict = nullptr;
   EXPECT_TRUE(result->GetDictionary(path_to_error_object, &error_dict));
-  scoped_ptr<base::Value> dummy;
-  error_dict->RemovePath("error.debugInfo", &dummy);
-  error_dict->RemovePath("error.message", &dummy);
+  error_dict->RemovePath("error.debugInfo", nullptr);
+  error_dict->RemovePath("error.message", nullptr);
   return result;
 }
 
diff --git a/src/privet/security_manager.cc b/src/privet/security_manager.cc
index 157c8d6..fe2bc4b 100644
--- a/src/privet/security_manager.cc
+++ b/src/privet/security_manager.cc
@@ -438,7 +438,7 @@
 }
 
 bool SecurityManager::IsPairingAuthSupported() const {
-  return GetSettings().local_pairing_enabled;
+  return GetSettings().local_access_enabled;
 }
 
 bool SecurityManager::IsLocalAuthSupported() const {