diff --git a/.coveragerc b/.coveragerc
index 5bd097e8abbc37fff471d3c2140e461660c2c69a..ea0530aa8f7aced3c3c90c1111a11e96560a173b 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -214,6 +214,7 @@ omit =
     homeassistant/components/media_player/emby.py
     homeassistant/components/media_player/firetv.py
     homeassistant/components/media_player/gpmdp.py
+    homeassistant/components/media_player/hdmi_cec.py
     homeassistant/components/media_player/itunes.py
     homeassistant/components/media_player/kodi.py
     homeassistant/components/media_player/lg_netcast.py
@@ -356,6 +357,7 @@ omit =
     homeassistant/components/switch/digitalloggers.py
     homeassistant/components/switch/dlink.py
     homeassistant/components/switch/edimax.py
+    homeassistant/components/switch/hdmi_cec.py
     homeassistant/components/switch/hikvisioncam.py
     homeassistant/components/switch/hook.py
     homeassistant/components/switch/kankun.py
diff --git a/homeassistant/components/hdmi_cec.py b/homeassistant/components/hdmi_cec.py
index 4fab7f84bd3fae03e92c6d2d7294815db56d31e3..44b205993b6ed72f647c6b2cf7b2fbf9ea59b8d9 100644
--- a/homeassistant/components/hdmi_cec.py
+++ b/homeassistant/components/hdmi_cec.py
@@ -1,27 +1,110 @@
 """
-CEC component.
+HDMI CEC component.
 
 For more details about this component, please refer to the documentation at
 https://home-assistant.io/components/hdmi_cec/
 """
 import logging
+import multiprocessing
+import os
+from collections import defaultdict
+from functools import reduce
 
 import voluptuous as vol
 
-from homeassistant.const import (EVENT_HOMEASSISTANT_START, CONF_DEVICES)
 import homeassistant.helpers.config_validation as cv
+from homeassistant import core
+from homeassistant.components import discovery
+from homeassistant.components.media_player import DOMAIN as MEDIA_PLAYER
+from homeassistant.components.switch import DOMAIN as SWITCH
+from homeassistant.config import load_yaml_config_file
+from homeassistant.const import (EVENT_HOMEASSISTANT_START, STATE_UNKNOWN,
+                                 EVENT_HOMEASSISTANT_STOP, STATE_ON,
+                                 STATE_OFF, CONF_DEVICES, CONF_PLATFORM,
+                                 CONF_CUSTOMIZE, STATE_PLAYING, STATE_IDLE,
+                                 STATE_PAUSED, CONF_HOST)
+from homeassistant.core import HomeAssistant, callback
+from homeassistant.helpers.entity import Entity
+
+REQUIREMENTS = ['pyCEC==0.4.6']
+
+DOMAIN = 'hdmi_cec'
 
-_CEC = None
 _LOGGER = logging.getLogger(__name__)
 
+ICON_UNKNOWN = 'mdi:help'
+ICON_AUDIO = 'mdi:speaker'
+ICON_PLAYER = 'mdi:play'
+ICON_TUNER = 'mdi:nest-thermostat'
+ICON_RECORDER = 'mdi:microphone'
+ICON_TV = 'mdi:television'
+ICONS_BY_TYPE = {
+    0: ICON_TV,
+    1: ICON_RECORDER,
+    3: ICON_TUNER,
+    4: ICON_PLAYER,
+    5: ICON_AUDIO
+}
+
+CEC_DEVICES = defaultdict(list)
+
+CMD_UP = 'up'
+CMD_DOWN = 'down'
+CMD_MUTE = 'mute'
+CMD_UNMUTE = 'unmute'
+CMD_MUTE_TOGGLE = 'toggle mute'
+CMD_PRESS = 'press'
+CMD_RELEASE = 'release'
+
+EVENT_CEC_COMMAND_RECEIVED = 'cec_command_received'
+EVENT_CEC_KEYPRESS_RECEIVED = 'cec_keypress_received'
+
+ATTR_PHYSICAL_ADDRESS = 'physical_address'
+ATTR_TYPE_ID = 'type_id'
+ATTR_VENDOR_NAME = 'vendor_name'
+ATTR_VENDOR_ID = 'vendor_id'
 ATTR_DEVICE = 'device'
