diff --git a/addons/bus/__openerp__.py b/addons/bus/__openerp__.py
index 2489f6a38f972993800bc7ce4c07fe21ae44b44d..2a7185fdaf7b8e570e35406ba07a2628a8413d0e 100644
--- a/addons/bus/__openerp__.py
+++ b/addons/bus/__openerp__.py
@@ -6,6 +6,7 @@
     'description': "Instant Messaging Bus allow you to send messages to users, in live.",
     'depends': ['base', 'web'],
     'data': [
+        'bus_presence_cron.xml',
         'views/bus.xml',
         'security/ir.model.access.csv',
     ],
diff --git a/addons/bus/bus_presence_cron.xml b/addons/bus/bus_presence_cron.xml
new file mode 100644
index 0000000000000000000000000000000000000000..309c641e3f79f9d27612f597ef6b544a6b19e1bc
--- /dev/null
+++ b/addons/bus/bus_presence_cron.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding='UTF-8'?>
+<odoo>
+	<record model="ir.cron" id="account_asset_cron">
+        <field name="name">Check User Disconnections</field>
+        <field name="interval_number">5</field>
+        <field name="interval_type">minutes</field>
+        <field name="numbercall">-1</field>
+        <field name="doall" eval="False"/>
+        <field name="model" eval="'bus.presence'"/>
+        <field name="function" eval="'check_users_disconnection'"/>
+        <field name="args" eval="'()'" />
+    </record>
+</odoo>
diff --git a/addons/bus/controllers/main.py b/addons/bus/controllers/main.py
index 050eb684625992f2d3f23881a217e5bab0ca3162..d755cabe54fe766c9d0b37b187b443bec5f0b6ff 100644
--- a/addons/bus/controllers/main.py
+++ b/addons/bus/controllers/main.py
@@ -19,6 +19,7 @@ class BusController(openerp.http.Controller):
 
     # override to add channels
     def _poll(self, dbname, channels, last, options):
+        channels.append((request.db, 'bus.presence'))
         # update the user presence
         if request.session.uid and 'im_presence' in options:
             request.env['bus.presence'].update(options.get('im_presence'))
diff --git a/addons/bus/models/bus_presence.py b/addons/bus/models/bus_presence.py
index 445e9491a7550915fa452011775c895d9667d296..70aa9ef97269858323d70946b6a2aaecaf1c5f00 100644
--- a/addons/bus/models/bus_presence.py
+++ b/addons/bus/models/bus_presence.py
@@ -1,6 +1,5 @@
 # -*- coding: utf-8 -*-
 import datetime
-import random
 import time
 
 from openerp import api, fields, models
@@ -11,6 +10,8 @@ from openerp.addons.bus.models.bus import TIMEOUT
 
 DISCONNECTION_TIMER = TIMEOUT + 5
 AWAY_TIMER = 600 # 10 minutes
+DISCONNECTIONS_CHECK_PERIOD = datetime.timedelta(minutes=1)  # check for user disconnections every minute
+last_disconnections_check = datetime.datetime.utcnow()
 
 
 class BusPresence(models.Model):
@@ -34,7 +35,7 @@ class BusPresence(models.Model):
     @api.model
     def update(self, user_presence=True):
         """ Register the given presence of the current user, and trigger a im_status change if necessary.
-            The status will not be written or sent if not necessary.
+            The status will not be sent if not necessary.
             :param user_presence : True, if the user (self._uid) is still detected using its browser.
             :type user_presence : boolean
         """
@@ -51,37 +52,31 @@ class BusPresence(models.Model):
             values['user_id'] = self._uid
             self.create(values)
         else:  # write the user presence if necessary
-            if user_presence:
-                values['last_presence'] = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
-                values['status'] = 'online'
-            else:
-                threshold = datetime.datetime.now() - datetime.timedelta(seconds=AWAY_TIMER)
-                if datetime.datetime.strptime(presence.last_presence, DEFAULT_SERVER_DATETIME_FORMAT) < threshold:
-                    values['status'] = 'away'
+            values['status'] = 'online' if user_presence else 'away'
             send_notification = presence.status != values['status']
-            # write only if the last_poll is passed TIMEOUT, or if the status has changed
-            delta = datetime.datetime.utcnow() - datetime.datetime.strptime(presence.last_poll, DEFAULT_SERVER_DATETIME_FORMAT)
-            if delta > datetime.timedelta(seconds=TIMEOUT) or send_notification:
-                # Hide transaction serialization errors, which can be ignored, the presence update is not essential
-                with tools.mute_logger('openerp.sql_db'):
-                    presence.write(values)
+            # Hide transaction serialization errors, which can be ignored, the presence update is not essential
+            with tools.mute_logger('openerp.sql_db'):
+                presence.write(values)
         # avoid TransactionRollbackError
         self.env.cr.commit() # TODO : check if still necessary
         # notify if the status has changed
         if send_notification: # TODO : add user_id to the channel tuple to allow using user_watch in controller presence
