diff --git a/models/calendar_event.py b/models/calendar_event.py
index fa46866756b2b31095ba1b740ef327a2b79541f6..3a21af54e9ac9e1289e4d48249fefeae78a0c6ce 100644
--- a/models/calendar_event.py
+++ b/models/calendar_event.py
@@ -1,124 +1,122 @@
 # Copyright 2021 Le Filament (https://le-filament.com)
 # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
 
+import binascii
 import logging
-import requests
-from datetime import timedelta
+import os
+
+from dateutil.parser import parse
 from dateutil.relativedelta import relativedelta
-from netbluemind.python.client import BMClient
-from netbluemind.core.api.date.BmDateTime import BmDateTime
-from netbluemind.core.api.date.BmDateTimePrecision import BmDateTimePrecision
+from netbluemind.calendar.api.VEvent import VEvent
 from netbluemind.calendar.api.VEventChanges import VEventChanges
 from netbluemind.calendar.api.VEventChangesItemAdd import VEventChangesItemAdd
-from netbluemind.calendar.api.VEventChangesItemModify import VEventChangesItemModify
-from netbluemind.calendar.api.VEventChangesItemDelete import VEventChangesItemDelete
 from netbluemind.calendar.api.VEventSeries import VEventSeries
-from netbluemind.calendar.api.VEvent import VEvent
-from netbluemind.calendar.api.VEventQuery import VEventQuery
-from netbluemind.icalendar.api.ICalendarElementClassification import ICalendarElementClassification
+from netbluemind.core.api.date.BmDateTime import BmDateTime
+from netbluemind.core.api.date.BmDateTimePrecision import BmDateTimePrecision
+from netbluemind.icalendar.api.ICalendarElementClassification import (
+    ICalendarElementClassification,
+)
 from netbluemind.icalendar.api.ICalendarElementRRule import ICalendarElementRRule
-from netbluemind.icalendar.api.ICalendarElementRRuleFrequency import ICalendarElementRRuleFrequency
-from netbluemind.icalendar.api.ICalendarElementRRuleWeekDay import ICalendarElementRRuleWeekDay
+from netbluemind.icalendar.api.ICalendarElementRRuleFrequency import (
+    ICalendarElementRRuleFrequency,
+)
+from netbluemind.icalendar.api.ICalendarElementRRuleWeekDay import (
+    ICalendarElementRRuleWeekDay,
+)
 from netbluemind.python.client import ServerFault
-
 from pytz import timezone
-from dateutil.parser import parse
 
-from odoo import api, fields, models, _
-from odoo.exceptions import UserError
-from odoo.loglevels import exception_to_unicode
+from odoo import api, fields, models
 
 _logger = logging.getLogger(__name__)
 
-
 PRIVACY_CONVERTER_B2O = {
-    'Public': 'public',
-    'Private': 'private',
-    'Confidential': 'confidential'
+    "Public": "public",
+    "Private": "private",
+    "Confidential": "confidential",
 }
 PRIVACY_CONVERTER_O2B = {
-    'public': 'Public',
-    'private': 'Private',
-    'confidential': 'Confidential'
+    "public": "Public",
+    "private": "Private",
+    "confidential": "Confidential",
 }
 
+
 class CalendarEvent(models.Model):
-    _inherit = 'calendar.event'
+    _inherit = "calendar.event"
 
     bluemind_id = fields.Char("Bluemind Event ID")
 
     def _bm_to_odoo_values(self, bm_event):
         # General fields
         data = {
-            'name': bm_event.value.main.summary,
-            'bluemind_id': bm_event.uid,
-            'privacy': PRIVACY_CONVERTER_B2O.get(bm_event.value.main.classification.value, self.default_get(['privacy'])['privacy']),
-            'location': bm_event.value.main.location,
-            'description': bm_event.value.main.description,
-            'user_id': self.env.user.id,
-            'create_uid': self.env.user.id
+            "name": bm_event.value.main.summary,
+            "bluemind_id": bm_event.uid,
+            "privacy": PRIVACY_CONVERTER_B2O.get(
+                bm_event.value.main.classification.value,
+                self.default_get(["privacy"])["privacy"],
+            ),
+            "location": bm_event.value.main.location,
+            "description": bm_event.value.main.description,
+            "user_id": self.env.user.id,
+            "create_uid": self.env.user.id,
         }
 
         # Dates handling
-        if bm_event.value.main.dtstart.precision.value == 'Date':
+        if bm_event.value.main.dtstart.precision.value == "Date":
             start = bm_event.value.main.dtstart.iso8601
             stop = bm_event.value.main.dtend.iso8601