+ATTR_COMMAND = 'command'
+ATTR_TYPE = 'type'
+ATTR_KEY = 'key'
+ATTR_DUR = 'dur'
+ATTR_SRC = 'src'
+ATTR_DST = 'dst'
+ATTR_CMD = 'cmd'
+ATTR_ATT = 'att'
+ATTR_RAW = 'raw'
+ATTR_DIR = 'dir'
+ATTR_ABT = 'abt'
+ATTR_NEW = 'new'
 
-DOMAIN = 'hdmi_cec'
+_VOL_HEX = vol.Any(vol.Coerce(int), lambda x: int(x, 16))
 
-MAX_DEPTH = 4
+SERVICE_SEND_COMMAND = 'send_command'
+SERVICE_SEND_COMMAND_SCHEMA = vol.Schema({
+    vol.Optional(ATTR_CMD): _VOL_HEX,
+    vol.Optional(ATTR_SRC): _VOL_HEX,
+    vol.Optional(ATTR_DST): _VOL_HEX,
+    vol.Optional(ATTR_ATT): _VOL_HEX,
+    vol.Optional(ATTR_RAW): vol.Coerce(str)
+}, extra=vol.PREVENT_EXTRA)
+
+SERVICE_VOLUME = 'volume'
+SERVICE_VOLUME_SCHEMA = vol.Schema({
+    vol.Optional(CMD_UP): vol.Any(CMD_PRESS, CMD_RELEASE, vol.Coerce(int)),
+    vol.Optional(CMD_DOWN): vol.Any(CMD_PRESS, CMD_RELEASE, vol.Coerce(int)),
+    vol.Optional(CMD_MUTE): None,
+    vol.Optional(CMD_UNMUTE): None,
+    vol.Optional(CMD_MUTE_TOGGLE): None
+}, extra=vol.PREVENT_EXTRA)
+
+SERVICE_UPDATE_DEVICES = 'update'
+SERVICE_UPDATE_DEVICES_SCHEMA = vol.Schema({
+    DOMAIN: vol.Schema({})
+}, extra=vol.PREVENT_EXTRA)
 
-SERVICE_POWER_ON = 'power_on'
 SERVICE_SELECT_DEVICE = 'select_device'
+
+SERVICE_POWER_ON = 'power_on'
 SERVICE_STANDBY = 'standby'
 
 # pylint: disable=unnecessary-lambda
@@ -30,92 +113,304 @@ DEVICE_SCHEMA = vol.Schema({
                                       cv.string)
 })
 
+CUSTOMIZE_SCHEMA = vol.Schema({
+    vol.Optional(CONF_PLATFORM, default=MEDIA_PLAYER): vol.Any(MEDIA_PLAYER,
+                                                               SWITCH)
+})
+
 CONFIG_SCHEMA = vol.Schema({
     DOMAIN: vol.Schema({
-        vol.Required(CONF_DEVICES): DEVICE_SCHEMA
+        vol.Optional(CONF_DEVICES): vol.Any(DEVICE_SCHEMA,
+                                            vol.Schema({
+                                                vol.All(cv.string): vol.Any(
+                                                    cv.string)
+                                            })),
+        vol.Optional(CONF_PLATFORM): vol.Any(SWITCH, MEDIA_PLAYER),
+        vol.Optional(CONF_HOST): cv.string,
     })
 }, extra=vol.ALLOW_EXTRA)
 
 
+def pad_physical_address(addr):
+    """Right-pad a physical address."""
+    return addr + [0] * (4 - len(addr))
+
+
 def parse_mapping(mapping, parents=None):
     """Parse configuration device mapping."""
     if parents is None:
         parents = []
     for addr, val in mapping.items():
-        cur = parents + [str(addr)]
-        if isinstance(val, dict):
-            yield from parse_mapping(val, cur)
-        elif isinstance(val, str):
-            yield (val, cur)
-
-
-def pad_physical_address(addr):
-    """Right-pad a physical address."""
-    return addr + ['0'] * (MAX_DEPTH - len(addr))
+        if isinstance(addr, (str,)) and isinstance(val, (str,)):
+            from pycec.network import PhysicalAddress
+            yield (addr, PhysicalAddress(val))
+        else:
+            cur = parents + [addr]
+            if isinstance(val, dict):
+                yield from parse_mapping(val, cur)
+            elif isinstance(val, str):
+                yield (val, pad_physical_address(cur))
 
 
-def setup(hass, config):
+def setup(hass: HomeAssistant, base_config):
     """Setup CEC capability."""
