     # Copy configs
     [current, _] = repos
     copy_configs(current)
-    {:ok, %{repos: repos, needs_hard_sync: needs_hard_sync}}
-  end
-
-  defp copy_configs(repo) do
-    case repo.one(Device) do
-      nil -> :ok
-      %{timezone: tz} ->
-        ConfigStorage.update_config_value(:string, "settings", "timezone", tz)
-        :ok
-    end
-  end
-
-  def terminate(_,_) do
-    BotState.set_sync_status(:sync_error)
+    {:ok, %{repos: repos, needs_hard_sync: needs_hard_sync, timer: start_timer()}}
+  end
+
+  def terminate(reason,_) do
+    if reason not in [:normal, :shutdown] do
+      BotState.set_sync_status(:sync_error)
+    end
+  end
+
+  def handle_call(:force_hard_sync, _, state) do
+    maybe_cancel_timer(state.timer)
+    BotState.set_sync_status(:sync_now)
+    {:reply, :ok, %{state | timer: nil, needs_hard_sync: true}}
   end
 
   def handle_call(:current_repo, _, %{repos: [repo_a, _]} = state) do

  end

  @doc false
  def start_link(repos) do
    GenServer.start_link(__MODULE__, repos, [name: __MODULE__])
  end

  def init([repo_a, repo_b]) do
    # Delete any old sync cmds.
    ConfigStorage.delete_all SyncCmd

    needs_hard_sync = if ConfigStorage.get_config_value(:bool, "settings", "first_sync") || auto_sync?() do
      do_sync_all_resources(repo_a)
      do_sync_all_resources(repo_b)
      ConfigStorage.update_config_value(:bool, "settings", "first_sync", false)
      BotState.set_sync_status(:synced)
      false

        # if there is an existing record, copy the ecto  meta from the old
        # record. This allows `insert_or_update` to work properly.
        existing ->
          mod.changeset(existing, not_struct)
          |> repo.update!
      end
    else