-            data.update({
-                'allday': True,
-                'start_date': start,
-                'stop_date': stop
-            })
+            data.update({"allday": True, "start_date": start, "stop_date": stop})
         else:
             timeZone_start = timezone(bm_event.value.main.dtstart.timezone)
             timeZone_stop = timezone(bm_event.value.main.dtend.timezone)
-            start = parse(bm_event.value.main.dtstart.iso8601).astimezone(timeZone_start).replace(tzinfo=None)
-            stop = parse(bm_event.value.main.dtend.iso8601).astimezone(timeZone_stop).replace(tzinfo=None)
-            data.update({
-                'allday': False,
-                'start': start,
-                'stop': stop
-            })
+            start = (
+                parse(bm_event.value.main.dtstart.iso8601)
+                .astimezone(timeZone_start)
+                .replace(tzinfo=None)
+            )
+            stop = (
+                parse(bm_event.value.main.dtend.iso8601)
+                .astimezone(timeZone_stop)
+                .replace(tzinfo=None)
+            )
+            data.update({"allday": False, "start": start, "stop": stop})
 
         # Recurrency handling
         rrule = bm_event.value.main.rrule
         if rrule:
-            data.update({
-                'recurrency': True,
-                'rrule_type': rrule.frequency.name.lower(),
-                'interval': rrule.interval
-            })
+            data.update(
+                {
+                    "recurrency": True,
+                    "rrule_type": rrule.frequency.name.lower(),
+                    "interval": rrule.interval,
+                }
+            )
             if rrule.count:
-                data.update({
-                    'end_type': 'count',
-                    'count': rrule.count
-                })
+                data.update({"end_type": "count", "count": rrule.count})
             elif rrule.until:
-                data.update({
-                    'end_type': 'end_date',
-                    'until': rrule.until.iso8601[:10]
-                })
+                data.update({"end_type": "end_date", "until": rrule.until.iso8601[:10]})
             else:
-                data.update({
-                    'end_type': 'forever'
-                })
+                data.update({"end_type": "forever"})
             if rrule.frequency.name == "MONTHLY":
                 if rrule.byDay:
-                    data.update({
-                        'month_by': 'day',
-                        'weekday': rrule.byDay[0].day,
-                        rrule.byDay[0].day.lower(): True,
-                        'byday': str(rrule.byDay[0].offset)
-                    })
+                    data.update(
+                        {
+                            "month_by": "day",
+                            "weekday": rrule.byDay[0].day,
+                            rrule.byDay[0].day.lower(): True,
+                            "byday": str(rrule.byDay[0].offset),
+                        }
+                    )
                 else:
-                    data.update({
-                        'month_by': 'date',
-                        'day': rrule.byMonthDay
-                    })
+                    data.update({"month_by": "date", "day": rrule.byMonthDay})
             elif rrule.frequency.name == "WEEKLY":
-                data.update({
-                    'weekday': rrule.byDay[0].day,
-                    rrule.byDay[0].day.lower(): True,
-                })
+                data.update(
+                    {
+                        "weekday": rrule.byDay[0].day,
+                        rrule.byDay[0].day.lower(): True,
+                    }
+                )
         return data
 
     def _odoo_to_bm_values(self, event):
@@ -126,80 +124,103 @@ class CalendarEvent(models.Model):
         bm_event.uid = self.generate_uid_bluemind()
         bm_event.value = VEventSeries()
         bm_event.value.main = VEvent()
-        bm_event.value.main.classification = ICalendarElementClassification()
+        bm_event.value.main.classification = ICalendarElementClassification(
+            PRIVACY_CONVERTER_O2B.get(event.privacy)
+        )
         bm_event.value.main.dtstart = BmDateTime()
-        bm_event.value.main.dtstart.precision = BmDateTimePrecision()
         bm_event.value.main.dtend = BmDateTime()
-        bm_event.value.main.dtend.precision = BmDateTimePrecision()
         bm_event.sendNotification = False
 
         bm_event.value.main.summary = event.name
-        bm_event.value.main.description = event.description or ''
-        bm_event.value.main.location = event.location or ''
-        bm_event.value.main.classification.value = PRIVACY_CONVERTER_O2B.get(event.privacy)
+        bm_event.value.main.description = event.description or ""
+        bm_event.value.main.location = event.location or ""
+
+        # These fields are required (although not marked as such in doc / code)
+        # Otherwise you get a NullPointerException
+        bm_event.value.main.attendees = []
+        bm_event.value.main.categories = []
+        bm_event.value.main.attachments = []
 
         if event.allday:
