text: """Helper to handle a set of topics to subscribe to."""
  import logging
  from typing import Any, Callable, Dict, Optional
  
  import attr
  
  from openpeerpower.components import mqtt
  from openpeerpower.helpers.typing import OpenPeerPowerType
  from openpeerpower.loader import bind_opp
  
  from .const import DEFAULT_QOS
  from .models import MessageCallbackType
  
  _LOGGER = logging.getLogger(__name__)
  
  
  @attr.s(slots=True)
  class EntitySubscription:
      """Class to hold data about an active entity topic subscription."""
  
      topic = attr.ib(type=str)
      message_callback = attr.ib(type=MessageCallbackType)
      unsubscribe_callback = attr.ib(type=Optional[Callable[[], None]])
      qos = attr.ib(type=int, default=0)
      encoding = attr.ib(type=str, default="utf-8")
  
      async def resubscribe_if_necessary(self, opp, other):
          """Re-subscribe to the new topic if necessary."""
          if not self._should_resubscribe(other):
              return
  
          if other is not None and other.unsubscribe_callback is not None:
              other.unsubscribe_callback()
  
          if self.topic is None:
              # We were asked to remove the subscription or not to create it
              return
  
          self.unsubscribe_callback = await mqtt.async_subscribe(
              opp, self.topic, self.message_callback, self.qos, self.encoding
          )
  
      def _should_resubscribe(self, other):
          """Check if we should re-subscribe to the topic using the old state."""
          if other is None:
              return True
  
          return (self.topic, self.qos, self.encoding) != (
              other.topic,
              other.qos,
              other.encoding,
          )
  
  
  @bind_opp
  async def async_subscribe_topics(
      opp: OpenPeerPowerType,
      new_state: Optional[Dict[str, EntitySubscription]],
      topics: Dict[str, Any],
  ):
      """(Re)Subscribe to a set of MQTT topics.
  
      State is kept in sub_state and a dictionary mapping from the subscription
      key to the subscription state.
  
      Please note that the sub state must not be shared between multiple
      sets of topics. Every call to async_subscribe_topics must always
      contain _all_ the topics the subscription state should manage.
      """
      current_subscriptions = new_state if new_state is not None else {}
      new_state = {}
      for key, value in topics.items():
          # Extract the new requested subscription
          requested = EntitySubscription(
              topic=value.get("topic", None),
              message_callback=value.get("msg_callback", None),
              unsubscribe_callback=None,
              qos=value.get("qos", DEFAULT_QOS),
              encoding=value.get("encoding", "utf-8"),
          )
          # Get the current subscription state
          current = current_subscriptions.pop(key, None)
          await requested.resubscribe_if_necessary(opp, current)
          new_state[key] = requested
  
      # Go through all remaining subscriptions and unsubscribe them
      for remaining in current_subscriptions.values():
          if remaining.unsubscribe_callback is not None:
              remaining.unsubscribe_callback()
  
      return new_state
  
  
  @bind_opp
  async def async_unsubscribe_topics(opp: OpenPeerPowerType, sub_state: dict):
      """Unsubscribe from all MQTT topics managed by async_subscribe_topics."""
      return await async_subscribe_topics(opp, sub_state, {})
  