-    global _CEC
-
-    try:
-        import cec
-    except ImportError:
-        _LOGGER.error("libcec must be installed")
-        return False
+    from pycec.network import HDMINetwork
+    from pycec.commands import CecCommand, KeyReleaseCommand, KeyPressCommand
+    from pycec.const import KEY_VOLUME_UP, KEY_VOLUME_DOWN, KEY_MUTE, \
+        ADDR_AUDIOSYSTEM, ADDR_BROADCAST, ADDR_UNREGISTERED
+    from pycec.cec import CecAdapter
+    from pycec.tcp import TcpAdapter
 
     # Parse configuration into a dict of device name to physical address
     # represented as a list of four elements.
-    flat = {}
-    for pair in parse_mapping(config[DOMAIN].get(CONF_DEVICES, {})):
-        flat[pair[0]] = pad_physical_address(pair[1])
+    device_aliases = {}
+    devices = base_config[DOMAIN].get(CONF_DEVICES, {})
+    _LOGGER.debug("Parsing config %s", devices)
+    device_aliases.update(parse_mapping(devices))
+    _LOGGER.debug("Parsed devices: %s", device_aliases)
 
-    # Configure libcec.
-    cfg = cec.libcec_configuration()
-    cfg.strDeviceName = 'HASS'
-    cfg.bActivateSource = 0
-    cfg.bMonitorOnly = 1
-    cfg.clientVersion = cec.LIBCEC_VERSION_CURRENT
+    platform = base_config[DOMAIN].get(CONF_PLATFORM, SWITCH)
 
-    # Setup CEC adapter.
-    _CEC = cec.ICECAdapter.Create(cfg)
+    loop = (
+        # Create own thread if more than 1 CPU
+        hass.loop if multiprocessing.cpu_count() < 2 else None)
+    host = base_config[DOMAIN].get(CONF_HOST, None)
+    if host:
+        adapter = TcpAdapter(host, name="HASS", activate_source=False)
+    else:
+        adapter = CecAdapter(name="HASS", activate_source=False)
+    hdmi_network = HDMINetwork(adapter, loop=loop)
 
-    def _power_on(call):
-        """Power on all devices."""
-        _CEC.PowerOnDevices()
+    def _volume(call):
+        """Increase/decrease volume and mute/unmute system."""
+        for cmd, att in call.data.items():
+            if cmd == CMD_UP:
+                _process_volume(KEY_VOLUME_UP, att)
+            elif cmd == CMD_DOWN:
+                _process_volume(KEY_VOLUME_DOWN, att)
+            elif cmd == CMD_MUTE:
+                hdmi_network.send_command(
+                    KeyPressCommand(KEY_MUTE, dst=ADDR_AUDIOSYSTEM))
+                hdmi_network.send_command(
+                    KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM))
+                _LOGGER.info("Audio muted")
+            else:
+                _LOGGER.warning("Unknown command %s", cmd)
+
+    def _process_volume(cmd, att):
+        if isinstance(att, (str,)):
+            att = att.strip()
+        if att == CMD_PRESS:
+            hdmi_network.send_command(
+                KeyPressCommand(cmd, dst=ADDR_AUDIOSYSTEM))
+        elif att == CMD_RELEASE:
+            hdmi_network.send_command(KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM))
+        else:
+            att = 1 if att == "" else int(att)
+            for _ in range(1, att):
+                hdmi_network.send_command(
+                    KeyPressCommand(cmd, dst=ADDR_AUDIOSYSTEM))
+                hdmi_network.send_command(
+                    KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM))
+
+    def _tx(call):
+        """Send CEC command."""
+        data = call.data
+        if ATTR_RAW in data:
+            command = CecCommand(data[ATTR_RAW])
+        else:
+            if ATTR_SRC in data:
+                src = data[ATTR_SRC]
+            else:
+                src = ADDR_UNREGISTERED
+            if ATTR_DST in data:
+                dst = data[ATTR_DST]
+            else:
+                dst = ADDR_BROADCAST
+            if ATTR_CMD in data:
+                cmd = data[ATTR_CMD]
+            else:
+                _LOGGER.error("Attribute 'cmd' is missing")
+                return False
+            if ATTR_ATT in data:
+                if isinstance(data[ATTR_ATT], (list,)):
+                    att = data[ATTR_ATT]
+                else:
+                    att = reduce(lambda x, y: "%s:%x" % (x, y), data[ATTR_ATT])
+            else:
+                att = ""
+            command = CecCommand(cmd, dst, src, att)
+        hdmi_network.send_command(command)
 
