diff --git a/platypush/backend/http/app/routes/plugins/camera/pi.py b/platypush/backend/http/app/routes/plugins/camera/pi.py
index f440c69f7..aff9405e6 100644
--- a/platypush/backend/http/app/routes/plugins/camera/pi.py
+++ b/platypush/backend/http/app/routes/plugins/camera/pi.py
@@ -2,7 +2,6 @@ import os
 import tempfile
 
 from flask import Response, Blueprint, send_from_directory
-from typing import Optional
 
 from platypush.backend.http.app import template_folder
 from platypush.backend.http.app.utils import authenticate, send_request
@@ -18,7 +17,6 @@ __routes__ = [
 
 
 def video_feed():
-    camera: Optional[CameraPiPlugin] = None
     camera_conf = Config.get('camera.pi') or {}
     camera = CameraPiPlugin(**camera_conf)
 
diff --git a/platypush/backend/http/static/css/source/webpanel/plugins/camera.android.ipcam b/platypush/backend/http/static/css/source/webpanel/plugins/camera.android.ipcam
new file mode 120000
index 000000000..5f839334f
--- /dev/null
+++ b/platypush/backend/http/static/css/source/webpanel/plugins/camera.android.ipcam
@@ -0,0 +1 @@
+camera
\ No newline at end of file
diff --git a/platypush/backend/http/static/js/plugins/camera.android.ipcam/index.js b/platypush/backend/http/static/js/plugins/camera.android.ipcam/index.js
new file mode 100644
index 000000000..7b0f57d90
--- /dev/null
+++ b/platypush/backend/http/static/js/plugins/camera.android.ipcam/index.js
@@ -0,0 +1,108 @@
+Vue.component('camera-android-ipcam', {
+    template: '#tmpl-camera-android-ipcam',
+    props: ['config'],
+
+    data: function() {
+        return {
+            bus: new Vue({}),
+            loading: false,
+            streaming: false,
+            capturing: false,
+            recording: false,
+            cameras: {},
+            selectedCamera: undefined,
+        };
+    },
+
+    computed: {
+        hasMultipleCameras: function () {
+            return Object.keys(this.cameras).length > 1;
+        },
+    },
+
+    methods: {
+        startStreaming: function() {
+            if (this.streaming)
+                return;
+
+            const cam = this.cameras[this.selectedCamera];
+            this.streaming = true;
+            this.capturing = false;
+            this.$refs.frame.setAttribute('src', cam.stream_url);
+        },
+
+        stopStreaming: function() {
+            if (!this.streaming)
+                return;
+
+            this.streaming = false;
+            this.capturing = false;
+            this.$refs.frame.removeAttribute('src');
+        },
+
+        capture: function() {
+            if (this.capturing)
+                return;
+
+            const cam = this.cameras[this.selectedCamera];
+            this.streaming = false;
+            this.capturing = true;
+            this.$refs.frame.setAttribute('src', cam.image_url + '?t=' + (new Date()).getTime());
+        },
+
+        onFrameLoaded: function(event) {
+            if (this.capturing)
+                this.capturing = false;
+        },
+
+        onCameraSelected: function(event) {
+            this.selectedCamera = event.target.value;
+        },
+
+        flipCamera: async function() {
+            const cam = this.cameras[this.selectedCamera];
+            this.loading = true;
+
+            try {
+                const value = !cam.ffc;
+                await request('camera.android.ipcam.set_front_facing_camera', {
+                    activate: value, camera: cam.name
+                });
+
+                this.cameras[this.selectedCamera].ffc = value;
+            } finally {
+                this.loading = false;
+            }
+        },
+
+        updateCameraStatus: async function() {
+            this.loading = true;
+
+            try {
+                const cameras = await request('camera.android.ipcam.status');
+                this.cameras = cameras.reduce((cameras, cam) => {
+                    for (const attr of ['stream_url', 'image_url', 'audio_url']) {
+                        if (cam[attr].startsWith('https://')) {
+                            cam[attr] = cam[attr].replace('https://', 'http://');
+                        }
+                    }
+
+                    cameras[cam.name] = cam;
+                    return cameras;
+                }, {});
+
+                if (cameras.length)
+                    this.selectedCamera = cameras[0].name;
+
+            } finally {
+                this.loading = false;
+            }
+        },
+    },
+
+    mounted: function() {
+        this.$refs.frame.addEventListener('load', this.onFrameLoaded);
+        this.updateCameraStatus();
+    },
+});
+
diff --git a/platypush/backend/http/templates/nav.html b/platypush/backend/http/templates/nav.html
index ef6a7a292..390474233 100644
--- a/platypush/backend/http/templates/nav.html
+++ b/platypush/backend/http/templates/nav.html
@@ -1,6 +1,7 @@
 {%
     with pluginIcons = {
         'camera': 'fas fa-camera',
+        'camera.android.ipcam': 'fab fa-android',
         'camera.pi': 'fab fa-raspberry-pi',
         'camera.ir.mlx90640': 'fas fa-sun',
         'execute': 'fas fa-play',
diff --git a/platypush/backend/http/templates/plugins/camera.android.ipcam/index.html b/platypush/backend/http/templates/plugins/camera.android.ipcam/index.html
new file mode 100644
index 000000000..c9109fe89
--- /dev/null
+++ b/platypush/backend/http/templates/plugins/camera.android.ipcam/index.html
@@ -0,0 +1,53 @@
+<script type="text/x-template" id="tmpl-camera-android-ipcam">
+    <div class="camera">
+        <div class="camera-selector">
+            <select ref="cameraSelector" @change="onCameraSelected" v-if="hasMultipleCameras">
+                <option v-for="(cam, name) in cameras"
+                        :key="name" :value="name">
+                    {% raw %}{{ name }}{% endraw %}
+                </option>
+            </select>
+        </div>
+
+        <div class="camera-container">
+            <div class="no-frame" v-if="!streaming && !capturing">The camera is not active</div>
+            <img class="frame" ref="frame">
+        </div>
+
+        <div class="controls">
+            <button type="button" @click="startStreaming" :disabled="capturing || loading" v-if="!streaming">
+                <i class="fa fa-play"></i>&nbsp; Start streaming video
+            </button>
+
+            <button type="button" @click="stopStreaming" :disabled="capturing || loading" v-else>
+                <i class="fa fa-stop"></i>&nbsp; Stop streaming video
+            </button>
+
+            <button type="button" @click="capture" :disabled="streaming || capturing || loading">
+                <i class="fas fa-camera"></i>&nbsp; Take snapshot
+            </button>
+        </div>
+
+        <div class="controls">
+            <button type="button" @click="recording = true" v-if="!recording" :disabled="loading">
+                <i class="fa fa-play"></i>&nbsp; Start streaming audio
+            </button>
+
+            <button type="button" @click="recording = false" v-else :disabled="loading">
+                <i class="fa fa-stop"></i>&nbsp; Stop streaming audio
+            </button>
+
+            <button type="button" @click="flipCamera" :disabled="loading">
+                <i class="fas fa-retweet"></i> Flip camera
+            </button>
+        </div>
+
+        <div class="sound-container">
+            <audio autoplay preload="none" ref="player" v-if="recording">
+                <source :src="cameras[selectedCamera].audio_url" type="audio/x-wav;codec=pcm">
+                Your browser does not support audio elements
+            </audio>
+        </div>
+    </div>
+</script>
+
diff --git a/platypush/message/response/camera/android.py b/platypush/message/response/camera/android.py
index 1d0481340..ed18f0c82 100644
--- a/platypush/message/response/camera/android.py
+++ b/platypush/message/response/camera/android.py
@@ -10,6 +10,9 @@ class AndroidCameraStatusResponse(CameraResponse):
         ..code-block:: json
 
         {
+            "stream_url": "https://192.168.1.30:8080/video",
+            "image_url": "https://192.168.1.30:8080/photo.jpg",
+            "audio_url": "https://192.168.1.30:8080/audio.wav",
             "orientation": "landscape",
             "idle": "off",
             "audio_only": "off",
@@ -71,6 +74,10 @@ class AndroidCameraStatusResponse(CameraResponse):
     }
 
     def __init__(self, *args,
+                 name: str = None,
+                 stream_url: str = None,
+                 image_url: str = None,
+                 audio_url: str = None,
                  orientation: str = None,
                  idle: str = None,
                  audio_only: str = None,
@@ -117,6 +124,10 @@ class AndroidCameraStatusResponse(CameraResponse):
                  **kwargs):
 
         self.status = {
+            "name": name,
+            "stream_url": stream_url,
+            "image_url": image_url,
+            "audio_url": audio_url,
             "orientation": orientation,
             "idle": True if idle == "on" else False,
             "audio_only": True if audio_only == "on" else False,
diff --git a/platypush/plugins/camera/android/ipcam.py b/platypush/plugins/camera/android/ipcam.py
index 5d812da97..86acac63d 100644
--- a/platypush/plugins/camera/android/ipcam.py
+++ b/platypush/plugins/camera/android/ipcam.py
@@ -148,19 +148,29 @@ class CameraAndroidIpcamPlugin(Plugin):
         :return: True if the camera is available, False otherwise
         """
         cameras = self._camera_name_to_idx.keys() if camera is None else [camera]
-        status = []
+        statuses = []
 
-        for cam in cameras:
+        for c in cameras:
             try:
-                status_data = self._exec('status.json', params={'show_avail': 1}, camera=cam).get('curvals', {})
-                status.append(AndroidCameraStatusResponse(
+                if isinstance(camera, int):
+                    cam = self.cameras[c]
+                else:
+                    cam = self.cameras[self._camera_name_to_idx[c]]
+
+                status_data = self._exec('status.json', params={'show_avail': 1}, camera=cam.name).get('curvals', {})
+                status = AndroidCameraStatusResponse(
+                    name=cam.name,
+                    stream_url=cam.stream_url,
+                    image_url=cam.image_url,
+                    audio_url=cam.audio_url,
                     **{k: v for k, v in status_data.items()
                        if k in AndroidCameraStatusResponse.attrs})
-                )
-            except Exception as e:
-                self.logger.warning('Could not get the status of {}: {}'.format(cam, str(e)))
 
-        return AndroidCameraStatusListResponse(status)
+                statuses.append(status)
+            except Exception as e:
+                self.logger.warning('Could not get the status of {}: {}'.format(c, str(e)))
+
+        return AndroidCameraStatusListResponse(statuses)
 
     @action
     def set_front_facing_camera(self, activate: bool = True, camera: Union[int, str] = None) -> bool:
@@ -246,23 +256,5 @@ class CameraAndroidIpcamPlugin(Plugin):
         """Set video orientation."""
         return self.change_setting('scenemode', scenemode, camera=camera)
 
-    @action
-    def get_stream_url(self, camera: Union[int, str] = None) -> str:
-        """ Get the streaming URL for the specified camera. """
-        cam = self._get_camera(camera)
-        return cam.stream_url
-
-    @action
-    def get_image_url(self, camera: Union[int, str] = None) -> str:
-        """ Get the URL to the camera static picture. """
-        cam = self._get_camera(camera)
-        return cam.image_url
-
-    @action
-    def get_audio_url(self, camera: Union[int, str] = None) -> str:
-        """ Get the URL to the camera audio source. """
-        cam = self._get_camera(camera)
-        return cam.audio_url
-
 
 # vim:sw=4:ts=4:et: