Example #1
0
def main():
    tcp_adapter = TcpAdapter("192.168.1.3", name="HASS", activate_source=False)
    hdmi_network = HDMINetwork(tcp_adapter)
    hdmi_network.start()
    while True:
        for d in hdmi_network.devices:
            _LOGGER.info("Device: %s", d)

        time.sleep(7)
Example #2
0
def test_devices():
    loop = asyncio.get_event_loop()
    network = HDMINetwork(
        MockAdapter([
            True,
            True,
            False,
            True,
            False,
            True,
            False,
            False,
            False,
            False,
            False,
            False,
            False,
            False,
            False,
            False,
        ]),
        scan_interval=0,
        loop=loop,
    )
    network._scan_delay = 0
    # network._adapter.set_command_callback(network.command_callback)
    network.init()
    network.scan()
    loop.run_until_complete(asyncio.sleep(0.1, loop))
    loop.stop()
    loop.run_forever()
    for i in [0, 1, 3, 5]:
        assert HDMIDevice(i) in network.devices
    for i in [2, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14]:
        assert HDMIDevice(i) not in network.devices
    for d in network.devices:
        d.stop()
    network.stop()
    loop.stop()
    loop.run_forever()
Example #3
0
def setup(hass: HomeAssistant, base_config):
    """Setup CEC capability."""
    from pycec.network import HDMINetwork
    from pycec.commands import CecCommand, KeyReleaseCommand, KeyPressCommand
    from pycec.const import KEY_VOLUME_UP, KEY_VOLUME_DOWN, KEY_MUTE_ON, \
        KEY_MUTE_OFF, KEY_MUTE_TOGGLE, 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.
    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)

    platform = base_config[DOMAIN].get(CONF_PLATFORM, SWITCH)

    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)
    display_name = base_config[DOMAIN].get(CONF_DISPLAY_NAME,
                                           DEFAULT_DISPLAY_NAME)
    if host:
        adapter = TcpAdapter(host, name=display_name, activate_source=False)
    else:
        adapter = CecAdapter(name=display_name, activate_source=False)
    hdmi_network = HDMINetwork(adapter, loop=loop)

    def _volume(call):
        """Increase/decrease volume and mute/unmute system."""
        mute_key_mapping = {
            ATTR_TOGGLE: KEY_MUTE_TOGGLE,
            ATTR_ON: KEY_MUTE_ON,
            ATTR_OFF: KEY_MUTE_OFF
        }
        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(mute_key_mapping[att],
                                    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(0, 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)

    def _standby(call):
        hdmi_network.standby()

    def _power_on(call):
        hdmi_network.power_on()

    def _select_device(call):
        """Select the active device."""
        from pycec.network import PhysicalAddress

        addr = call.data[ATTR_DEVICE]
        if not addr:
            _LOGGER.error("Device not found: %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()

    def _new_device(device):
        """Called when new device is detected by HDMI network."""
        key = DOMAIN + '.' + device.name
        hass.data[key] = device
        ent_platform = base_config[DOMAIN][CONF_TYPES].get(key, platform)
        discovery.load_platform(hass,
                                ent_platform,
                                DOMAIN,
                                discovered={ATTR_NEW: [key]},
                                hass_config=base_config)

    def _shutdown(call):
        hdmi_network.stop()

    def _start_cec(event):
        """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)

        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
Example #4
0
def setup(opp: OpenPeerPower, base_config):  # noqa: C901
    """Set up the CEC capability."""

    # Parse configuration into a dict of device name to physical address
    # represented as a list of four elements.
    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)

    platform = base_config[DOMAIN].get(CONF_PLATFORM, SWITCH)

    loop = (
        # Create own thread if more than 1 CPU
        opp.loop if multiprocessing.cpu_count() < 2 else None)
    host = base_config[DOMAIN].get(CONF_HOST)
    display_name = base_config[DOMAIN].get(CONF_DISPLAY_NAME,
                                           DEFAULT_DISPLAY_NAME)
    if host:
        adapter = TcpAdapter(host, name=display_name, activate_source=False)
    else:
        adapter = CecAdapter(name=display_name[:12], activate_source=False)
    hdmi_network = HDMINetwork(adapter, loop=loop)

    def _adapter_watchdog(now=None):
        _LOGGER.debug("Reached _adapter_watchdog")
        event.async_call_later(opp, WATCHDOG_INTERVAL, _adapter_watchdog)
        if not adapter.initialized:
            _LOGGER.info("Adapter not initialized; Trying to restart")
            opp.bus.fire(EVENT_HDMI_CEC_UNAVAILABLE)
            adapter.init()

    hdmi_network.set_initialized_callback(
        partial(event.async_call_later, opp, WATCHDOG_INTERVAL,
                _adapter_watchdog))

    def _volume(call):
        """Increase/decrease volume and mute/unmute system."""
        mute_key_mapping = {
            ATTR_TOGGLE: KEY_MUTE_TOGGLE,
            ATTR_ON: KEY_MUTE_ON,
            ATTR_OFF: KEY_MUTE_OFF,
        }
        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(mute_key_mapping[att],
                                    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(0, 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: f"{x}:{y:x}", data[ATTR_ATT])
            else:
                att = ""
            command = CecCommand(cmd, dst, src, att)
        hdmi_network.send_command(command)

    def _standby(call):
        hdmi_network.standby()

    def _power_on(call):
        hdmi_network.power_on()

    def _select_device(call):
        """Select the active device."""
        addr = call.data[ATTR_DEVICE]
        if not addr:
            _LOGGER.error("Device not found: %s", call.data[ATTR_DEVICE])
            return
        if addr in device_aliases:
            addr = device_aliases[addr]
        else:
            entity = opp.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):
        """
        Update if device update is needed.

        Called by service, requests CEC network to update data.
        """
        hdmi_network.scan()

    def _new_device(device):
        """Handle new devices which are detected by HDMI network."""
        key = f"{DOMAIN}.{device.name}"
        opp.data[key] = device
        ent_platform = base_config[DOMAIN][CONF_TYPES].get(key, platform)
        discovery.load_platform(
            opp,
            ent_platform,
            DOMAIN,
            discovered={ATTR_NEW: [key]},
            opp_config=base_config,
        )

    def _shutdown(call):
        hdmi_network.stop()

    def _start_cec(callback_event):
        """Register services and start HDMI network to watch for devices."""
        opp.services.register(DOMAIN, SERVICE_SEND_COMMAND, _tx,
                              SERVICE_SEND_COMMAND_SCHEMA)
        opp.services.register(DOMAIN,
                              SERVICE_VOLUME,
                              _volume,
                              schema=SERVICE_VOLUME_SCHEMA)
        opp.services.register(
            DOMAIN,
            SERVICE_UPDATE_DEVICES,
            _update,
            schema=SERVICE_UPDATE_DEVICES_SCHEMA,
        )
        opp.services.register(DOMAIN, SERVICE_POWER_ON, _power_on)
        opp.services.register(DOMAIN, SERVICE_STANDBY, _standby)
        opp.services.register(DOMAIN, SERVICE_SELECT_DEVICE, _select_device)

        hdmi_network.set_new_device_callback(_new_device)
        hdmi_network.start()

    opp.bus.listen_once(EVENT_OPENPEERPOWER_START, _start_cec)
    opp.bus.listen_once(EVENT_OPENPEERPOWER_STOP, _shutdown)
    return True
Example #5
0
def test_scan():
    loop = asyncio.get_event_loop()
    network = HDMINetwork(
        MockAdapter([
            True,
            True,
            False,
            True,
            False,
            True,
            False,
            False,
            False,
            False,
            False,
            False,
            False,
            False,
            False,
            False,
        ]),
        scan_interval=0,
        loop=loop,
    )
    network._scan_delay = 0
    # network._adapter.set_command_callback(network.command_callback)
    network.init()
    network.scan()
    loop.run_until_complete(asyncio.sleep(0.1, loop))
    loop.stop()
    loop.run_forever()

    assert HDMIDevice(0) in network.devices
    device = network.get_device(0)
    assert "Test0" == device.osd_name
    assert 2 == device.power_status

    assert HDMIDevice(1) in network.devices
    device = network.get_device(1)
    assert "Test1" == device.osd_name
    assert 2 == device.power_status

    assert HDMIDevice(2) not in network.devices

    assert HDMIDevice(3) in network.devices
    device = network.get_device(3)
    assert "Test3" == device.osd_name
    assert 2 == device.power_status
    for d in network.devices:
        d.stop()
    network.stop()
    loop.stop()
    loop.run_forever()
Example #6
0
def setup(hass: HomeAssistant, base_config: ConfigType) -> bool:  # noqa: C901
    """Set up the CEC capability."""

    # Parse configuration into a dict of device name to physical address
    # represented as a list of four elements.
    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)

    platform = base_config[DOMAIN].get(CONF_PLATFORM, SWITCH)

    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)
    display_name = base_config[DOMAIN].get(CONF_DISPLAY_NAME,
                                           DEFAULT_DISPLAY_NAME)
    if host:
        adapter = TcpAdapter(host, name=display_name, activate_source=False)
    else:
        adapter = CecAdapter(name=display_name[:12], activate_source=False)
    hdmi_network = HDMINetwork(adapter, loop=loop)

    def _adapter_watchdog(now=None):
        _LOGGER.debug("Reached _adapter_watchdog")
        event.async_call_later(hass, WATCHDOG_INTERVAL, _adapter_watchdog)
        if not adapter.initialized:
            _LOGGER.info("Adapter not initialized; Trying to restart")
            hass.bus.fire(EVENT_HDMI_CEC_UNAVAILABLE)
            adapter.init()

    @callback
    def _async_initialized_callback(*_: Any):
        """Add watchdog on initialization."""
        return event.async_call_later(hass, WATCHDOG_INTERVAL,
                                      _adapter_watchdog)

    hdmi_network.set_initialized_callback(_async_initialized_callback)

    def _volume(call: ServiceCall) -> None:
        """Increase/decrease volume and mute/unmute system."""
        mute_key_mapping = {
            ATTR_TOGGLE: KEY_MUTE_TOGGLE,
            ATTR_ON: KEY_MUTE_ON,
            ATTR_OFF: KEY_MUTE_OFF,
        }
        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(mute_key_mapping[att],
                                    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(0, att):
                hdmi_network.send_command(
                    KeyPressCommand(cmd, dst=ADDR_AUDIOSYSTEM))
                hdmi_network.send_command(
                    KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM))

    def _tx(call: ServiceCall) -> None:
        """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
            if ATTR_ATT in data:
                if isinstance(data[ATTR_ATT], (list, )):
                    att = data[ATTR_ATT]
                else:
                    att = reduce(lambda x, y: f"{x}:{y:x}", data[ATTR_ATT])
            else:
                att = ""
            command = CecCommand(cmd, dst, src, att)
        hdmi_network.send_command(command)

    def _standby(call: ServiceCall) -> None:
        hdmi_network.standby()

    def _power_on(call: ServiceCall) -> None:
        hdmi_network.power_on()

    def _select_device(call: ServiceCall) -> None:
        """Select the active device."""
        if not (addr := call.data[ATTR_DEVICE]):
            _LOGGER.error("Device not found: %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)
Example #7
0
#!/usr/bin/env python
import sys
from flask import Flask, jsonify, request, abort
from pycec.cec import CecAdapter
from pycec.const import POWER_OFF, POWER_ON
from pycec.network import HDMINetwork

from encoder import HDMIDeviceEncoder

app = Flask(__name__)
app.json_encoder = HDMIDeviceEncoder

network = HDMINetwork(CecAdapter("raspberry", activate_source=True))
network.start()


@app.route('/api/v1.0/cec', methods=['GET'])
def cec_list():
    return jsonify({'devices': network.devices})


@app.route('/api/v1.0/cec/<int:device_id>', methods=['GET'])
def cec_get(device_id):
    return jsonify(network.get_device(device_id))


@app.route('/api/v1.0/cec/<int:device_id>', methods=['PUT'])
def cec_post(device_id):
    if not request.json or 'powerStatus' not in request.json:
        abort(400)
Example #8
0
def main():
    config = configure()

    # Configure logging
    setup_logger(config)

    transports = set()
    loop = asyncio.get_event_loop()
    network = HDMINetwork(CecAdapter("pyCEC", activate_source=False), loop=loop)

    class CECServerProtocol(asyncio.Protocol):
        transport = None
        buffer = ''

        def connection_made(self, transport):
            _LOGGER.info("Connection opened by %s",
                         transport.get_extra_info('peername'))
            self.transport = transport
            transports.add(transport)

        def data_received(self, data):
            self.buffer += bytes.decode(data)
            for line in self.buffer.splitlines(keepends=True):
                if line.endswith('\n'):
                    line = line.rstrip()
                    if len(line) == 2:
                        _LOGGER.info("Received poll %s from %s", line,
                                     self.transport.get_extra_info('peername'))
                        d = CecCommand(line).dst
                        t = network._adapter.poll_device(d)
                        t.add_done_callback(
                            functools.partial(_after_poll, d))
                    else:
                        _LOGGER.info("Received command %s from %s", line,
                                     self.transport.get_extra_info('peername'))
                        network.send_command(CecCommand(line))
                    self.buffer = ''
                else:
                    self.buffer = line

        def connection_lost(self, exc):
            _LOGGER.info("Connection with %s lost",
                         self.transport.get_extra_info('peername'))
            transports.remove(self.transport)

    def _after_poll(d, f):
        if f.result():
            cmd = PollCommand(network._adapter.get_logical_address(), src=d)
            _send_command_to_tcp(cmd)

    def _send_command_to_tcp(command):
        for t in transports:
            _LOGGER.info("Sending %s to %s", command,
                         t.get_extra_info('peername'))
            t.write(str.encode("%s\n" % command.raw))

    network.set_command_callback(_send_command_to_tcp)
    loop.run_until_complete(network.async_init())

    _LOGGER.info("CEC initialized... Starting server.")
    # Each client connection will create a new protocol instance
    coro = loop.create_server(CECServerProtocol, config['DEFAULT']['host'],
                              int(config['DEFAULT']['port']))
    server = loop.run_until_complete(coro)
    # Serve requests until Ctrl+C is pressed
    _LOGGER.info('Serving on {}'.format(server.sockets[0].getsockname()))
    if _LOGGER.level >= logging.DEBUG:
        loop.create_task(async_show_devices(network, loop))
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass

    # Close the server
    server.close()
    loop.run_until_complete(server.wait_closed())
    loop.close()