overall: host a minimal copy of `ainsicolors`
diff --git a/apps/console.py b/apps/console.py
index a1d4d3a..b7c30c7 100644
--- a/apps/console.py
+++ b/apps/console.py
@@ -27,7 +27,6 @@
 from collections import OrderedDict
 
 import click
-import colors
 
 from prompt_toolkit import Application
 from prompt_toolkit.history import FileHistory
@@ -53,6 +52,7 @@
 
 from bumble import __version__
 import bumble.core
+from bumble import colors
 from bumble.core import UUID, AdvertisingData, BT_LE_TRANSPORT
 from bumble.device import ConnectionParametersPreferences, Device, Connection, Peer
 from bumble.utils import AsyncRunner
diff --git a/apps/controller_info.py b/apps/controller_info.py
index 22a1ce5..9c9345e 100644
--- a/apps/controller_info.py
+++ b/apps/controller_info.py
@@ -19,9 +19,9 @@
 import os
 import logging
 import click
-from colors import color
 from bumble.company_ids import COMPANY_IDENTIFIERS
 
+from bumble.colors import color
 from bumble.core import name_or_number
 from bumble.hci import (
     map_null_terminated_utf8_string,
diff --git a/apps/gatt_dump.py b/apps/gatt_dump.py
index 6a9cf72..a3205c0 100644
--- a/apps/gatt_dump.py
+++ b/apps/gatt_dump.py
@@ -19,9 +19,9 @@
 import os
 import logging
 import click
-from colors import color
 
 import bumble.core
+from bumble.colors import color
 from bumble.device import Device, Peer
 from bumble.gatt import show_services
 from bumble.transport import open_transport_or_link
diff --git a/apps/gg_bridge.py b/apps/gg_bridge.py
index a3f9f52..17c1662 100644
--- a/apps/gg_bridge.py
+++ b/apps/gg_bridge.py
@@ -20,8 +20,8 @@
 import struct
 import logging
 import click
-from colors import color
 
+from bumble.colors import color
 from bumble.device import Device, Peer
 from bumble.core import AdvertisingData
 from bumble.gatt import Service, Characteristic, CharacteristicValue
diff --git a/apps/l2cap_bridge.py b/apps/l2cap_bridge.py
index 91462a0..17623e4 100644
--- a/apps/l2cap_bridge.py
+++ b/apps/l2cap_bridge.py
@@ -19,8 +19,8 @@
 import logging
 import os
 import click
-from colors import color
 
+from bumble.colors import color
 from bumble.transport import open_transport_or_link
 from bumble.device import Device
 from bumble.utils import FlowControlAsyncPipe
diff --git a/apps/link_relay/link_relay.py b/apps/link_relay/link_relay.py
index e8bdceb..6036fa0 100644
--- a/apps/link_relay/link_relay.py
+++ b/apps/link_relay/link_relay.py
@@ -23,9 +23,10 @@
 import uuid
 import os
 from urllib.parse import urlparse
-from colors import color
 import websockets
 
+from bumble.colors import color
+
 # -----------------------------------------------------------------------------
 # Logging
 # -----------------------------------------------------------------------------
diff --git a/apps/pair.py b/apps/pair.py
index 0a1b8f9..3729143 100644
--- a/apps/pair.py
+++ b/apps/pair.py
@@ -19,9 +19,9 @@
 import os
 import logging
 import click
-from colors import color
 from prompt_toolkit.shortcuts import PromptSession
 
+from bumble.colors import color
 from bumble.device import Device, Peer
 from bumble.transport import open_transport_or_link
 from bumble.smp import PairingDelegate, PairingConfig
diff --git a/apps/scan.py b/apps/scan.py
index 672877f..dac7a2c 100644
--- a/apps/scan.py
+++ b/apps/scan.py
@@ -19,8 +19,8 @@
 import os
 import logging
 import click
-from colors import color
 
+from bumble.colors import color
 from bumble.device import Device
 from bumble.transport import open_transport_or_link
 from bumble.keys import JsonKeyStore
diff --git a/apps/show.py b/apps/show.py
index 86b5fb1..bf01ead 100644
--- a/apps/show.py
+++ b/apps/show.py
@@ -17,8 +17,8 @@
 # -----------------------------------------------------------------------------
 import struct
 import click
-from colors import color
 
+from bumble.colors import color
 from bumble import hci
 from bumble.transport.common import PacketReader
 from bumble.helpers import PacketTracer
diff --git a/apps/usb_probe.py b/apps/usb_probe.py
index 16ea4db..785b0dd 100644
--- a/apps/usb_probe.py
+++ b/apps/usb_probe.py
@@ -30,8 +30,8 @@
 import logging
 import click
 import usb1
-from colors import color
 
+from bumble.colors import color
 from bumble.transport.usb import load_libusb
 
 
diff --git a/bumble/att.py b/bumble/att.py
index b97aef1..e5cdadf 100644
--- a/bumble/att.py
+++ b/bumble/att.py
@@ -24,12 +24,12 @@
 # -----------------------------------------------------------------------------
 from __future__ import annotations
 import struct
-from colors import color
 from pyee import EventEmitter
 from typing import Dict, Type
 
 from bumble.core import UUID, name_or_number
 from bumble.hci import HCI_Object, key_with_value
+from bumble.colors import color
 
 
 # -----------------------------------------------------------------------------
diff --git a/bumble/avdtp.py b/bumble/avdtp.py
index 62e7b49..238036d 100644
--- a/bumble/avdtp.py
+++ b/bumble/avdtp.py
@@ -20,7 +20,6 @@
 import struct
 import time
 import logging
-from colors import color
 from pyee import EventEmitter
 from typing import Dict, Type
 
@@ -40,6 +39,7 @@
     VendorSpecificMediaCodecInformation,
 )
 from . import sdp
+from .colors import color
 
 # -----------------------------------------------------------------------------
 # Logging
diff --git a/bumble/colors.py b/bumble/colors.py
new file mode 100644
index 0000000..2813cfe
--- /dev/null
+++ b/bumble/colors.py
@@ -0,0 +1,103 @@
+# Copyright (c) 2012 Giorgos Verigakis <[email protected]>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from functools import partial
+from typing import List, Optional, Union
+
+
+# ANSI color names. There is also a "default"
+COLORS = ('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white')
+
+# ANSI style names
+STYLES = (
+    'none',
+    'bold',
+    'faint',
+    'italic',
+    'underline',
+    'blink',
+    'blink2',
+    'negative',
+    'concealed',
+    'crossed',
+)
+
+
+ColorSpec = Union[str, int]
+
+
+def _join(*values: ColorSpec) -> str:
+    return ';'.join(str(v) for v in values)
+
+
+def _color_code(spec: ColorSpec, base: int) -> str:
+    if isinstance(spec, str):
+        spec = spec.strip().lower()
+
+    if spec == 'default':
+        return _join(base + 9)
+    elif spec in COLORS:
+        return _join(base + COLORS.index(spec))
+    elif isinstance(spec, int) and 0 <= spec <= 255:
+        return _join(base + 8, 5, spec)
+    else:
+        raise ValueError('Invalid color spec "%s"' % spec)
+
+
+def color(
+    s: str,
+    fg: Optional[ColorSpec] = None,
+    bg: Optional[ColorSpec] = None,
+    style: Optional[str] = None,
+) -> str:
+    codes: List[ColorSpec] = []
+
+    if fg:
+        codes.append(_color_code(fg, 30))
+    if bg:
+        codes.append(_color_code(bg, 40))
+    if style:
+        for style_part in style.split('+'):
+            if style_part in STYLES:
+                codes.append(STYLES.index(style_part))
+            else:
+                raise ValueError('Invalid style "%s"' % style_part)
+
+    if codes:
+        return '\x1b[{0}m{1}\x1b[0m'.format(_join(*codes), s)
+    else:
+        return s
+
+
+# Foreground color shortcuts
+black = partial(color, fg='black')
+red = partial(color, fg='red')
+green = partial(color, fg='green')
+yellow = partial(color, fg='yellow')
+blue = partial(color, fg='blue')
+magenta = partial(color, fg='magenta')
+cyan = partial(color, fg='cyan')
+white = partial(color, fg='white')
+
+# Style shortcuts
+bold = partial(color, style='bold')
+none = partial(color, style='none')
+faint = partial(color, style='faint')
+italic = partial(color, style='italic')
+underline = partial(color, style='underline')
+blink = partial(color, style='blink')
+blink2 = partial(color, style='blink2')
+negative = partial(color, style='negative')
+concealed = partial(color, style='concealed')
+crossed = partial(color, style='crossed')
diff --git a/bumble/controller.py b/bumble/controller.py
index bc59fe4..da5d4cf 100644
--- a/bumble/controller.py
+++ b/bumble/controller.py
@@ -20,7 +20,7 @@
 import itertools
 import random
 import struct
-from colors import color
+from bumble.colors import color
 from bumble.core import BT_CENTRAL_ROLE, BT_PERIPHERAL_ROLE
 
 from bumble.hci import (
diff --git a/bumble/device.py b/bumble/device.py
index a971391..d97df49 100644
--- a/bumble/device.py
+++ b/bumble/device.py
@@ -25,8 +25,7 @@
 from dataclasses import dataclass
 from typing import Any, ClassVar, Dict, List, Optional, Tuple, Union
 
-from colors import color
-
+from .colors import color
 from .att import ATT_CID, ATT_DEFAULT_MTU, ATT_PDU
 from .gatt import Characteristic, Descriptor, Service
 from .hci import (
diff --git a/bumble/gatt.py b/bumble/gatt.py
index 180941b..7aa065c 100644
--- a/bumble/gatt.py
+++ b/bumble/gatt.py
@@ -29,8 +29,8 @@
 import logging
 import struct
 from typing import Optional, Sequence
-from colors import color
 
+from .colors import color
 from .core import UUID, get_dict_key_by_value
 from .att import Attribute
 
diff --git a/bumble/gatt_client.py b/bumble/gatt_client.py
index 9d0ce5c..2fd7573 100644
--- a/bumble/gatt_client.py
+++ b/bumble/gatt_client.py
@@ -27,9 +27,9 @@
 import logging
 import struct
 
-from colors import color
 from pyee import EventEmitter
 
+from .colors import color
 from .hci import HCI_Constant
 from .att import (
     ATT_ATTRIBUTE_NOT_FOUND_ERROR,
diff --git a/bumble/gatt_server.py b/bumble/gatt_server.py
index 9efc900..86786cd 100644
--- a/bumble/gatt_server.py
+++ b/bumble/gatt_server.py
@@ -29,8 +29,8 @@
 import struct
 from typing import List, Tuple, Optional
 from pyee import EventEmitter
-from colors import color
 
+from .colors import color
 from .core import UUID
 from .att import (
     ATT_ATTRIBUTE_NOT_FOUND_ERROR,
diff --git a/bumble/hci.py b/bumble/hci.py
index 951e81c..d8517c2 100644
--- a/bumble/hci.py
+++ b/bumble/hci.py
@@ -20,9 +20,9 @@
 import collections
 import logging
 import functools
-from colors import color
 from typing import Dict, Type, Union
 
+from .colors import color
 from .core import (
     BT_BR_EDR_TRANSPORT,
     AdvertisingData,
diff --git a/bumble/helpers.py b/bumble/helpers.py
index 55fe065..83c7c6d 100644
--- a/bumble/helpers.py
+++ b/bumble/helpers.py
@@ -16,8 +16,8 @@
 # Imports
 # -----------------------------------------------------------------------------
 import logging
-from colors import color
 
+from .colors import color
 from .att import ATT_CID, ATT_PDU
 from .smp import SMP_CID, SMP_Command
 from .core import name_or_number
diff --git a/bumble/hfp.py b/bumble/hfp.py
index 749570a..7bb9f08 100644
--- a/bumble/hfp.py
+++ b/bumble/hfp.py
@@ -18,7 +18,8 @@
 import logging
 import asyncio
 import collections
-from colors import color
+
+from .colors import color
 
 
 # -----------------------------------------------------------------------------
diff --git a/bumble/host.py b/bumble/host.py
index bb29eb0..65c7741 100644
--- a/bumble/host.py
+++ b/bumble/host.py
@@ -20,8 +20,7 @@
 import logging
 import struct
 
-from colors import color
-
+from bumble.colors import color
 from bumble.l2cap import L2CAP_PDU
 
 from .hci import (
diff --git a/bumble/keys.py b/bumble/keys.py
index 47be875..7fed660 100644
--- a/bumble/keys.py
+++ b/bumble/keys.py
@@ -25,8 +25,8 @@
 import os
 import json
 from typing import Optional
-from colors import color
 
+from .colors import color
 from .hci import Address
 
 
diff --git a/bumble/l2cap.py b/bumble/l2cap.py
index aaea46f..2610adc 100644
--- a/bumble/l2cap.py
+++ b/bumble/l2cap.py
@@ -21,10 +21,10 @@
 import struct
 
 from collections import deque
-from colors import color
 from pyee import EventEmitter
 from typing import Dict, Type
 
+from .colors import color
 from .core import BT_CENTRAL_ROLE, InvalidStateError, ProtocolError
 from .hci import (
     HCI_LE_Connection_Update_Command,
diff --git a/bumble/link.py b/bumble/link.py
index 09d249f..84ff47e 100644
--- a/bumble/link.py
+++ b/bumble/link.py
@@ -19,9 +19,9 @@
 import asyncio
 from functools import partial
 
-from colors import color
 import websockets
 
+from bumble.colors import color
 from bumble.hci import (
     Address,
     HCI_SUCCESS,
diff --git a/bumble/rfcomm.py b/bumble/rfcomm.py
index 66a681b..a6b02ba 100644
--- a/bumble/rfcomm.py
+++ b/bumble/rfcomm.py
@@ -18,10 +18,10 @@
 import logging
 import asyncio
 
-from colors import color
 from pyee import EventEmitter
 
 from . import core
+from .colors import color
 from .core import BT_BR_EDR_TRANSPORT, InvalidStateError, ProtocolError
 
 # -----------------------------------------------------------------------------
diff --git a/bumble/sdp.py b/bumble/sdp.py
index 896a47a..019b8e6 100644
--- a/bumble/sdp.py
+++ b/bumble/sdp.py
@@ -18,11 +18,10 @@
 from __future__ import annotations
 import logging
 import struct
-from colors import color
-import colors
 from typing import Dict, List, Type
 
 from . import core
+from .colors import color
 from .core import InvalidStateError
 from .hci import HCI_Object, name_or_number, key_with_value
 
@@ -506,7 +505,7 @@
     def to_string(self, with_colors=False):
         if with_colors:
             return (
-                f'Attribute(id={colors.color(self.id_name(self.id),"magenta")},'
+                f'Attribute(id={color(self.id_name(self.id),"magenta")},'
                 f'value={self.value})'
             )
 
diff --git a/bumble/smp.py b/bumble/smp.py
index 416fb2b..9a72cb0 100644
--- a/bumble/smp.py
+++ b/bumble/smp.py
@@ -29,8 +29,8 @@
 from typing import Dict, Optional, Type
 
 from pyee import EventEmitter
-from colors import color
 
+from .colors import color
 from .hci import Address, HCI_LE_Enable_Encryption_Command, HCI_Object, key_with_value
 from .core import (
     BT_BR_EDR_TRANSPORT,
diff --git a/bumble/transport/common.py b/bumble/transport/common.py
index a296407..945ba4b 100644
--- a/bumble/transport/common.py
+++ b/bumble/transport/common.py
@@ -18,9 +18,9 @@
 import struct
 import asyncio
 import logging
-from colors import color
 
 from .. import hci
+from ..colors import color
 
 
 # -----------------------------------------------------------------------------
diff --git a/bumble/transport/pyusb.py b/bumble/transport/pyusb.py
index 6b3152a..8ad8598 100644
--- a/bumble/transport/pyusb.py
+++ b/bumble/transport/pyusb.py
@@ -22,10 +22,10 @@
 
 import usb.core
 import usb.util
-from colors import color
 
 from .common import Transport, ParserSource
 from .. import hci
+from ..colors import color
 
 
 # -----------------------------------------------------------------------------
diff --git a/bumble/transport/usb.py b/bumble/transport/usb.py
index f0efa32..68c5a6f 100644
--- a/bumble/transport/usb.py
+++ b/bumble/transport/usb.py
@@ -23,10 +23,10 @@
 import platform
 
 import usb1
-from colors import color
 
 from .common import Transport, ParserSource
 from .. import hci
+from ..colors import color
 
 
 # -----------------------------------------------------------------------------
diff --git a/bumble/utils.py b/bumble/utils.py
index 111f055..474fff2 100644
--- a/bumble/utils.py
+++ b/bumble/utils.py
@@ -22,9 +22,9 @@
 import sys
 from typing import Awaitable, TypeVar
 from functools import wraps
-from colors import color
 from pyee import EventEmitter
 
+from .colors import color
 
 # -----------------------------------------------------------------------------
 # Logging
diff --git a/examples/battery_client.py b/examples/battery_client.py
index a7854ce..3cf11b4 100644
--- a/examples/battery_client.py
+++ b/examples/battery_client.py
@@ -19,7 +19,7 @@
 import sys
 import os
 import logging
-from colors import color
+from bumble.colors import color
 from bumble.device import Device
 from bumble.transport import open_transport
 from bumble.profiles.battery_service import BatteryServiceProxy
diff --git a/examples/device_information_client.py b/examples/device_information_client.py
index 9fa6c15..416aa2f 100644
--- a/examples/device_information_client.py
+++ b/examples/device_information_client.py
@@ -19,7 +19,7 @@
 import sys
 import os
 import logging
-from colors import color
+from bumble.colors import color
 from bumble.device import Device, Peer
 from bumble.profiles.device_information_service import DeviceInformationServiceProxy
 from bumble.transport import open_transport
diff --git a/examples/heart_rate_client.py b/examples/heart_rate_client.py
index 99ed359..ecfcffb 100644
--- a/examples/heart_rate_client.py
+++ b/examples/heart_rate_client.py
@@ -19,7 +19,7 @@
 import sys
 import os
 import logging
-from colors import color
+from bumble.colors import color
 from bumble.device import Device
 from bumble.transport import open_transport
 from bumble.profiles.heart_rate_service import HeartRateServiceProxy
diff --git a/examples/keyboard.py b/examples/keyboard.py
index b8729c0..16dbeb6 100644
--- a/examples/keyboard.py
+++ b/examples/keyboard.py
@@ -22,7 +22,7 @@
 import struct
 import json
 import websockets
-from colors import color
+from bumble.colors import color
 
 from bumble.core import AdvertisingData
 from bumble.device import Device, Connection, Peer
diff --git a/examples/run_a2dp_info.py b/examples/run_a2dp_info.py
index 48d8500..2f21cfa 100644
--- a/examples/run_a2dp_info.py
+++ b/examples/run_a2dp_info.py
@@ -20,7 +20,7 @@
 import os
 import logging
 
-from colors import color
+from bumble.colors import color
 from bumble.device import Device
 from bumble.transport import open_transport_or_link
 from bumble.core import (
diff --git a/examples/run_a2dp_source.py b/examples/run_a2dp_source.py
index 68aa435..2443518 100644
--- a/examples/run_a2dp_source.py
+++ b/examples/run_a2dp_source.py
@@ -20,7 +20,7 @@
 import os
 import logging
 
-from colors import color
+from bumble.colors import color
 from bumble.device import Device
 from bumble.transport import open_transport_or_link
 from bumble.core import BT_BR_EDR_TRANSPORT
diff --git a/examples/run_classic_connect.py b/examples/run_classic_connect.py
index 70575a5..bb46bf7 100644
--- a/examples/run_classic_connect.py
+++ b/examples/run_classic_connect.py
@@ -19,7 +19,7 @@
 import sys
 import os
 import logging
-from colors import color
+from bumble.colors import color
 
 from bumble.device import Device
 from bumble.transport import open_transport_or_link
diff --git a/examples/run_classic_discovery.py b/examples/run_classic_discovery.py
index 00f8814..569c8b3 100644
--- a/examples/run_classic_discovery.py
+++ b/examples/run_classic_discovery.py
@@ -19,7 +19,7 @@
 import sys
 import os
 import logging
-from colors import color
+from bumble.colors import color
 
 from bumble.device import Device
 from bumble.transport import open_transport_or_link
diff --git a/examples/run_controller_with_scanner.py b/examples/run_controller_with_scanner.py
index bdf53a7..9603cff 100644
--- a/examples/run_controller_with_scanner.py
+++ b/examples/run_controller_with_scanner.py
@@ -19,7 +19,7 @@
 import asyncio
 import sys
 import os
-from colors import color
+from bumble.colors import color
 
 from bumble.device import Device
 from bumble.controller import Controller
diff --git a/examples/run_gatt_client.py b/examples/run_gatt_client.py
index 09d0edc..dcf8a1b 100644
--- a/examples/run_gatt_client.py
+++ b/examples/run_gatt_client.py
@@ -19,7 +19,7 @@
 import sys
 import os
 import logging
-from colors import color
+from bumble.colors import color
 
 from bumble.core import ProtocolError
 from bumble.device import Device, Peer
diff --git a/examples/run_gatt_client_and_server.py b/examples/run_gatt_client_and_server.py
index 6586ca4..f3df733 100644
--- a/examples/run_gatt_client_and_server.py
+++ b/examples/run_gatt_client_and_server.py
@@ -18,7 +18,7 @@
 import asyncio
 import os
 import logging
-from colors import color
+from bumble.colors import color
 
 from bumble.core import ProtocolError
 from bumble.controller import Controller
diff --git a/examples/run_hfp_gateway.py b/examples/run_hfp_gateway.py
index 09942c4..63a2a7c 100644
--- a/examples/run_hfp_gateway.py
+++ b/examples/run_hfp_gateway.py
@@ -20,7 +20,7 @@
 import os
 import logging
 
-from colors import color
+from bumble.colors import color
 
 import bumble.core
 from bumble.device import Device
diff --git a/examples/run_rfcomm_client.py b/examples/run_rfcomm_client.py
index f6cf24e..9a94278 100644
--- a/examples/run_rfcomm_client.py
+++ b/examples/run_rfcomm_client.py
@@ -20,7 +20,7 @@
 import os
 import logging
 
-from colors import color
+from bumble.colors import color
 
 import bumble.core
 from bumble.device import Device
diff --git a/examples/run_scanner.py b/examples/run_scanner.py
index b04b0e8..bdd7fba 100644
--- a/examples/run_scanner.py
+++ b/examples/run_scanner.py
@@ -19,7 +19,7 @@
 import sys
 import os
 import logging
-from colors import color
+from bumble.colors import color
 
 from bumble.device import Device
 from bumble.transport import open_transport_or_link
diff --git a/setup.cfg b/setup.cfg
index 781c367..3be6bcc 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -30,7 +30,6 @@
     bumble.apps = apps
 include-package-data = True
 install_requires =
-    ansicolors >= 1.1
     appdirs >= 1.4
     click >= 7.1.2; platform_system!='Emscripten'
     construct >= 2.10