diff --git a/src/lib/cloud-manager-hoc.jsx b/src/lib/cloud-manager-hoc.jsx
index 129e7416c66eb0797d4e2212374cb0d2c1c690cd..6ca6b7500fc22cb4e27b8ee8c741d81b80e35f13 100644
--- a/src/lib/cloud-manager-hoc.jsx
+++ b/src/lib/cloud-manager-hoc.jsx
@@ -21,48 +21,48 @@ const cloudManagerHOC = function (WrappedComponent) {
             this.cloudProvider = null;
         }
         componentDidMount () {
-            if (this.canUseCloud(this.props)) {
+            if (this.shouldConnect(this.props)) {
                 this.connectToCloud();
             }
         }
         componentDidUpdate (prevProps) {
-            // TODO add disconnection logic when loading a new project
+            // TODO need to add cloud provider disconnection logic and cloud data clearing logic
+            // when loading a new project e.g. via file upload
             // (and eventually move it out of the vm.clear function)
-            // e.g. was previously showingWithId but no longer, but we need
-            // to check that we don't disconnect when saving a project to the
-            // server.
 
-            // If we couldn't use cloud data before, but now we can, try opening a cloud connection
-            if (this.canUseCloud(this.props) && !this.canUseCloud(prevProps)) {
+            if (this.shouldConnect(this.props) && !this.shouldConnect(prevProps)) {
                 this.connectToCloud();
-                return;
             }
 
-            // TODO add scratcher check
-
-            if ((this.props.username !== prevProps.username) ||
-                (this.props.projectId !== prevProps.projectId)) {
-                this.connectToCloud();
+            if (this.shouldDisconnect(this.props, prevProps)) {
+                this.disconnectFromCloud();
             }
         }
         componentWillUnmount () {
-            if (this.cloudProvider) {
-                this.cloudProvider.requestCloseConnection();
-                this.cloudProvider = null;
-            }
+            this.disconnectFromCloud();
         }
         canUseCloud (props) {
-            return props.cloudHost && props.username && props.isShowingWithId &&
-                props.vm && props.username && props.projectId;
+            // TODO add a canUseCloud to pass down to this HOC (e.g. from www) and also check that here.
+            // This should cover info about the website specifically, like scrather status
+            return !!(props.cloudHost && props.username && props.vm && props.projectId);
         }
-        connectToCloud () {
-            if (this.cloudProvider && this.cloudProvider.connection) {
-                // Already connected
-                return;
-            }
-
+        shouldConnect (props) {
+            return !this.isConnected() && this.canUseCloud(props) && props.isShowingWithId;
+        }
+        shouldDisconnect (props, prevProps) {
+            return this.isConnected() &&
+                ( // Can no longer use cloud or cloud provider info is now stale
+                    !this.canUseCloud(this.props) ||
+                    (props.projectId !== prevProps.projectId) ||
+                    (props.username !== prevProps.username)
+                );
             // TODO need to add provisions for viewing someone
             // else's project in editor mode
+        }
+        isConnected () {
+            return this.cloudProvider && !!this.cloudProvider.connection;
+        }
+        connectToCloud () {
             this.cloudProvider = new CloudProvider(
                 this.props.cloudHost,
                 this.props.vm,
@@ -70,6 +70,13 @@ const cloudManagerHOC = function (WrappedComponent) {
                 this.props.projectId);
             this.props.vm.setCloudProvider(this.cloudProvider);
         }
+        disconnectFromCloud () {
+            if (this.cloudProvider) {
+                this.cloudProvider.requestCloseConnection();
+                this.cloudProvider = null;
+                this.props.vm.setCloudProvider(null);
+            }
+        }
         render () {
             const {
                 /* eslint-disable no-unused-vars */
diff --git a/src/lib/cloud-provider.js b/src/lib/cloud-provider.js
index 2e159054c2d01d5d04600db3d3daebba18b07619..f268ae473f7bde4425b2678530a6e07ebf317dd2 100644
--- a/src/lib/cloud-provider.js
+++ b/src/lib/cloud-provider.js
@@ -54,8 +54,10 @@ class CloudProvider {
         log.info(`Received websocket message: ${messageString}`);
         // Multiple commands can be received, newline separated
         messageString.split('\n').forEach(message => {
-            const parsedData = this.parseMessage(JSON.parse(message));
-            this.vm.postIOData('cloud', parsedData);
+            if (message) { // .split can also contain '' in the array it returns
+                const parsedData = this.parseMessage(JSON.parse(message));
+                this.vm.postIOData('cloud', parsedData);
+            }
         });
     }
 
diff --git a/test/unit/util/cloud-manager-hoc.test.jsx b/test/unit/util/cloud-manager-hoc.test.jsx
index 854f9b596b1d73d0e790f98f54132502b4fcad5b..7414fc7cb6dd3de0420cab86c7660b421448f4fe 100644
--- a/test/unit/util/cloud-manager-hoc.test.jsx
+++ b/test/unit/util/cloud-manager-hoc.test.jsx
@@ -6,7 +6,13 @@ import {mount} from 'enzyme';
 import VM from 'scratch-vm';
 import {LoadingState} from '../../../src/reducers/project-state';
 import CloudProvider from '../../../src/lib/cloud-provider';
-jest.mock('../../../src/lib/cloud-provider');
+const mockCloudProviderInstance = {
+    connection: true,
+    requestCloseConnection: jest.fn()
+};
+jest.mock('../../../src/lib/cloud-provider', () =>
+    jest.fn().mockImplementation(() => mockCloudProviderInstance)
+);
 
 import cloudManagerHOC from '../../../src/lib/cloud-manager-hoc.jsx';
 
@@ -36,6 +42,7 @@ describe('CloudManagerHOC', () => {
         vm = new VM();
         vm.setCloudProvider = jest.fn();
         CloudProvider.mockClear();
+        mockCloudProviderInstance.requestCloseConnection.mockClear();
     });
     test('when it mounts, the cloud provider is set on the vm', () => {
         const Component = () => (<div />);
@@ -50,6 +57,7 @@ describe('CloudManagerHOC', () => {
         );
         expect(vm.setCloudProvider.mock.calls.length).toBe(1);
         expect(CloudProvider).toHaveBeenCalledTimes(1);
+        expect(vm.setCloudProvider).toHaveBeenCalledWith(mockCloudProviderInstance);
     });
 
     test('when cloudHost is missing, the cloud provider is not set on the vm', () => {
@@ -115,5 +123,105 @@ describe('CloudManagerHOC', () => {
         });
         expect(vm.setCloudProvider.mock.calls.length).toBe(1);
         expect(CloudProvider).toHaveBeenCalledTimes(1);
+        expect(vm.setCloudProvider).toHaveBeenCalledWith(mockCloudProviderInstance);
+    });
+
+    test('projectId change should not trigger cloudProvider connection unless isShowingWithId becomes true', () => {
+        const Component = () => <div />;
+        const WrappedComponent = cloudManagerHOC(Component);
+        const mounted = mount(
+            <WrappedComponent
+                cloudHost="nonEmpty"
+                store={stillLoadingStore}
+                username="user"
+                vm={vm}
+            />
+        );
+        mounted.setProps({
+            projectId: 'a different id'
+        });
+        expect(vm.setCloudProvider.mock.calls.length).toBe(0);
+        expect(CloudProvider).not.toHaveBeenCalled();
+        mounted.setProps({
+            isShowingWithId: true,
+            loadingState: LoadingState.SHOWING_WITH_ID
+        });
+        expect(vm.setCloudProvider.mock.calls.length).toBe(1);
+        expect(CloudProvider).toHaveBeenCalledTimes(1);
+        expect(vm.setCloudProvider).toHaveBeenCalledWith(mockCloudProviderInstance);
+    });
+
+    test('when it unmounts, the cloud provider is set on the vm', () => {
+        const Component = () => (<div />);
+        const WrappedComponent = cloudManagerHOC(Component);
+        const mounted = mount(
+            <WrappedComponent
+                cloudHost="nonEmpty"
+                store={store}
+                username="user"
+                vm={vm}
+            />
+        );
+
+        expect(CloudProvider).toHaveBeenCalled();
+        const requestCloseConnection = mockCloudProviderInstance.requestCloseConnection;
+
+        mounted.unmount();
+
+        // vm.setCloudProvider is called twice,
+        // once during mount and once during unmount
+        expect(vm.setCloudProvider.mock.calls.length).toBe(2);
+        expect(vm.setCloudProvider).toHaveBeenCalledWith(null);
+        expect(requestCloseConnection).toHaveBeenCalledTimes(1);
+    });
+
+    test('projectId changing should trigger cloudProvider disconnection', () => {
+        const Component = () => <div />;
+        const WrappedComponent = cloudManagerHOC(Component);
+        const mounted = mount(
+            <WrappedComponent
+                cloudHost="nonEmpty"
+                store={store}
+                username="user"
+                vm={vm}
+            />
+        );
+
+        expect(CloudProvider).toHaveBeenCalled();
+        const requestCloseConnection = mockCloudProviderInstance.requestCloseConnection;
+
+        mounted.setProps({
+            projectId: 'a different id'
+        });
+
+        expect(vm.setCloudProvider.mock.calls.length).toBe(2);
+        expect(vm.setCloudProvider).toHaveBeenCalledWith(null);
+        expect(requestCloseConnection).toHaveBeenCalledTimes(1);
+
+    });
+
+    test('username changing should trigger cloudProvider disconnection', () => {
+        const Component = () => <div />;
+        const WrappedComponent = cloudManagerHOC(Component);
+        const mounted = mount(
+            <WrappedComponent
+                cloudHost="nonEmpty"
+                store={store}
+                username="user"
+                vm={vm}
+            />
+        );
+
+        expect(CloudProvider).toHaveBeenCalled();
+        const requestCloseConnection = mockCloudProviderInstance.requestCloseConnection;
+
+        mounted.setProps({
+            username: 'a different user'
+        });
+
+        expect(vm.setCloudProvider.mock.calls.length).toBe(2);
+        expect(vm.setCloudProvider).toHaveBeenCalledWith(null);
+        expect(requestCloseConnection).toHaveBeenCalledTimes(1);
+
     });
 });
diff --git a/test/unit/util/cloud-provider.test.js b/test/unit/util/cloud-provider.test.js
index ee720044d9ce6c398a6081aa1f5de02aea534d63..3510339c01eddeb20f3cb04246ace23fd75c688f 100644
--- a/test/unit/util/cloud-provider.test.js
+++ b/test/unit/util/cloud-provider.test.js
@@ -88,6 +88,16 @@ describe('CloudProvider', () => {
         expect(vmIOData[0].varUpdate.value).toEqual('value');
     });
 
+    test('onMessage with newline at the end', () => {
+        const msg1 = JSON.stringify({
+            method: 'set',
+            name: 'name1',
+            value: 'value'
+        });
+        cloudProvider.onMessage({data: `${msg1}\n`});
+        expect(vmIOData[0].varUpdate.name).toEqual('name1');
+    });
+
     test('onMessage with multiple commands', () => {
         const msg1 = JSON.stringify({
             method: 'set',