+    @callback
     def _standby(call):
-        """Standby all devices."""
-        _CEC.StandbyDevices()
+        hdmi_network.standby()
+
+    @callback
+    def _power_on(call):
+        hdmi_network.power_on()
 
     def _select_device(call):
         """Select the active device."""
-        path = flat.get(call.data[ATTR_DEVICE])
-        if not path:
+        from pycec.network import PhysicalAddress
+
+        addr = call.data[ATTR_DEVICE]
+        if not addr:
             _LOGGER.error("Device not found: %s", call.data[ATTR_DEVICE])
-        cmds = []
-        for i in range(1, MAX_DEPTH - 1):
-            addr = pad_physical_address(path[:i])
-            cmds.append('1f:82:{}{}:{}{}'.format(*addr))
-            cmds.append('1f:86:{}{}:{}{}'.format(*addr))
-        for cmd in cmds:
-            _CEC.Transmit(_CEC.CommandFromString(cmd))
-        _LOGGER.info("Selected %s", call.data[ATTR_DEVICE])
+            return
+        if addr in device_aliases:
+            addr = device_aliases[addr]
+        else:
+            entity = hass.states.get(addr)
+            _LOGGER.debug("Selecting entity %s", entity)
+            if entity is not None:
+                addr = entity.attributes['physical_address']
+                _LOGGER.debug("Address acquired: %s", addr)
+                if addr is None:
+                    _LOGGER.error("Device %s has not physical address.",
+                                  call.data[ATTR_DEVICE])
+                    return
+        if not isinstance(addr, (PhysicalAddress,)):
+            addr = PhysicalAddress(addr)
+        hdmi_network.active_source(addr)
+        _LOGGER.info("Selected %s (%s)", call.data[ATTR_DEVICE], addr)
+
+    def _update(call):
+        """
+        Callback called when device update is needed.
+
+        - called by service, requests CEC network to update data.
+        """
+        hdmi_network.scan()
+
+    @callback
+    def _new_device(device):
+        """Called when new device is detected by HDMI network."""
+        key = DOMAIN + '.' + device.name
+        hass.data[key] = device
+        discovery.load_platform(hass, base_config.get(core.DOMAIN).get(
+            CONF_CUSTOMIZE, {}).get(key, {}).get(CONF_PLATFORM, platform),
+                                DOMAIN, discovered={ATTR_NEW: [key]},
+                                hass_config=base_config)
+
+    def _shutdown(call):
+        hdmi_network.stop()
 
     def _start_cec(event):
-        """Open CEC adapter."""
-        adapters = _CEC.DetectAdapters()
-        if len(adapters) == 0:
-            _LOGGER.error("No CEC adapter found")
-            return
+        """Register services and start HDMI network to watch for devices."""
+        descriptions = load_yaml_config_file(
+            os.path.join(os.path.dirname(__file__), 'services.yaml'))[DOMAIN]
+        hass.services.register(DOMAIN, SERVICE_SEND_COMMAND, _tx,
+                               descriptions[SERVICE_SEND_COMMAND],
+                               SERVICE_SEND_COMMAND_SCHEMA)
+        hass.services.register(DOMAIN, SERVICE_VOLUME, _volume,
+                               descriptions[SERVICE_VOLUME],
+                               SERVICE_VOLUME_SCHEMA)
+        hass.services.register(DOMAIN, SERVICE_UPDATE_DEVICES, _update,
+                               descriptions[SERVICE_UPDATE_DEVICES],
+                               SERVICE_UPDATE_DEVICES_SCHEMA)
+        hass.services.register(DOMAIN, SERVICE_POWER_ON, _power_on)
+        hass.services.register(DOMAIN, SERVICE_STANDBY, _standby)
+        hass.services.register(DOMAIN, SERVICE_SELECT_DEVICE, _select_device)
 