-            bm_event.value.main.dtstart.iso8601 = event.start_date.to_string()
-            bm_event.value.main.dtstart.precision.value = 'Date'
+            bm_event.value.main.dtstart.iso8601 = event.start_date.isoformat()
+            bm_event.value.main.dtstart.precision = BmDateTimePrecision("Date")
             stop_date = event.stop_date + relativedelta(days=1)
-            bm_event.value.main.dtend.iso8601 = stop_date.strftime("%Y-%m-%d")
-            bm_event.value.main.dtend.precision.value = 'Date'
+            bm_event.value.main.dtend.iso8601 = stop_date.isoformat()
+            bm_event.value.main.dtend.precision = BmDateTimePrecision("Date")
         else:
-            bm_event.value.main.dtstart.iso8601 = event.start.strftime("%Y-%m-%dT%H:%M:%S")
-            bm_event.value.main.dtstart.precision.value = 'DateTime'
-            bm_event.value.main.dtend.iso8601 = event.stop.strftime("%Y-%m-%dT%H:%M:%S")
-            bm_event.value.main.dtend.precision.value = 'DateTime'
-
-        bm_event.value.main.dtstart.timezone = timezone(event.event_tz)
-        bm_event.value.main.dtend.timezone = timezone(event.event_tz)
+            tz = timezone(self.env.context.get("tz"))
+            bm_event.value.main.dtstart.iso8601 = event.start.astimezone(tz).isoformat(
+                timespec="milliseconds"
+            )
+            bm_event.value.main.dtstart.timezone = tz.zone
+            bm_event.value.main.dtstart.precision = BmDateTimePrecision("DateTime")
+            bm_event.value.main.dtend.iso8601 = event.stop.astimezone(tz).isoformat(
+                timespec="milliseconds"
+            )
+            bm_event.value.main.dtend.timezone = tz.zone
+            bm_event.value.main.dtend.precision = BmDateTimePrecision("DateTime")
 
         # Recurrency Handling
         if event.recurrency:
             bm_event.value.main.rrule = ICalendarElementRRule()
-            bm_event.value.main.rrule.frequency = ICalendarElementRRuleFrequency()
-            bm_event.value.main.rrule.frequency.value = event.frequency.toupper()
+            bm_event.value.main.rrule.frequency = ICalendarElementRRuleFrequency(
+                event.frequency.toupper()
+            )
             bm_event.value.main.rrule.interval = event.interval
-            if end_type == "count":
+            if event.end_type == "count":
                 bm_event.value.main.rrule.count = event.count
-            elif end_type == "date_end":
+            elif event.end_type == "end_date":
                 bm_event.value.main.rrule.until = BmDateTime()
-                bm_event.value.main.rrule.until.iso8601 = event.until.strftime("%Y-%m-%d")
-                bm_event.value.main.rrule.until.precision = BmDateTimePrecision()
-                bm_event.value.main.rrule.until.precision.value = 'Date'
+                bm_event.value.main.rrule.until.iso8601 = event.until.strftime(
+                    "%Y-%m-%d"
+                )
+                bm_event.value.main.rrule.until.precision = BmDateTimePrecision("Date")
                 bm_event.value.main.rrule.until.timezone = event.event_tz
 
-            if (event.frequency == "monthly" and event.month_by == "day") or event.frequency == "weekly":
-                    bm_event.value.main.rrule.byDay = [ICalendarElementRRuleWeekDay()]
-                    bm_event.value.main.rrule.byDay[0].day = str(event.weekday)
-                    bm_event.value.main.rrule.byDay[0].offset = int(event.byday)
+            if (
+                event.frequency == "monthly" and event.month_by == "day"
+            ) or event.frequency == "weekly":
+                bm_event.value.main.rrule.byDay = [ICalendarElementRRuleWeekDay()]
+                bm_event.value.main.rrule.byDay[0].day = str(event.weekday)
+                bm_event.value.main.rrule.byDay[0].offset = int(event.byday)
             elif event.frequency == "monthly" and event.month_by == "date":
                 bm_event.value.main.rrule.byMonthDay = [event.day]
 
         return bm_event
 
     def generate_uid_bluemind(self):