-            self.env['bus.bus'].sendone((self._cr.dbname, 'bus.presence'), {'id': self._uid, 'im_status': values['status']})
-        # gc : disconnect the users having a too old last_poll. 1 on 100 chance to do it.
-        if random.random() < 0.01:
-            self.check_users_disconnection()
+            self.env['bus.bus'].sendone((self._cr.dbname, 'bus.presence'), {'id': self.env.user.partner_id.id, 'im_status': values['status']})
+        # check for disconnected users
+        self.check_users_disconnection()
         return True
 
     @api.model
     def check_users_disconnection(self):
         """ Disconnect the users having a too old last_poll """
-        limit_date = (datetime.datetime.utcnow() - datetime.timedelta(0, DISCONNECTION_TIMER)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
-        presences = self.search([('last_poll', '<', limit_date), ('status', '!=', 'offline')])
-        presences.write({'status': 'offline'})
+        global last_disconnections_check
+        now = datetime.datetime.utcnow()
         notifications = []
-        for presence in presences:
-            notifications.append([(self._cr.dbname, 'bus.presence'), {'id': presence.user_id.id, 'im_status': presence.status}])
+        if (now - DISCONNECTIONS_CHECK_PERIOD) > last_disconnections_check:
+            last_disconnections_check = now
+            limit_date = (now - datetime.timedelta(0, DISCONNECTION_TIMER)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
+            presences = self.search([('last_poll', '<', limit_date), ('status', '!=', 'offline')])
+            presences.write({'status': 'offline'})
+            for presence in presences:
+                notifications.append([(self._cr.dbname, 'bus.presence'), {'id': presence.user_id.partner_id.id, 'im_status': presence.status}])
         self.env['bus.bus'].sendmany(notifications)
diff --git a/addons/bus/static/src/js/bus.js b/addons/bus/static/src/js/bus.js
index 9373b7c7b1c034758912d0cb7fb1a2a5c9e7b2e2..29540b36cdff3d74331adc7b0374b034c1c1bf3e 100644
--- a/addons/bus/static/src/js/bus.js
+++ b/addons/bus/static/src/js/bus.js
@@ -7,11 +7,15 @@ var Widget = require('web.Widget');
 var bus = {};
 
 bus.ERROR_DELAY = 10000;
+bus.AWAY_TIMEOUT = 300000;  // 5 minutes
 
 bus.Bus = Widget.extend({
     init: function(){
+        var self = this;
         this._super();
-        this.options = {};
+        this.options = {
+            im_presence: true,
+        };
         this.activated = false;
         this.channels = [];
         this.last = 0;
@@ -21,9 +25,14 @@ bus.Bus = Widget.extend({
         // bus presence
         this.set("window_focus", true);
         this.on("change:window_focus", this, function () {
-            this.options.im_presence = this.get("window_focus");
+            clearTimeout(self.away_timeout);
             if (this.get("window_focus")) {
+                this.options.im_presence = true;
                 this.trigger('window_focus', this.is_master);
+            } else {
+                this.away_timeout = setTimeout(function () {
+                    self.options.im_presence = false;
+                }, bus.AWAY_TIMEOUT);
             }
         });
         $(window).on("focus", _.bind(this.window_focus, this));
@@ -120,13 +129,6 @@ var CrossTabBus = bus.Bus.extend({
         }
 
         on("storage", this.on_storage.bind(this));
-        if (this.is_master) {
-            setItem('bus.channels', this.channels);
-            setItem('bus.options', this.options);
-        } else {
-            this.channels = getItem('bus.channels', this.channels);
-            this.options = getItem('bus.options', this.options);
-        }
     },
     start_polling: function(){
         var self = this;
@@ -139,6 +141,13 @@ var CrossTabBus = bus.Bus.extend({
                 self.is_master = false;
                 self.stop_polling();
             });
+            if (this.is_master) {
+                setItem('bus.channels', this.channels);
+                setItem('bus.options', this.options);
+            } else {
+                this.channels = getItem('bus.channels', this.channels);
+                this.options = getItem('bus.options', this.options);
+            }
             return;  // start_polling will be called again on tab registration
         }
 
diff --git a/addons/mail/static/src/js/chat_manager.js b/addons/mail/static/src/js/chat_manager.js
index 909a4a0ff5b04d19d74292e31acec70b1d159bba..cd758790d64d8b5f76cf4700db6b83b56bfe26be 100644
--- a/addons/mail/static/src/js/chat_manager.js
+++ b/addons/mail/static/src/js/chat_manager.js
@@ -272,6 +272,7 @@ function make_channel (data, options) {
     if ('direct_partner' in data) {
         channel.type = "dm";
         channel.name = data.direct_partner[0].name;
+        channel.direct_partner_id = data.direct_partner[0].id;
         channel.status = data.direct_partner[0].im_status;
     }
     return channel;
@@ -399,6 +400,9 @@ function on_notification (notification) {
     } else if (model === 'res.partner') {
         // channel joined/left, message marked as read/(un)starred, chat open/closed
         on_partner_notification(notification[1]);
+    } else if (model === 'bus.presence') {
+        // update presence of users
+        on_presence_notification(notification[1]);
     }
 }
 
@@ -530,6 +534,14 @@ function on_chat_session_notification (chat_session) {
     }
 }
 
+function on_presence_notification (data) {
+    var dm = _.findWhere(channels, {direct_partner_id: data.id});
+    if (dm) {
+        dm.status = data.im_status;
+        chat_manager.bus.trigger('update_dm_presence', dm);
+    }
+}
+
 // Public interface
 //----------------------------------------------------------------------------------
 var chat_manager = {
diff --git a/addons/mail/static/src/js/client_action.js b/addons/mail/static/src/js/client_action.js
index 1a12ff36912f6b8d1a9a01ed089fb385f727ce60..3d97a35af0e80c91df0aee55d592619570d87cb3 100644
--- a/addons/mail/static/src/js/client_action.js
+++ b/addons/mail/static/src/js/client_action.js
@@ -52,8 +52,8 @@ var PartnerInviteDialog = Dialog.extend({
             width: '100%',
             allowClear: true,
             multiple: true,
-            formatResult: function(item){
-                var css_class = "fa-circle" + (item.im_status === 'online' ? "" : "-o");
+            formatResult: function(item) {
+                var css_class = (item.im_status === 'away' ? "fa-clock-o" : "fa-circle" + (item.im_status === 'online' ? "" : "-o"));
                 return $('<span class="fa">').addClass(css_class).text(item.text);
             },
             query: function (query) {
@@ -135,6 +135,7 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
         this.action = action;
         this.options = options || {};
         this.channels_scrolltop = {};
+        this.throttled_render_sidebar = _.throttle(this.render_sidebar.bind(this), 100, { leading: false });
     },
 
     willStart: function () {
@@ -213,9 +214,10 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
                 chat_manager.bus.on('anyone_listening', self, function (channel, query) {
                     query.is_displayed = query.is_displayed || channel.id === self.channel.id;
                 });
-                chat_manager.bus.on('update_needaction', self, self.render_sidebar);
                 chat_manager.bus.on('unsubscribe_from_channel', self, self.render_sidebar);
-                chat_manager.bus.on('update_channel_unread_counter', self, self.render_sidebar);
+                chat_manager.bus.on('update_needaction', self, self.throttled_render_sidebar);
+                chat_manager.bus.on('update_channel_unread_counter', self, self.throttled_render_sidebar);
+                chat_manager.bus.on('update_dm_presence', self, self.throttled_render_sidebar);
             });
     },
 
diff --git a/addons/mail/static/src/xml/client_action.xml b/addons/mail/static/src/xml/client_action.xml
index 11b32ee0e8dc5cb0895e3809b2709a502f0158f0..bb29d8cca2706e5c2b44185fc888ae6864e15ee9 100644
--- a/addons/mail/static/src/xml/client_action.xml
+++ b/addons/mail/static/src/xml/client_action.xml
@@ -77,7 +77,7 @@
             <t t-set="counter" t-value="channel.needaction_counter"/>
             <div t-if="channel.type === channel_type" t-att-data-channel-id="channel.id"
                  t-attf-class="o_mail_chat_channel_item #{channel.unread_counter ? ' o_unread_message' : ''} #{(active_channel_id == channel.id) ? 'o_active': ''}">
-                <span><i t-if="display_status" t-att-class="'o_user_status fa ' + (channel.status == 'online' ? 'fa-circle' : 'fa-circle-o')"/></span>
+                <span><i t-if="display_status" t-att-class="'o_user_status fa ' + (channel.status == 'online' ? 'fa-circle' : (channel.status == 'away' ? 'fa-clock-o' : 'fa-circle-o'))" t-attf-title="#{channel.status}"/></span>
                 <span t-if="display_hash" class="o_mail_hash">#</span>
                 <t t-esc="channel.name"/>
                 <i t-if="channel.mass_mailing" class="fa fa-envelope-o"/>