-        if _CEC.Open(adapters[0].strComName):
-            hass.services.register(DOMAIN, SERVICE_POWER_ON, _power_on)
-            hass.services.register(DOMAIN, SERVICE_STANDBY, _standby)
-            hass.services.register(DOMAIN, SERVICE_SELECT_DEVICE,
-                                   _select_device)
-        else:
-            _LOGGER.error("Failed to open adapter")
+        hdmi_network.set_new_device_callback(_new_device)
+        hdmi_network.start()
 
     hass.bus.listen_once(EVENT_HOMEASSISTANT_START, _start_cec)
+    hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, _shutdown)
     return True
+
+
+class CecDevice(Entity):
+    """Representation of a HDMI CEC device entity."""
+
+    def __init__(self, hass: HomeAssistant, device, logical):
+        """Initialize the device."""
+        self._device = device
+        self.hass = hass
+        self._icon = None
+        self._state = STATE_UNKNOWN
+        self._logical_address = logical
+        self.entity_id = "%s.%d" % (DOMAIN, self._logical_address)
+        device.set_update_callback(self._update)
+
+    def update(self):
+        """Update device status."""
+        self._update()
+
+    def _update(self, device=None):
+        """Update device status."""
+        if device:
+            from pycec.const import STATUS_PLAY, STATUS_STOP, STATUS_STILL, \
+                POWER_OFF, POWER_ON
+            if device.power_status == POWER_OFF:
+                self._state = STATE_OFF
+            elif device.status == STATUS_PLAY:
+                self._state = STATE_PLAYING
+            elif device.status == STATUS_STOP:
+                self._state = STATE_IDLE
+            elif device.status == STATUS_STILL:
+                self._state = STATE_PAUSED
+            elif device.power_status == POWER_ON:
+                self._state = STATE_ON
+            else:
+                _LOGGER.warning("Unknown state: %d", device.power_status)
+        self.schedule_update_ha_state()
+
+    @property
+    def name(self):
+        """Return the name of the device."""
+        return (
+            "%s %s" % (self.vendor_name, self._device.osd_name)
+            if (self._device.osd_name is not None and
+                self.vendor_name is not None and self.vendor_name != 'Unknown')
+            else "%s %d" % (self._device.type_name, self._logical_address)
+            if self._device.osd_name is None
+            else "%s %d (%s)" % (self._device.type_name, self._logical_address,
+                                 self._device.osd_name))
+
+    @property
+    def vendor_id(self):
+        """ID of device's vendor."""
+        return self._device.vendor_id
+
+    @property
+    def vendor_name(self):
+        """Name of device's vendor."""
+        return self._device.vendor
+
+    @property
+    def physical_address(self):
+        """Physical address of device in HDMI network."""
+        return str(self._device.physical_address)
+
+    @property
+    def type(self):
+        """String representation of device's type."""
+        return self._device.type_name
+
+    @property
+    def type_id(self):
+        """Type ID of device."""
+        return self._device.type
+
+    @property
+    def icon(self):
+        """Icon for device by its type."""
+        return (self._icon if self._icon is not None else
+                ICONS_BY_TYPE.get(self._device.type)
+                if self._device.type in ICONS_BY_TYPE else ICON_UNKNOWN)
+
+    @property
+    def device_state_attributes(self):
+        """Return the state attributes."""
+        state_attr = {}
+        if self.vendor_id is not None:
+            state_attr[ATTR_VENDOR_ID] = self.vendor_id
+            state_attr[ATTR_VENDOR_NAME] = self.vendor_name
+        if self.type_id is not None:
+            state_attr[ATTR_TYPE_ID] = self.type_id
+            state_attr[ATTR_TYPE] = self.type
+        if self.physical_address is not None:
+            state_attr[ATTR_PHYSICAL_ADDRESS] = self.physical_address
+        return state_attr
diff --git a/homeassistant/components/media_player/hdmi_cec.py b/homeassistant/components/media_player/hdmi_cec.py
new file mode 100644
index 0000000000000000000000000000000000000000..4998072018ed1c794d3a1a7190bf66db6b4033ed
--- /dev/null
+++ b/homeassistant/components/media_player/hdmi_cec.py
@@ -0,0 +1,175 @@
+"""
+Support for HDMI CEC devices as media players.
+
+For more details about this component, please refer to the documentation at
+https://home-assistant.io/components/hdmi_cec/
+"""
+import logging
+
+from homeassistant.components.hdmi_cec import ATTR_NEW, CecDevice
+from homeassistant.components.media_player import MediaPlayerDevice, DOMAIN, \
+    SUPPORT_TURN_ON, SUPPORT_TURN_OFF, SUPPORT_PLAY_MEDIA, SUPPORT_PAUSE, \
+    SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK, SUPPORT_STOP, \
+    SUPPORT_VOLUME_STEP, SUPPORT_VOLUME_MUTE
+from homeassistant.const import STATE_ON, STATE_OFF, STATE_PLAYING, \
+    STATE_IDLE, STATE_PAUSED
+from homeassistant.core import HomeAssistant
+
+DEPENDENCIES = ['hdmi_cec']
+
+_LOGGER = logging.getLogger(__name__)
+
+ENTITY_ID_FORMAT = DOMAIN + '.{}'
+
+
+def setup_platform(hass, config, add_devices, discovery_info=None):
+    """Find and return HDMI devices as +switches."""
+    if ATTR_NEW in discovery_info:
+        _LOGGER.info("Setting up HDMI devices %s", discovery_info[ATTR_NEW])
+        add_devices(CecPlayerDevice(hass, hass.data.get(device),
+                                    hass.data.get(device).logical_address) for
+                    device in discovery_info[ATTR_NEW])
+
+
+class CecPlayerDevice(CecDevice, MediaPlayerDevice):
+    """Representation of a HDMI device as a Media palyer."""
+
+    def __init__(self, hass: HomeAssistant, device, logical):
+        """Initialize the HDMI device."""
+        CecDevice.__init__(self, hass, device, logical)
+        self.entity_id = "%s.%s_%s" % (
+            DOMAIN, 'hdmi', hex(self._logical_address)[2:])
+        self.update()
+
+    def send_keypress(self, key):
+        """Send keypress to CEC adapter."""
+        from pycec.commands import KeyPressCommand, KeyReleaseCommand
+        _LOGGER.debug("Sending keypress %s to device %s", hex(key),
+                      hex(self._logical_address))
+        self._device.send_command(
+            KeyPressCommand(key, dst=self._logical_address))
+        self._device.send_command(
+            KeyReleaseCommand(dst=self._logical_address))
+
+    def send_playback(self, key):
+        """Send playback status to CEC adapter."""
+        from pycec.commands import CecCommand
+        self._device.async_send_command(
+            CecCommand(key, dst=self._logical_address))
+
+    def mute_volume(self, mute):
+        """Mute volume."""
+        from pycec.const import KEY_MUTE
+        self.send_keypress(KEY_MUTE)
+
+    def media_previous_track(self):
+        """Go to previous track."""
+        from pycec.const import KEY_BACKWARD
+        self.send_keypress(KEY_BACKWARD)
+
+    def turn_on(self):
+        """Turn device on."""
+        self._device.turn_on()
+        self._state = STATE_ON
+
+    def clear_playlist(self):
+        """Clear players playlist."""
+        raise NotImplementedError()
+
+    def turn_off(self):
+        """Turn device off."""
+        self._device.turn_off()
+        self._state = STATE_OFF
+
+    def media_stop(self):
+        """Stop playback."""
+        from pycec.const import KEY_STOP
+        self.send_keypress(KEY_STOP)
+        self._state = STATE_IDLE
+
+    def play_media(self, media_type, media_id):
+        """Not supported."""
+        raise NotImplementedError()
+
+    def media_next_track(self):
+        """Skip to next track."""
+        from pycec.const import KEY_FORWARD
+        self.send_keypress(KEY_FORWARD)
+
+    def media_seek(self, position):
+        """Not supported."""
+        raise NotImplementedError()
+
+    def set_volume_level(self, volume):
+        """Set volume level, range 0..1."""
+        raise NotImplementedError()
+
+    def media_pause(self):
+        """Pause playback."""
+        from pycec.const import KEY_PAUSE
+        self.send_keypress(KEY_PAUSE)
+        self._state = STATE_PAUSED
+
+    def select_source(self, source):
+        """Not supported."""
+        raise NotImplementedError()
+
+    def media_play(self):
+        """Start playback."""
+        from pycec.const import KEY_PLAY
+        self.send_keypress(KEY_PLAY)
+        self._state = STATE_PLAYING
+
+    def volume_up(self):
+        """Increase volume."""
+        from pycec.const import KEY_VOLUME_UP
+        _LOGGER.debug("%s: volume up", self._logical_address)
+        self.send_keypress(KEY_VOLUME_UP)
+
+    def volume_down(self):
+        """Decrease volume."""
+        from pycec.const import KEY_VOLUME_DOWN
+        _LOGGER.debug("%s: volume down", self._logical_address)
+        self.send_keypress(KEY_VOLUME_DOWN)
+
+    @property
+    def state(self) -> str:
+        """Cached state of device."""
+        return self._state
+
+    def _update(self, device=None):
+        """Update device status."""
+        if device:
+            from pycec.const import STATUS_PLAY, STATUS_STOP, STATUS_STILL, \
+                POWER_OFF, POWER_ON
+            if device.power_status == POWER_OFF:
+                self._state = STATE_OFF
+            elif not self.support_pause:
+                if device.power_status == POWER_ON:
+                    self._state = STATE_ON
+            elif device.status == STATUS_PLAY:
+                self._state = STATE_PLAYING
+            elif device.status == STATUS_STOP:
+                self._state = STATE_IDLE
+            elif device.status == STATUS_STILL:
+                self._state = STATE_PAUSED
+            else:
+                _LOGGER.warning("Unknown state: %s", device.status)
+        self.schedule_update_ha_state()
+
+    @property
+    def supported_media_commands(self):
+        """Flag media commands that are supported."""
+        from pycec.const import TYPE_RECORDER, TYPE_PLAYBACK, TYPE_TUNER, \
+            TYPE_AUDIO
+        if self.type_id == TYPE_RECORDER or self.type == TYPE_PLAYBACK:
+            return (SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PLAY_MEDIA |
+                    SUPPORT_PAUSE | SUPPORT_STOP | SUPPORT_PREVIOUS_TRACK |
+                    SUPPORT_NEXT_TRACK)
+        if self.type == TYPE_TUNER:
+            return (SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PLAY_MEDIA |
+                    SUPPORT_PAUSE | SUPPORT_STOP)
+        if self.type_id == TYPE_AUDIO:
+            return (SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_VOLUME_STEP |
+                    SUPPORT_VOLUME_MUTE)
+        return SUPPORT_TURN_ON | SUPPORT_TURN_OFF
diff --git a/homeassistant/components/services.yaml b/homeassistant/components/services.yaml
index 54c0e18a3ee200d18b4ea23792ce002568ae45fa..53f82d5c059310bca14f8e58115f83aca43e3fa7 100644
--- a/homeassistant/components/services.yaml
+++ b/homeassistant/components/services.yaml
@@ -153,3 +153,58 @@ verisure:
       device_serial:
         description: The serial number of the smartcam you want to capture an image from.
         example: '2DEU AT5Z'
