text: import logging
  from datetime import date, timedelta
  
  from .api import ApiConnector, Urls, Payloads, Defaults
  from .model import Mapper, System, HotWater, QuickMode, QuickVeto, Room, Zone, HeatingMode, Circulation, Constants
  
  LOGGER = logging.getLogger('SystemManager')
  
  
  class SystemManager:
      """
      This is the main class to use if you want to do more advanced things with your system.
  
      The manager is throwing :exc:`vr900connector.api.ApiError` (thrown by :class:`vr900connector.api.ApiConnector`)
      without altering it
  
      Args:
          user: User for login
          password: Password for login
          smart_phone_id: Smart phone id required by the API
          file_path: Where to store files created by the underlying connector.
      """
  
      def __init__(self, user: str, password: str, smart_phone_id: str = Defaults.SMART_PHONE_ID,
                   file_path: str = Defaults.FILES_PATH):
          self._connector = ApiConnector(user, password, smart_phone_id, file_path)
  
      def get_system(self):
          full_system = self._connector.get(Urls.system())
          live_report = self._connector.get(Urls.live_report())
          hvac_state = self._connector.get(Urls.hvac())
  
          holiday_mode = Mapper.holiday_mode(full_system)
          boiler_status = Mapper.boiler_status(hvac_state, live_report)
  
          zones = Mapper.zones(full_system)
  
          rooms = None
          for zone in zones:
              if zone.rbr:
                  raw_rooms = self._connector.get(Urls.rooms())
                  rooms = Mapper.rooms(raw_rooms)
                  break
  
          hot_water = Mapper.domestic_hot_water(full_system, live_report)
          circulation = Mapper.circulation(full_system)
  
          outdoorTemperature = Mapper.outdoor_temp(full_system)
          quickMode = Mapper.quick_mode(full_system)
          errors = Mapper.errors(hvac_state)
  
          return System(holiday_mode, boiler_status, zones, rooms, hot_water, circulation, outdoorTemperature, quickMode,
                        errors)
  
      def get_hot_water(self, hot_water: HotWater):
          full_system = self._connector.get(Urls.hot_water(hot_water.id))
          live_report = self._connector.get(Urls.live_report())
          return Mapper.domestic_hot_water_alone(full_system, hot_water.id, live_report)
  
      def get_room(self, room: Room):
          new_room = self._connector.get(Urls.room(room.id))
          return Mapper.room(new_room)
  
      def get_zone(self, zone: Zone):
          new_zone = self._connector.get(Urls.zone(zone.id))
          return Mapper.zone(new_zone)
  
      def get_circulation(self, circulation: Circulation):
          new_circulation = self._connector.get(Urls.circulation(circulation.id))
          return Mapper.circulation_alone(new_circulation, circulation.id)
  
      def set_hot_water_setpoint_temperature(self, hot_water: HotWater, temperature: float):
          """
          This set the target temperature for the hotwater.
  
          :param hot_water: the hot_water you want to set target temperature
          :param temperature: the temperature
          :return: True/False whether the update occurred or not
          """
          LOGGER.info("Will try to set dhw target temperature to %s", temperature)
          if temperature and hot_water:
              self._connector.put(Urls.hot_water_temperature_setpoint(hot_water.id),
                                  Payloads.hotwater_temperature_setpoint(self._round(temperature)))
              return True
          else:
              LOGGER.debug("No temperature nor hot_water provided, nothing to do")
              return False
  
      def set_hot_water_operation_mode(self, hotwater: HotWater, new_mode: HeatingMode):
          """
          Set new operation mode for the hot water.
  
          :param hotwater: the :class:`vr900connector.model.HotWater` representing to current hotwater component
          :param new_mode: Name of the new mode, see :mod:`vr900connector.model.HeatingMode`
          :return: True/False whether new_mode has been set or not
          """
          LOGGER.info("Will try to set hot water mode to %s", new_mode)
  
          if hotwater:
              if new_mode:
                  if new_mode in HotWater.MODES:
                      LOGGER.debug("New mode is %s", new_mode)
                      self._connector.put(Urls.hot_water_operation_mode(hotwater.id),
                                          Payloads.hot_water_operation_mode(new_mode.name))
                      return True
                  else:
                      LOGGER.debug("New mode is not available for hot water %s", new_mode)
                      return False
              else:
                  LOGGER.debug("No new mode provided, nothing to do")
                  return False
          else:
              LOGGER.debug("No hotwater provided")
              return False
  
      def set_room_operation_mode(self, room: Room, new_mode: HeatingMode):
          """
          Set new operation mode for a room.
  
          :param room: the :class:`vr900connector.model.Room` representing to current room component
          :param new_mode:
          :return: True/False whether new_mode has been set or not
          """
  
          if room:
              if new_mode:
                  if new_mode in Room.MODES and new_mode != HeatingMode.QUICK_VETO:
                      LOGGER.debug("New mode is %s", new_mode)
                      self._connector.put(Urls.room_operation_mode(room.id), Payloads.room_operation_mode(new_mode.name))
                      return True
                  else:
                      LOGGER.debug("New mode is not available for room %s", new_mode)
                      return False
              else:
                  LOGGER.debug("No new mode provided, nothing to do")
                  return False
          else:
              LOGGER.debug("No room provided")
              return False
  
      def set_zone_operation_mode(self, zone: Zone, new_mode: HeatingMode):
          """
          Set new operation mode for a zone.
  
          :param zone: the :class:`vr900connector.model.Zone` representing to current zone component
          :param new_mode:
          :return: True/False whether new_mode has been set or not
          """
  
          if zone:
              if new_mode:
                  if new_mode in Zone.MODES and new_mode != HeatingMode.QUICK_VETO:
                      LOGGER.debug("New mode is %s", new_mode)
                      self._connector.put(Urls.zone_heating_mode(zone.id), Payloads.zone_operation_mode(new_mode.name))
                      return True
                  else:
                      LOGGER.debug("New mode is not available for zone %s", new_mode)
                      return False
              else:
                  LOGGER.debug("No new mode provided, nothing to do")
                  return False
          else:
              LOGGER.debug("No zone provided")
              return False
  
      def set_quick_mode(self, current_quick_mode: QuickMode, new_quick_mode: QuickMode):
          """
          Set quick mode system wise
          :return: True/False whether new_mode has been set or not
          """
  
          if not current_quick_mode:
              if new_quick_mode:
                  self._connector.put(Urls.system_quickmode(), Payloads.quickmode(new_quick_mode.name))
                  return True
              else:
                  LOGGER.debug("No new quick mode provided")
                  return False
          else:
              LOGGER.debug("There is already a quick mode in place: %s", current_quick_mode.name)
              return False
  
      def set_room_quick_veto(self, room: Room, quick_veto: QuickVeto):
          if quick_veto and room:
              self._connector.put(Urls.room_quick_veto(room.id),
                                  Payloads.room_quick_veto(quick_veto.target_temperature, quick_veto.remaining_time))
              return True
          else:
              LOGGER.debug("Quick veto %s or room %s not provided", quick_veto, room)
              return False
  
      def remove_room_quick_veto(self, room: Room):
          self._connector.delete(Urls.room_quick_veto(room.id))
          return True
  
      def set_zone_quick_veto(self, zone: Zone, quick_veto: QuickVeto):
          if quick_veto and zone:
              self._connector.put(Urls.zone_quick_veto(zone.id),
                                  Payloads.zone_quick_veto(quick_veto.target_temperature))
              return True
          else:
              LOGGER.debug("Quick veto %s or zone %s not provided", quick_veto, zone)
              return False
  
      def remove_zone_quick_veto(self, zone: Zone):
          self._connector.delete(Urls.zone_quick_veto(zone.id))
          return True
  
      def set_room_setpoint_temperature(self, room: Room, temperature: float):
          """
          This set the target temperature for a room.
  
          :param room: the room you want to set target temperature
          :param temperature: the temperature
          :return: True/False whether the update occurred or not
          """
          LOGGER.info("Will try to set room target temperature to %s", temperature)
          if temperature and room:
              self._connector.put(Urls.room_set_temperature_setpoint(room.id),
                                  Payloads.room_temperature_setpoint(self._round(temperature)))
              return True
          else:
              LOGGER.debug("No temperature nor room provided, nothing to do")
              return False
  
      def set_zone_setpoint_temperature(self, zone: Zone, temperature: float):
          """
          This set the target temperature for a zone.
  
          :param zone: the zone you want to set target temperature
          :param temperature: the temperature
          :return: True/False whether the update occurred or not
          """
          LOGGER.info("Will try to set zone target temperature to %s", temperature)
          if temperature and zone:
              self._connector.put(Urls.zone_heating_setpoint_temperature(zone.id),
                                  Payloads.zone_temperature_setpoint(self._round(temperature)))
              return True
          else:
              LOGGER.debug("No temperature nor zone provided, nothing to do")
              return False
  
      def set_zone_setback_temperature(self, zone: Zone, temperature: float):
          """
          This set the setback temperature for a zone.
  
          :param zone: the zone you want to set setback temperature
          :param temperature: the temperature
          :return: True/False whether the update occurred or not
          """
          LOGGER.info("Will try to set zone setback temperature to %s", temperature)
          if temperature and zone:
              self._connector.put(Urls.zone_heating_setback_temperature(zone.id),
                                  Payloads.zone_temperature_setback(self._round(temperature)))
              return True
          else:
              LOGGER.debug("No temperature nor zone provided, nothing to do")
              return False
  
      def set_holiday_mode(self, start_date: date, end_date: date, temperature: float):
          """
          Set the holiday mode
  
          :param start_date: starting date of the holiday mode
          :param end_date: ending date of the holiday mode
          :param temperature: minimal temperature
          :return: True if update occurred
          """
          self._connector.put(Urls.system_holiday_mode(), Payloads.holiday_mode(True, start_date, end_date, temperature))
          return True
  
      def remove_holiday_mode(self, temperature: float = Constants.FROST_PROTECTION_TEMP):
          """
          Remove holiday mode. Set start date to two days before and end date to yesterday
  
          :param temperature: default is :class:`vr900connector.model.Constants#FROST_PROTECTION_TEMP`
          :return: True if update occurred
          """
          self._connector.put(Urls.system_holiday_mode(), Payloads.holiday_mode(False,
                                                                                date.today() - timedelta(days=2),
                                                                                date.today() - timedelta(days=1),
                                                                                temperature))
          return True
  
      def request_hvac_update(self):
          """
          Request an hvac update. Please note the update is done asynchronously. Most of the time, it takes about 1 or 2
          minutes before you can see the new status in :class:`vr900connector.model.BoilerStatus`
          :return: True if the update request was accepted by the API
          """
          self._connector.put(Urls.hvac_update())
          return True
  
      def logout(self):
          """
          Get logged out from the API
          """
          self._connector.logout()
  
      def _round(self, number: float):
          """
          This function round a float to the nearest 0.5, as vaillant API only accepts 0.5 step
          :param number: the number to round
          :return: the rounded number
          """
          return round(number * 2) / 2
  