text: """
  Tellstick Component.
  
  For more details about this component, please refer to the documentation at
  https://home-assistant.io/components/tellstick/
  """
  import logging
  import threading
  
  import voluptuous as vol
  
  from homeassistant.helpers import discovery
  from homeassistant.const import EVENT_HOMEASSISTANT_STOP
  from homeassistant.helpers.entity import Entity
  
  DOMAIN = 'tellstick'
  
  REQUIREMENTS = ['tellcore-py==1.1.2']
  
  _LOGGER = logging.getLogger(__name__)
  
  ATTR_SIGNAL_REPETITIONS = 'signal_repetitions'
  DEFAULT_SIGNAL_REPETITIONS = 1
  
  ATTR_DISCOVER_DEVICES = 'devices'
  ATTR_DISCOVER_CONFIG = 'config'
  
  # Use a global tellstick domain lock to handle Tellcore errors then calling
  # to concurrently
  TELLSTICK_LOCK = threading.Lock()
  
  # Keep a reference the the callback registry. Used from entities that register
  # callback listeners
  TELLCORE_REGISTRY = None
  
  CONFIG_SCHEMA = vol.Schema({
      DOMAIN: vol.Schema({
          vol.Optional(ATTR_SIGNAL_REPETITIONS,
                       default=DEFAULT_SIGNAL_REPETITIONS): vol.Coerce(int),
      }),
  }, extra=vol.ALLOW_EXTRA)
  
  
  def _discover(hass, config, found_devices, component_name):
      """Setup and send the discovery event."""
      if not len(found_devices):
          return
  
      _LOGGER.info(
          "Discovered %d new %s devices", len(found_devices), component_name)
  
      signal_repetitions = config[DOMAIN].get(ATTR_SIGNAL_REPETITIONS)
  
      discovery.load_platform(hass, component_name, DOMAIN, {
          ATTR_DISCOVER_DEVICES: found_devices,
          ATTR_DISCOVER_CONFIG: signal_repetitions}, config)
  
  
  def setup(hass, config):
      """Setup the Tellstick component."""
      # pylint: disable=global-statement, import-error
      global TELLCORE_REGISTRY
  
      import tellcore.telldus as telldus
      import tellcore.constants as tellcore_constants
      from tellcore.library import DirectCallbackDispatcher
  
      core = telldus.TelldusCore(callback_dispatcher=DirectCallbackDispatcher())
  
      TELLCORE_REGISTRY = TellstickRegistry(hass, core)
  
      devices = core.devices()
  
      # Register devices
      TELLCORE_REGISTRY.register_devices(devices)
  
      # Discover the switches
      _discover(hass, config, [switch.id for switch in
                               devices if not switch.methods(
                                   tellcore_constants.TELLSTICK_DIM)],
                'switch')
  
      # Discover the lights
      _discover(hass, config, [light.id for light in
                               devices if light.methods(
                                   tellcore_constants.TELLSTICK_DIM)],
                'light')
  
      return True
  
  
  class TellstickRegistry(object):
      """Handle everything around Tellstick callbacks.
  
      Keeps a map device ids to home-assistant entities.
      Also responsible for registering / cleanup of callbacks.
  
      All device specific logic should be elsewhere (Entities).
      """
  
      def __init__(self, hass, tellcore_lib):
          """Initialize the Tellstick mappings and callbacks."""
          self._core_lib = tellcore_lib
          # used when map callback device id to ha entities.
          self._id_to_entity_map = {}
          self._id_to_device_map = {}
          self._setup_device_callback(hass, tellcore_lib)
  
      def _device_callback(self, tellstick_id, method, data, cid):
          """Handle the actual callback from Tellcore."""
          entity = self._id_to_entity_map.get(tellstick_id, None)
          if entity is not None:
              entity.set_tellstick_state(method, data)
              entity.update_ha_state()
  
      def _setup_device_callback(self, hass, tellcore_lib):
          """Register the callback handler."""
          callback_id = tellcore_lib.register_device_event(self._device_callback)
  
          def clean_up_callback(event):
              """Unregister the callback bindings."""
              if callback_id is not None:
                  tellcore_lib.unregister_callback(callback_id)
  
          hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, clean_up_callback)
  
      def register_entity(self, tellcore_id, entity):
          """Register a new entity to receive callback updates."""
          self._id_to_entity_map[tellcore_id] = entity
  
      def register_devices(self, devices):
          """Register a list of devices."""
          self._id_to_device_map.update(
              {device.id: device for device in devices})
  
      def get_device(self, tellcore_id):
          """Return a device by tellcore_id."""
          return self._id_to_device_map.get(tellcore_id, None)
  
  
  class TellstickDevice(Entity):
      """Representation of a Tellstick device.
  
      Contains the common logic for all Tellstick devices.
      """
  
      def __init__(self, tellstick_device, signal_repetitions):
          """Initalize the Tellstick device."""
          self.signal_repetitions = signal_repetitions
          self._state = None
          self.tellstick_device = tellstick_device
          # Add to id to entity mapping
          TELLCORE_REGISTRY.register_entity(tellstick_device.id, self)
          # Query tellcore for the current state
          self.update()
  
      @property
      def should_poll(self):
          """Tell Home Assistant not to poll this entity."""
          return False
  
      @property
      def assumed_state(self):
          """Tellstick devices are always assumed state."""
          return True
  
      @property
      def name(self):
          """Return the name of the switch if any."""
          return self.tellstick_device.name
  
      def set_tellstick_state(self, last_command_sent, last_data_sent):
          """Set the private switch state."""
          raise NotImplementedError(
              "set_tellstick_state needs to be implemented.")
  
      def _send_tellstick_command(self, command, data):
          """Do the actual call to the tellstick device."""
          raise NotImplementedError(
              "_call_tellstick needs to be implemented.")
  
      def call_tellstick(self, command, data=None):
          """Send a command to the device."""
          from tellcore.library import TelldusError
          with TELLSTICK_LOCK:
              try:
                  for _ in range(self.signal_repetitions):
                      self._send_tellstick_command(command, data)
                  # Update the internal state
                  self.set_tellstick_state(command, data)
                  self.update_ha_state()
              except TelldusError:
                  _LOGGER.error(TelldusError)
  
      def update(self):
          """Poll the current state of the device."""
          import tellcore.constants as tellcore_constants
          from tellcore.library import TelldusError
          try:
              last_command = self.tellstick_device.last_sent_command(
                  tellcore_constants.TELLSTICK_TURNON |
                  tellcore_constants.TELLSTICK_TURNOFF |
                  tellcore_constants.TELLSTICK_DIM
              )
              last_value = self.tellstick_device.last_sent_value()
              self.set_tellstick_state(last_command, last_value)
          except TelldusError:
              _LOGGER.error(TelldusError)
  