+
+hdmi_cec:
+  send_command:
+    description: Sends CEC command into HDMI CEC capable adapter.
+
+    fields:
+      raw:
+        description: 'Raw CEC command in format "00:00:00:00" where first two digits are source and destination, second byte is command and optional other bytes are command parameters. If raw command specified, other params are ignored.'
+        example: '"10:36"'
+
+      src:
+        desctiption: 'Source of command. Could be decimal number or string with hexadeximal notation: "0x10".'
+        example: '12 or "0xc"'
+
+      dst:
+        description: 'Destination for command. Could be decimal number or string with hexadeximal notation: "0x10".'
+        example: '5 or "0x5"'
+
+      cmd:
+        description: 'Command itself. Could be decimal number or string with hexadeximal notation: "0x10".'
+        example: '144 or "0x90"'
+
+      att:
+        description: Optional parameters.
+        example: [0, 2]
+
+  update:
+    description: Update devices state from network.
+
+  volume:
+    description: Increase or decrease volume of system.
+
+    fields:
+      up:
+        description: Increases volume x levels.
+        example: 3
+      down:
+        description: Decreases volume x levels.
+        example: 3
+      mute: Mutes audio system. Value is ignored.
+      unmute: Unmutes audio system. Value is ignored.
+      toggle mute: Toggles mute of audio system. Value is ignored.
+
+  select_device:
+    description: Select HDMI device.
+    fields:
+      device:
+        description: Addres of device to select. Can be entity_id, physical address or alias from confuguration.
+        example: '"switch.hdmi_1" or "1.1.0.0" or "01:10"'
+
+  power_on:
+    description: Power on all devices which supports it.
+
+  standby:
+    description: Standby all devices which supports it.
diff --git a/homeassistant/components/switch/hdmi_cec.py b/homeassistant/components/switch/hdmi_cec.py
new file mode 100644
index 0000000000000000000000000000000000000000..bd1f9ea657877e348b5af988c4328f94507639c2
--- /dev/null
+++ b/homeassistant/components/switch/hdmi_cec.py
@@ -0,0 +1,63 @@
+"""
+Support for HDMI CEC devices as switches.
+
+For more details about this component, please refer to the documentation at
+https://home-assistant.io/components/hdmi_cec/
+"""
+import logging
+
+from homeassistant.components.hdmi_cec import CecDevice, ATTR_NEW
+from homeassistant.components.switch import SwitchDevice, DOMAIN
+from homeassistant.const import STATE_OFF, STATE_STANDBY, STATE_ON
+from homeassistant.core import HomeAssistant
+
+DEPENDENCIES = ['hdmi_cec']
+
+_LOGGER = logging.getLogger(__name__)
+
+ENTITY_ID_FORMAT = DOMAIN + '.{}'
+
+
+def setup_platform(hass, config, add_devices, discovery_info=None):
+    """Find and return HDMI devices as switches."""
+    if ATTR_NEW in discovery_info:
+        _LOGGER.info("Setting up HDMI devices %s", discovery_info[ATTR_NEW])
+        add_devices(CecSwitchDevice(hass, hass.data.get(device),
+                                    hass.data.get(device).logical_address) for
+                    device in discovery_info[ATTR_NEW])
+
+
+class CecSwitchDevice(CecDevice, SwitchDevice):
+    """Representation of a HDMI device as a Switch."""
+
+    def __init__(self, hass: HomeAssistant, device, logical):
+        """Initialize the HDMI device."""
+        CecDevice.__init__(self, hass, device, logical)
+        self.entity_id = "%s.%s_%s" % (
+            DOMAIN, 'hdmi', hex(self._logical_address)[2:])
+        self.update()
+
+    def turn_on(self, **kwargs) -> None:
+        """Turn device on."""
+        self._device.turn_on()
+        self._state = STATE_ON
+
+    def turn_off(self, **kwargs) -> None:
+        """Turn device off."""
+        self._device.turn_off()
+        self._state = STATE_ON
+
+    @property
+    def is_on(self) -> bool:
+        """Return True if entity is on."""
+        return self._state == STATE_ON
+
+    @property
+    def is_standby(self):
+        """Return true if device is in standby."""
+        return self._state == STATE_OFF or self._state == STATE_STANDBY
+
+    @property
+    def state(self) -> str:
+        """Cached state of device."""
+        return self._state
diff --git a/requirements_all.txt b/requirements_all.txt
index ec328eca44de17c549f868a56abfbf98ac081a55..59927a3672b36df4bf0f4e50d0526c6eae968407 100755
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -377,6 +377,9 @@ pwaqi==1.3
 # homeassistant.components.sensor.cpuspeed
 py-cpuinfo==0.2.3
 
+# homeassistant.components.hdmi_cec
+pyCEC==0.4.6
+
 # homeassistant.components.switch.tplink
 pyHS100==0.2.3