-        '''
+        """
         Creates a uid which corresponds to bluemind representation\
         (needed for all uplink creation)
-        '''
-        uid = binascii.b2a_hex(os.urandom(4)) + '-'.encode() \
-            + binascii.b2a_hex(os.urandom(2)) + '-'.encode() \
-            + binascii.b2a_hex(os.urandom(2)) + '-'.encode() \
-            + binascii.b2a_hex(os.urandom(2)) + '-'.encode() \
+        """
+        uid = (
+            binascii.b2a_hex(os.urandom(4))
+            + b"-"
+            + binascii.b2a_hex(os.urandom(2))
+            + b"-"
+            + binascii.b2a_hex(os.urandom(2))
+            + b"-"
+            + binascii.b2a_hex(os.urandom(2))
+            + b"-"
             + binascii.b2a_hex(os.urandom(6))
-        return uid
+        )
+        return uid.decode()
 
     # TODO: Create function to update Odoo event from Bluemind updated event
     def update_odoo_event_from_bm(self, odoo_event, bm_event):
         return NotImplementedError
 
     # TODO: add function to update Bluemind object when Odoo object is modified
+    # Take caution about attendees (only one calendar event in Odoo for all attendees,
+    # when in Bluemind you get a different one per attendee)
     def write(self, values):
         return super().write(values)
 
+    @api.model_create_multi
     def create(self, vals_list):
         odoo_events = super().create(vals_list)
         bm_events = VEventChanges()
+        bm_events.add = []
         for odoo_event in odoo_events:
             if not odoo_event.bluemind_id:
                 bm_event = self._odoo_to_bm_values(odoo_event)
@@ -207,24 +228,28 @@ class CalendarEvent(models.Model):
                 odoo_event.bluemind_id = bm_event.uid
         if bm_events.add:
             try:
-                bm_calendar = self.env.user.bluemind_auth().calendar(self.bluemind_calendar_id)
+                bm_calendar = self.env.user.bluemind_auth().calendar(
+                    self.env.user.bluemind_calendar_id
+                )
                 bm_calendar.updates(bm_events)
-            except (ServerFault) as e:
+            except (ServerFault, Exception) as e:
                 # TODO: better manage exceptions
-                print(e)
+                _logger.warning(
+                    "Did not manage to push events to Bluemind, error [%s]", e
+                )
         return odoo_events
 
     def unlink(self, from_bluemind=False):
-        '''Unlink override, deletes an event from Odoo to BlueMind'''
+        """Unlink override, deletes an event from Odoo to BlueMind"""
         if not from_bluemind:
             for event in self:
                 if event.bluemind_id:
-                    bm_calendar = self.env.user.bluemind_auth().calendar(self.bluemind_calendar_id)
+                    bm_calendar = self.env.user.bluemind_auth().calendar(
+                        self.env.user.bluemind_calendar_id
+                    )
                     try:
                         bm_calendar.delete(event.bluemind_id, False)
-                    except (ServerFault) as e:
+                    except (ServerFault):
                         # TODO: better manage exceptions
                         event.active = False
-        return super(
-            CalendarEvent,
-            self.filtered(lambda e: e.active is True)).unlink()
+        return super(CalendarEvent, self.filtered(lambda e: e.active is True)).unlink()
diff --git a/models/res_users.py b/models/res_users.py
index f791deadb93480c2a4c84754d79432a3b88247e6..0bd3044e456f2da86c0ccfe8949d4feeedaabb9b 100644
--- a/models/res_users.py
+++ b/models/res_users.py
@@ -2,50 +2,67 @@
 # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
 
 import logging
-import requests
-from datetime import timedelta
-from netbluemind.python.client import BMClient
-from netbluemind.python.client import ServerFault
 
-from odoo import api, fields, models, _
+from netbluemind.python.client import BMClient, ServerFault
+
+from odoo import _, api, fields, models
 from odoo.exceptions import UserError
-from odoo.loglevels import exception_to_unicode
 
 _logger = logging.getLogger(__name__)
 
+
 class ResUser(models.Model):
-    _inherit = 'res.users'
+    _inherit = "res.users"
 
     bluemind_login = fields.Char("Bluemind e-mail")
     bluemind_password = fields.Char("Bluemind password or API key")
     is_bm_connection_ok = fields.Boolean("Bluemind Connection OK", default=False)
-    bluemind_user_id = fields.Char("Bluemind user ID", compute='_retrieve_bluemind_user_id', store=True)
-    bluemind_calendar_id = fields.Char("Bluemind calendar ID", compute='_compute_bluemind_calendar_uid', store=True)
+    bluemind_user_id = fields.Char(
+        "Bluemind user ID", compute="_compute_bluemind_user_id", store=True
+    )
+    bluemind_calendar_id = fields.Char(
+        "Bluemind calendar ID", compute="_compute_bluemind_calendar_uid", store=True
+    )
     last_sync_version = fields.Integer("Last sync version", readonly=True, default=0)
 
     # Authentication to Bluemind
     @api.depends("company_id.bluemind_url", "bluemind_login", "bluemind_password")
-    def bluemind_auth(self):
+    def bluemind_auth(self, force=False):
         self.ensure_one()
-
-        if self.company_id.bluemind_url and self.bluemind_login and self.bluemind_password:
+        if (
+            self.company_id.bluemind_url
+            and self.bluemind_login
+            and self.bluemind_password
+            and (self.is_bm_connection_ok or force)
+        ):
             try:
                 client = BMClient(self.company_id.bluemind_url + "/api")
                 client.login(self.bluemind_login, self.bluemind_password)
                 self.is_bm_connection_ok = True
                 return client
-                # TODO: add boolean if connection is OK, in order not to sync users with NOK connection
             # TODO: better manage exception
             except (ServerFault, Exception) as e:
-                raise UserError(_("Something went wrong during connection : [%s]", e))
                 self.is_bm_connection_ok = False
+                if force:
+                    raise UserError(
+                        _("Something went wrong during connection : [%s]", e)
+                    )
             return False
 
     @api.depends("company_id.bluemind_domain", "bluemind_login", "bluemind_password")
-    def _retrieve_bluemind_user_id(self):
+    def _compute_bluemind_user_id(self):
         for user in self:
-            if user.company_id.bluemind_url and user.bluemind_login and user.bluemind_password:
-                user.bluemind_user_id = user.bluemind_auth().directory(user.company_id.bluemind_domain).getByEmail(user.bluemind_login).entryUid
+            if (
+                user.company_id.bluemind_url
+                and user.bluemind_login
+                and user.bluemind_password
+            ):
+                user.bluemind_user_id = (
+                    user.bluemind_auth()
+                    .directory(user.company_id.bluemind_domain)
+                    .getByEmail(user.bluemind_login)
+                    .entryUid
+                )
 
     @api.depends("bluemind_user_id")
     def _compute_bluemind_calendar_uid(self):
@@ -66,14 +83,18 @@ class ResUser(models.Model):
         bm_last_version = bm_changeset.version
 
         Calendar = self.env["calendar.event"]
-        odoo_events_bm_linked = Calendar.search([("user_id", "=", self.id), ("bluemind_id", "!=", False)])
+        odoo_events_bm_linked = Calendar.search(
+            [("user_id", "=", self.id), ("bluemind_id", "!=", False)]
+        )
         odoo_events_bm_uids = odoo_events_bm_linked.mapped("bluemind_id")
 
         # Calendar entries created on Bluemind side, not already in Odoo
         # TODO: move events already in Odoo to updated section ?
         # May need to distinguish between : first sync ever, new sync after unsync
         events_to_create = []
-        bm_events_to_create_uids = [e for e in bm_created_uids if e not in odoo_events_bm_uids]
+        bm_events_to_create_uids = [
+            e for e in bm_created_uids if e not in odoo_events_bm_uids
+        ]
         bm_events_to_create = bm_calendar.multipleGet(bm_events_to_create_uids)
         for bm_event in bm_events_to_create:
             events_to_create.append(Calendar._bm_to_odoo_values(bm_event))
@@ -82,11 +103,15 @@ class ResUser(models.Model):
         # Calendar entries that have been modified on Bluemind
         bm_events_to_update = bm_calendar.multipleGet(bm_updated_uids)
         for bm_event in bm_events_to_update:
-            odoo_event = odoo_events_bm_linked.filtered([("bluemind_id", "=", bm_event.uid)])
+            odoo_event = odoo_events_bm_linked.filtered(
+                [("bluemind_id", "=", bm_event.uid)]
+            )
             Calendar.update_odoo_event_from_bm(odoo_event, bm_event)
 
         # Calendar entries that have been deleted on Bluemind to be deleted on Odoo
-        odoo_events_to_delete = odoo_events_bm_linked.filtered([("bluemind_id", "in", bm_deleted_uids)])
+        odoo_events_to_delete = odoo_events_bm_linked.filtered(
+            [("bluemind_id", "in", bm_deleted_uids)]
+        )
         odoo_events_to_delete.unlink(from_bluemind=True)
 
         self.last_sync_version = bm_last_version