text: """Support for magicseaweed data from magicseaweed.com."""
  from datetime import timedelta
  import logging
  
  import magicseaweed
  import voluptuous as vol
  
  from homeassistant.components.sensor import PLATFORM_SCHEMA
  from homeassistant.const import (
      ATTR_ATTRIBUTION,
      CONF_API_KEY,
      CONF_MONITORED_CONDITIONS,
      CONF_NAME,
  )
  import homeassistant.helpers.config_validation as cv
  from homeassistant.helpers.entity import Entity
  from homeassistant.util import Throttle
  import homeassistant.util.dt as dt_util
  
  _LOGGER = logging.getLogger(__name__)
  
  CONF_HOURS = "hours"
  CONF_SPOT_ID = "spot_id"
  CONF_UNITS = "units"
  
  DEFAULT_UNIT = "us"
  DEFAULT_NAME = "MSW"
  DEFAULT_ATTRIBUTION = "Data provided by magicseaweed.com"
  
  ICON = "mdi:waves"
  
  HOURS = ["12AM", "3AM", "6AM", "9AM", "12PM", "3PM", "6PM", "9PM"]
  
  SENSOR_TYPES = {
      "max_breaking_swell": ["Max"],
      "min_breaking_swell": ["Min"],
      "swell_forecast": ["Forecast"],
  }
  
  UNITS = ["eu", "uk", "us"]
  
  PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
      {
          vol.Required(CONF_MONITORED_CONDITIONS): vol.All(
              cv.ensure_list, [vol.In(SENSOR_TYPES)]
          ),
          vol.Required(CONF_API_KEY): cv.string,
          vol.Required(CONF_SPOT_ID): vol.All(cv.ensure_list, [cv.string]),
          vol.Optional(CONF_HOURS, default=None): vol.All(
              cv.ensure_list, [vol.In(HOURS)]
          ),
          vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
          vol.Optional(CONF_UNITS): vol.In(UNITS),
      }
  )
  
  # Return cached results if last scan was less then this time ago.
  MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30)
  
  
  def setup_platform(hass, config, add_entities, discovery_info=None):
      """Set up the Magicseaweed sensor."""
      name = config.get(CONF_NAME)
      spot_id = config[CONF_SPOT_ID]
      api_key = config[CONF_API_KEY]
      hours = config.get(CONF_HOURS)
  
      if CONF_UNITS in config:
          units = config.get(CONF_UNITS)
      elif hass.config.units.is_metric:
          units = UNITS[0]
      else:
          units = UNITS[2]
  
      forecast_data = MagicSeaweedData(api_key=api_key, spot_id=spot_id, units=units)
      forecast_data.update()
  
      # If connection failed don't setup platform.
      if forecast_data.currently is None or forecast_data.hourly is None:
          return
  
      sensors = []
      for variable in config[CONF_MONITORED_CONDITIONS]:
          sensors.append(MagicSeaweedSensor(forecast_data, variable, name, units))
          if "forecast" not in variable and hours is not None:
              for hour in hours:
                  sensors.append(
                      MagicSeaweedSensor(forecast_data, variable, name, units, hour)
                  )
      add_entities(sensors, True)
  
  
  class MagicSeaweedSensor(Entity):
      """Implementation of a MagicSeaweed sensor."""
  
      def __init__(self, forecast_data, sensor_type, name, unit_system, hour=None):
          """Initialize the sensor."""
          self.client_name = name
          self.data = forecast_data
          self.hour = hour
          self.type = sensor_type
          self._attrs = {ATTR_ATTRIBUTION: DEFAULT_ATTRIBUTION}
          self._name = SENSOR_TYPES[sensor_type][0]
          self._icon = None
          self._state = None
          self._unit_system = unit_system
          self._unit_of_measurement = None
  
      @property
      def name(self):
          """Return the name of the sensor."""
          if self.hour is None and "forecast" in self.type:
              return f"{self.client_name} {self._name}"
          if self.hour is None:
              return f"Current {self.client_name} {self._name}"
          return f"{self.hour} {self.client_name} {self._name}"
  
      @property
      def state(self):
          """Return the state of the sensor."""
          return self._state
  
      @property
      def unit_system(self):
          """Return the unit system of this entity."""
          return self._unit_system
  
      @property
      def unit_of_measurement(self):
          """Return the unit of measurement of this entity, if any."""
          return self._unit_of_measurement
  
      @property
      def icon(self):
          """Return the entity weather icon, if any."""
          return ICON
  
      @property
      def extra_state_attributes(self):
          """Return the state attributes."""
          return self._attrs
  
      def update(self):
          """Get the latest data from Magicseaweed and updates the states."""
          self.data.update()
          if self.hour is None:
              forecast = self.data.currently
          else:
              forecast = self.data.hourly[self.hour]
  
          self._unit_of_measurement = forecast.swell_unit
          if self.type == "min_breaking_swell":
              self._state = forecast.swell_minBreakingHeight
          elif self.type == "max_breaking_swell":
              self._state = forecast.swell_maxBreakingHeight
          elif self.type == "swell_forecast":
              summary = f"{forecast.swell_minBreakingHeight} - {forecast.swell_maxBreakingHeight}"
              self._state = summary
              if self.hour is None:
                  for hour, data in self.data.hourly.items():
                      occurs = hour
                      hr_summary = f"{data.swell_minBreakingHeight} - {data.swell_maxBreakingHeight} {data.swell_unit}"
                      self._attrs[occurs] = hr_summary
  
          if self.type != "swell_forecast":
              self._attrs.update(forecast.attrs)
  
  
  class MagicSeaweedData:
      """Get the latest data from MagicSeaweed."""
  
      def __init__(self, api_key, spot_id, units):
          """Initialize the data object."""
          self._msw = magicseaweed.MSW_Forecast(api_key, spot_id, None, units)
          self.currently = None
          self.hourly = {}
  
          # Apply throttling to methods using configured interval
          self.update = Throttle(MIN_TIME_BETWEEN_UPDATES)(self._update)
  
      def _update(self):
          """Get the latest data from MagicSeaweed."""
          try:
              forecasts = self._msw.get_future()
              self.currently = forecasts.data[0]
              for forecast in forecasts.data[:8]:
                  hour = dt_util.utc_from_timestamp(forecast.localTimestamp).strftime(
                      "%-I%p"
                  )
                  self.hourly[hour] = forecast
          except ConnectionError:
              _LOGGER.error("Unable to retrieve data from Magicseaweed")
  