Repository: XKNX/xknx

Issue:
Rename PhysicalAddress
Should we rename "physical address" to "individual address" in our code as used in the specs?

Please leave a reaction with 👍 or 👎

Discussion:
coveralls: 
[![Coverage Status](https://coveralls.io/builds/35310559/badge)](https://coveralls.io/builds/35310559)

Coverage remained the same at 94.581% when pulling **d908e788d9a36fc0a8015164d2ec05f0657c8973 on farmio:individual_address** into **825ea6303687a8bb4b9f5cb9a371d259e6e4c2ab on XKNX:master**.

farmio: I accidently committed and pushed https://github.com/XKNX/xknx/pull/518/commits/1279edc5716992d8af4b30ea8fe682858dc740a1 to the wrong branch so it landed here. I can remove it again, if you like, but its not really worth an extra PR anyway.

Patch:
diff --git a/changelog.md b/changelog.md
index e61724dd..43f43ced 100644
--- a/changelog.md
+++ b/changelog.md
@@ -8,6 +8,7 @@
 
 ### Internals
 
+- renamed "PhysicalAddress" to "IndividualAddress"
 - Telegram: `group_address` renamed to `destination_address`, to prepare support for other APCI services and add `source_address`
 - Farewell Travis CI; Welcome Github Actions!
 
diff --git a/docs/configuration.md b/docs/configuration.md
index 5755f961..79b4ffc0 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -14,7 +14,7 @@ XKNX is controlled via a configuration file. Per default the configuration file
 The configuration file can contain three sections.
 
 - The `general` section can contain:
-  - `own_address` the individual / physical address of the XKNX daemon
+  - `own_address` the individual (physical) address of the XKNX daemon
   - `rate_limit` a rate limit for telegrams sent to the bus
   - `multicast_group` to override the default multicast address (`224.0.23.12`)
   - `multicast_port` to override the default multicast port (`3671`)
diff --git a/docs/xknx.md b/docs/xknx.md
index 57427120..28670162 100644
--- a/docs/xknx.md
+++ b/docs/xknx.md
@@ -38,7 +38,7 @@ The constructor of the XKNX object takes several parameters:
 
 * `config` defines a path to the local [XKNX.yaml](/configuration).
 * `loop` points to the asyncio.loop object. Of not specified it uses `asyncio.get_event_loop()`.
-* `own_address` may be used to specify the physical KNX address of the XKNX daemon. If not speficied it uses `15.15.250`.
+* `own_address` may be used to specify the individual (physical) KNX address of the XKNX daemon. If not speficied it uses `15.15.250`.
 * `address_format` may be used to specify the type of group addresses to use. Possible values are:
 ** FREE: integer or hex representation
 ** SHORT: representation like '1/34' without middle groups
@@ -148,4 +148,3 @@ asyncio.run(main())
 
 
 
-
diff --git a/examples/example_tunnel.py b/examples/example_tunnel.py
index e6461125..c5450568 100644
--- a/examples/example_tunnel.py
+++ b/examples/example_tunnel.py
@@ -4,7 +4,7 @@ import asyncio
 from xknx import XKNX
 from xknx.dpt import DPTBinary
 from xknx.io import GatewayScanner, Tunnel
-from xknx.telegram import GroupAddress, PhysicalAddress, Telegram
+from xknx.telegram import GroupAddress, IndividualAddress, Telegram
 
 
 async def main():
@@ -19,7 +19,7 @@ async def main():
 
     gateway = gateways[0]
     # an individual address will most likely be assigned by the tunnelling server
-    xknx.own_address = PhysicalAddress("15.15.249")
+    xknx.own_address = IndividualAddress("15.15.249")
 
     print(
         "Connecting to {}:{} from {}".format(
diff --git a/xknx/config/config_validation.py b/xknx/config/config_validation.py
index c3138870..3d33e1ac 100644
--- a/xknx/config/config_validation.py
+++ b/xknx/config/config_validation.py
@@ -7,7 +7,7 @@ from typing import Any, List, Type, TypeVar, Union
 
 import voluptuous as vol
 from xknx.dpt import DPTBase
-from xknx.telegram import GroupAddress, PhysicalAddress
+from xknx.telegram import GroupAddress, IndividualAddress
 
 # pylint: disable=invalid-name
 # typing typevar
@@ -63,11 +63,11 @@ def ensure_group_address(value: str) -> str:
     return value
 
 
-def ensure_physical_address(value: str) -> str:
-    """Ensure value is a valid physical address."""
+def ensure_individual_address(value: str) -> str:
+    """Ensure value is a valid individual address."""
     value = str(value)
-    if not PhysicalAddress.ADDRESS_RE.match(value):
-        raise vol.Invalid(f"{value} is not a valid physical address")
+    if not IndividualAddress.ADDRESS_RE.match(value):
+        raise vol.Invalid(f"{value} is not a valid individual address")
 
     return value
 
diff --git a/xknx/config/schema.py b/xknx/config/schema.py
index 91c2d400..5cc6ef65 100644
--- a/xknx/config/schema.py
+++ b/xknx/config/schema.py
@@ -10,8 +10,8 @@ from ..remote_value.remote_value_datetime import DateTimeType
 from .config_validation import (
     boolean,
     ensure_group_address,
+    ensure_individual_address,
     ensure_list,
-    ensure_physical_address,
     enum,
     isdir,
     match_all,
@@ -592,7 +592,7 @@ class XKNXSchema:
                 vol.Coerce(int), vol.Range(min=0, max=100)
             ),
             vol.Optional(CONF_VERSION, default=2): string,
-            vol.Optional(CONF_OWN_ADDRESS): ensure_physical_address,
+            vol.Optional(CONF_OWN_ADDRESS): ensure_individual_address,
             vol.Optional(CONF_LOG_DIRECTORY): isdir,
             vol.Inclusive(CONF_FIRE_EVENT, "fire_ev"): boolean,
             vol.Inclusive(CONF_FIRE_EVENT_FILTER, "fire_ev"): vol.All(
diff --git a/xknx/io/tunnel.py b/xknx/io/tunnel.py
index 07f04f7e..d81fbb9e 100644
--- a/xknx/io/tunnel.py
+++ b/xknx/io/tunnel.py
@@ -14,7 +14,7 @@ from xknx.knxip import (
     TunnellingAck,
     TunnellingRequest,
 )
-from xknx.telegram import PhysicalAddress, TelegramDirection
+from xknx.telegram import IndividualAddress, TelegramDirection
 
 from .connect import Connect
 from .connectionstate import ConnectionState
@@ -139,7 +139,7 @@ class Tunnel:
         self._is_reconnecting = False
         self.communication_channel = connect.communication_channel
         # Use the individual address provided by the tunnelling server
-        self._src_address = PhysicalAddress(connect.identifier)
+        self._src_address = IndividualAddress(connect.identifier)
         self.sequence_number = 0
         await self.start_heartbeat()
 
diff --git a/xknx/knxip/cemi_frame.py b/xknx/knxip/cemi_frame.py
index 5f4ac8be..c26dd1e6 100644
--- a/xknx/knxip/cemi_frame.py
+++ b/xknx/knxip/cemi_frame.py
@@ -15,7 +15,7 @@ from typing import Union
 
 from xknx.dpt import DPTArray, DPTBinary
 from xknx.exceptions import ConversionError, CouldNotParseKNXIP, UnsupportedCEMIMessage
-from xknx.telegram import GroupAddress, PhysicalAddress, Telegram, TelegramType
+from xknx.telegram import GroupAddress, IndividualAddress, Telegram, TelegramType
 
 from .knxip_enum import APCICommand, CEMIFlags, CEMIMessageCode
 
@@ -31,8 +31,8 @@ class CEMIFrame:
         code: CEMIMessageCode = CEMIMessageCode.L_DATA_IND,
         flags: int = 0,
         cmd: APCICommand = APCICommand.GROUP_READ,
-        src_addr: PhysicalAddress = PhysicalAddress(None),
-        dst_addr: Union[GroupAddress, PhysicalAddress] = GroupAddress(None),
+        src_addr: IndividualAddress = IndividualAddress(None),
+        dst_addr: Union[GroupAddress, IndividualAddress] = GroupAddress(None),
         mpdu_len: int = 0,
         payload=None,
     ):
@@ -51,7 +51,7 @@ class CEMIFrame:
         xknx,
         telegram: Telegram,
         code: CEMIMessageCode = CEMIMessageCode.L_DATA_IND,
-        src_addr: PhysicalAddress = PhysicalAddress(None),
+        src_addr: IndividualAddress = IndividualAddress(None),
     ):
         """Return CEMIFrame from a Telegram."""
         cemi = CEMIFrame(xknx, code=code, src_addr=src_addr)
@@ -100,7 +100,7 @@ class CEMIFrame:
 
         if isinstance(telegram.destination_address, GroupAddress):
             self.flags |= CEMIFlags.DESTINATION_GROUP_ADDRESS
-        elif isinstance(telegram.destination_address, PhysicalAddress):
+        elif isinstance(telegram.destination_address, IndividualAddress):
             self.flags |= CEMIFlags.DESTINATION_INDIVIDUAL_ADDRESS
         else:
             raise TypeError()
@@ -174,14 +174,14 @@ class CEMIFrame:
         # Control field 1 and Control field 2 - first 2 octets after Additional information
         self.flags = cemi[2 + addil] * 256 + cemi[3 + addil]
 
-        self.src_addr = PhysicalAddress((cemi[4 + addil], cemi[5 + addil]))
+        self.src_addr = IndividualAddress((cemi[4 + addil], cemi[5 + addil]))
 
         if self.flags & CEMIFlags.DESTINATION_GROUP_ADDRESS:
             self.dst_addr = GroupAddress(
                 (cemi[6 + addil], cemi[7 + addil]), levels=self.xknx.address_format
             )
         else:
-            self.dst_addr = PhysicalAddress((cemi[6 + addil], cemi[7 + addil]))
+            self.dst_addr = IndividualAddress((cemi[6 + addil], cemi[7 + addil]))
 
         self.mpdu_len = cemi[8 + addil]
 
@@ -217,9 +217,9 @@ class CEMIFrame:
 
     def to_knx(self):
         """Serialize to KNX/IP raw data."""
-        if not isinstance(self.src_addr, (GroupAddress, PhysicalAddress)):
+        if not isinstance(self.src_addr, (GroupAddress, IndividualAddress)):
             raise ConversionError("src_addr not set")
-        if not isinstance(self.dst_addr, (GroupAddress, PhysicalAddress)):
+        if not isinstance(self.dst_addr, (GroupAddress, IndividualAddress)):
             raise ConversionError("dst_addr not set")
 
         data = []
diff --git a/xknx/knxip/dib.py b/xknx/knxip/dib.py
index 5f34570b..bb68b23e 100644
--- a/xknx/knxip/dib.py
+++ b/xknx/knxip/dib.py
@@ -12,7 +12,7 @@ A KNX/IP Search Response may contain several DIBs of different types:
 """
 
 from xknx.exceptions import CouldNotParseKNXIP
-from xknx.telegram import PhysicalAddress
+from xknx.telegram import IndividualAddress
 
 from .knxip_enum import DIBServiceFamily, DIBTypeCode, KNXMedium
 
@@ -183,7 +183,7 @@ class DIBDeviceInformation(DIB):
         super().__init__()
         self.knx_medium = KNXMedium.TP1
         self.programming_mode = False
-        self.individual_address = PhysicalAddress(None)
+        self.individual_address = IndividualAddress(None)
         self.installation_number = 0
         self.project_number = 0
         self.serial_number = ""
@@ -207,7 +207,7 @@ class DIBDeviceInformation(DIB):
         self.knx_medium = KNXMedium(raw[2])
         # last bit of device_status. All other bits are unused
         self.programming_mode = bool(raw[3])
-        self.individual_address = PhysicalAddress((raw[4], raw[5]))
+        self.individual_address = IndividualAddress((raw[4], raw[5]))
         installation_project_identifier = raw[6] * 256 + raw[7]
         self.project_number = installation_project_identifier >> 4
         self.installation_number = installation_project_identifier & 15
diff --git a/xknx/telegram/__init__.py b/xknx/telegram/__init__.py
index 34663554..b47b5fd0 100644
--- a/xknx/telegram/__init__.py
+++ b/xknx/telegram/__init__.py
@@ -6,6 +6,6 @@ Module for handling KNX primitves.
 
 """
 # flake8: noqa
-from .address import GroupAddress, GroupAddressType, PhysicalAddress
+from .address import GroupAddress, GroupAddressType, IndividualAddress
 from .address_filter import AddressFilter
 from .telegram import Telegram, TelegramDirection, TelegramType
diff --git a/xknx/telegram/address.py b/xknx/telegram/address.py
index b70f6418..42516c96 100644
--- a/xknx/telegram/address.py
+++ b/xknx/telegram/address.py
@@ -3,7 +3,7 @@ Module for serialization/deserialization and handling of KNX addresses.
 
 The module can handle:
 
-* physical addresses of devices.
+* individual addresses of devices.
 * (logical) group addresses.
 
 The module supports all different writings of group addresses:
@@ -70,8 +70,8 @@ class BaseAddress:  # pylint: disable=too-few-public-methods
         return self.raw
 
 
-class PhysicalAddress(BaseAddress):
-    """Class for handling KNX pyhsical addresses."""
+class IndividualAddress(BaseAddress):
+    """Class for handling KNX individual addresses."""
 
     MAX_AREA = 15
     MAX_MAIN = 15
@@ -83,7 +83,7 @@ class PhysicalAddress(BaseAddress):
     def __init__(self, address: Union[object, str, int, tuple]) -> None:
         """Initialize Address class."""
         super().__init__()
-        if isinstance(address, PhysicalAddress):
+        if isinstance(address, IndividualAddress):
             self.raw: int = address.raw
         elif isinstance(address, str):
             if address.isdigit():
@@ -124,17 +124,17 @@ class PhysicalAddress(BaseAddress):
 
     @property
     def area(self) -> int:
-        """Return area part of pyhsical address."""
+        """Return area part of individual address."""
         return (self.raw >> 12) & self.MAX_AREA
 
     @property
     def main(self) -> int:
-        """Return main part of pyhsical address."""
+        """Return main part of individual address."""
         return (self.raw >> 8) & self.MAX_MAIN
 
     @property
     def line(self) -> int:
-        """Return line part of pyhsical address."""
+        """Return line part of individual address."""
         return self.raw & self.MAX_LINE
 
     @property
@@ -153,7 +153,7 @@ class PhysicalAddress(BaseAddress):
 
     def __repr__(self) -> str:
         """Return this object as parsable string."""
-        return f'PhysicalAddress("{self}")'
+        return f'IndividualAddress("{self}")'
 
 
 class GroupAddressType(Enum):
diff --git a/xknx/telegram/telegram.py b/xknx/telegram/telegram.py
index b5fe023c..759bc6e0 100644
--- a/xknx/telegram/telegram.py
+++ b/xknx/telegram/telegram.py
@@ -17,7 +17,7 @@ It contains
 from enum import Enum
 from typing import Any, Union
 
-from .address import GroupAddress, PhysicalAddress
+from .address import GroupAddress, IndividualAddress
 
 
 class TelegramDirection(Enum):
@@ -42,11 +42,13 @@ class Telegram:
 
     def __init__(
         self,
-        destination_address: Union[GroupAddress, PhysicalAddress] = GroupAddress(None),
+        destination_address: Union[GroupAddress, IndividualAddress] = GroupAddress(
+            None
+        ),
         telegramtype: TelegramType = TelegramType.GROUP_WRITE,
         direction: TelegramDirection = TelegramDirection.OUTGOING,
         payload: Any = None,
-        source_address: PhysicalAddress = PhysicalAddress(None),
+        source_address: IndividualAddress = IndividualAddress(None),
     ) -> None:
         """Initialize Telegram class."""
         self.destination_address = destination_address
@@ -62,8 +64,8 @@ class Telegram:
             'destination_address="{}" payload="{}" />'.format(
                 self.direction.value,
                 self.telegramtype.value,
-                self.source_address.__repr__(),
-                self.destination_address.__repr__(),
+                self.source_address,
+                self.destination_address,
                 self.payload,
             )
         )
diff --git a/xknx/xknx.py b/xknx/xknx.py
index b30b747d..d06f68d1 100644
--- a/xknx/xknx.py
+++ b/xknx/xknx.py
@@ -15,7 +15,7 @@ from xknx.io import (
     ConnectionConfig,
     KNXIPInterface,
 )
-from xknx.telegram import GroupAddressType, PhysicalAddress
+from xknx.telegram import GroupAddressType, IndividualAddress
 
 from .__version__ import __version__ as VERSION
 
@@ -55,7 +55,7 @@ class XKNX:
         self.knxip_interface = None
         self.started = asyncio.Event()
         self.address_format = address_format
-        self.own_address = PhysicalAddress(own_address)
+        self.own_address = IndividualAddress(own_address)
         self.rate_limit = rate_limit
         self.multicast_group = multicast_group
         self.multicast_port = multicast_port


Test patch:
diff --git a/test/io_tests/gateway_scanner_test.py b/test/io_tests/gateway_scanner_test.py
index 5c214615..bc8813fe 100644
--- a/test/io_tests/gateway_scanner_test.py
+++ b/test/io_tests/gateway_scanner_test.py
@@ -16,7 +16,7 @@ from xknx.knxip import (
     KNXIPServiceType,
     SearchResponse,
 )
-from xknx.telegram import PhysicalAddress
+from xknx.telegram import IndividualAddress
 
 
 class TestGatewayScanner(unittest.TestCase):
@@ -198,7 +198,7 @@ def fake_router_search_response(xknx: XKNX) -> KNXIPFrame:
     _device_information = DIBDeviceInformation()
     _device_information.name = "Gira KNX/IP-Router"
     _device_information.serial_number = "11:22:33:44:55:66"
-    _device_information.individual_address = PhysicalAddress("1.1.0")
+    _device_information.individual_address = IndividualAddress("1.1.0")
     _device_information.mac_address = "01:02:03:04:05:06"
 
     _svc_families = DIBSuppSVCFamilies()
diff --git a/test/io_tests/tunnelling_test.py b/test/io_tests/tunnelling_test.py
index 265e5ef9..198fe70e 100644
--- a/test/io_tests/tunnelling_test.py
+++ b/test/io_tests/tunnelling_test.py
@@ -7,7 +7,7 @@ from xknx import XKNX
 from xknx.dpt import DPTArray
 from xknx.io import Tunnelling, UDPClient
 from xknx.knxip import ErrorCode, KNXIPFrame, KNXIPServiceType, TunnellingAck
-from xknx.telegram import GroupAddress, PhysicalAddress, Telegram
+from xknx.telegram import GroupAddress, IndividualAddress, Telegram
 
 
 class TestTunnelling(unittest.TestCase):
@@ -32,7 +32,7 @@ class TestTunnelling(unittest.TestCase):
             destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x1, 0x2, 0x3))
         )
         sequence_counter = 42
-        src_address = PhysicalAddress("2.2.2")
+        src_address = IndividualAddress("2.2.2")
         tunnelling = Tunnelling(
             xknx,
             udp_client,
diff --git a/test/knxip_tests/cemi_frame_test.py b/test/knxip_tests/cemi_frame_test.py
index 912e4f89..c3804a2c 100644
--- a/test/knxip_tests/cemi_frame_test.py
+++ b/test/knxip_tests/cemi_frame_test.py
@@ -7,7 +7,7 @@ from xknx.dpt import DPTBinary, DPTComparator
 from xknx.exceptions import ConversionError, CouldNotParseKNXIP, UnsupportedCEMIMessage
 from xknx.knxip.cemi_frame import CEMIFrame
 from xknx.knxip.knxip_enum import APCICommand, CEMIFlags, CEMIMessageCode
-from xknx.telegram import GroupAddress, PhysicalAddress, Telegram
+from xknx.telegram import GroupAddress, IndividualAddress, Telegram
 
 
 def get_data(code, adil, flags, src, dst, mpdu_len, tpci_apci, payload):
@@ -44,8 +44,8 @@ def test_valid_command(frame):
     assert frame.flags == 0
     assert frame.mpdu_len == 1
     assert frame.payload == DPTBinary(0)
-    assert frame.src_addr == PhysicalAddress(0)
-    assert frame.dst_addr == PhysicalAddress(0)
+    assert frame.src_addr == IndividualAddress(0)
+    assert frame.dst_addr == IndividualAddress(0)
     assert packet_len == 11
 
 
@@ -69,7 +69,7 @@ def test_invalid_src_addr(frame):
     frame.mpdu_len = 1
     frame.payload = DPTBinary(0)
     frame.src_addr = None
-    frame.dst_addr = PhysicalAddress(0)
+    frame.dst_addr = IndividualAddress(0)
 
     with raises(ConversionError, match=r"src_addr not set"):
         frame.to_knx()
@@ -82,7 +82,7 @@ def test_invalid_dst_addr(frame):
     frame.flags = 0
     frame.mpdu_len = 1
     frame.payload = DPTBinary(0)
-    frame.src_addr = PhysicalAddress(0)
+    frame.src_addr = IndividualAddress(0)
     frame.dst_addr = None
 
     with raises(ConversionError, match=r"dst_addr not set"):
@@ -96,8 +96,8 @@ def test_no_payload(frame):
     frame.flags = 0
     frame.mpdu_len = 1
     frame.payload = None
-    frame.src_addr = PhysicalAddress(0)
-    frame.dst_addr = PhysicalAddress(0)
+    frame.src_addr = IndividualAddress(0)
+    frame.dst_addr = IndividualAddress(0)
 
     assert [41, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0] == frame.to_knx()
     assert 11 == frame.calculated_length()
@@ -110,8 +110,8 @@ def test_invalid_payload(frame):
     frame.flags = 0
     frame.mpdu_len = 1
     frame.payload = DPTComparator()
-    frame.src_addr = PhysicalAddress(0)
-    frame.dst_addr = PhysicalAddress(0)
+    frame.src_addr = IndividualAddress(0)
+    frame.dst_addr = IndividualAddress(0)
 
     with raises(TypeError):
         frame.calculated_length()
@@ -162,11 +162,11 @@ def test_from_knx_group_address(frame):
     assert frame.dst_addr == GroupAddress(0)
 
 
-def test_from_knx_physical_address(frame):
-    """Test conversion for a cemi with a physical address as destination"""
+def test_from_knx_individual_address(frame):
+    """Test conversion for a cemi with a individual address as destination"""
     frame.from_knx(get_data(0x29, 0, 0x00, 0, 0, 1, 0, []))
 
-    assert frame.dst_addr == PhysicalAddress(0)
+    assert frame.dst_addr == IndividualAddress(0)
 
 
 def test_telegram_group_address(frame):
@@ -178,9 +178,9 @@ def test_telegram_group_address(frame):
     ) == CEMIFlags.DESTINATION_GROUP_ADDRESS
 
 
-def test_telegram_physical_address(frame):
-    """Test telegram conversion flags with a physical address"""
-    frame.telegram = Telegram(destination_address=PhysicalAddress(0))
+def test_telegram_individual_address(frame):
+    """Test telegram conversion flags with a individual address"""
+    frame.telegram = Telegram(destination_address=IndividualAddress(0))
 
     assert (
         frame.flags & CEMIFlags.DESTINATION_INDIVIDUAL_ADDRESS
diff --git a/test/knxip_tests/dib_test.py b/test/knxip_tests/dib_test.py
index ca9cab23..40701c2c 100644
--- a/test/knxip_tests/dib_test.py
+++ b/test/knxip_tests/dib_test.py
@@ -11,7 +11,7 @@ from xknx.knxip import (
     DIBTypeCode,
     KNXMedium,
 )
-from xknx.telegram import PhysicalAddress
+from xknx.telegram import IndividualAddress
 
 
 class Test_KNXIP_DIB(unittest.TestCase):
@@ -98,7 +98,7 @@ class Test_KNXIP_DIB(unittest.TestCase):
         self.assertEqual(dib.from_knx(raw), DIBDeviceInformation.LENGTH)
         self.assertEqual(dib.knx_medium, KNXMedium.TP1)
         self.assertEqual(dib.programming_mode, False)
-        self.assertEqual(dib.individual_address, PhysicalAddress("1.1.0"))
+        self.assertEqual(dib.individual_address, IndividualAddress("1.1.0"))
         self.assertEqual(dib.name, "Gira KNX/IP-Router")
         self.assertEqual(dib.mac_address, "00:01:02:03:04:05")
         self.assertEqual(dib.multicast_address, "224.0.23.12")
diff --git a/test/knxip_tests/routing_indication_test.py b/test/knxip_tests/routing_indication_test.py
index 6ee944a7..fed7dda7 100644
--- a/test/knxip_tests/routing_indication_test.py
+++ b/test/knxip_tests/routing_indication_test.py
@@ -6,7 +6,7 @@ import unittest
 from xknx import XKNX
 from xknx.dpt import DPTArray, DPTBinary, DPTTemperature, DPTTime
 from xknx.knxip import CEMIFrame, KNXIPFrame, KNXIPServiceType, RoutingIndication
-from xknx.telegram import GroupAddress, PhysicalAddress, Telegram, TelegramType
+from xknx.telegram import GroupAddress, IndividualAddress, Telegram, TelegramType
 
 
 class Test_KNXIP(unittest.TestCase):
@@ -33,7 +33,7 @@ class Test_KNXIP(unittest.TestCase):
         self.assertTrue(isinstance(knxipframe.body, RoutingIndication))
         self.assertTrue(isinstance(knxipframe.body.cemi, CEMIFrame))
 
-        self.assertEqual(knxipframe.body.cemi.src_addr, PhysicalAddress("1.2.2"))
+        self.assertEqual(knxipframe.body.cemi.src_addr, IndividualAddress("1.2.2"))
         self.assertEqual(knxipframe.body.cemi.dst_addr, GroupAddress(337))
 
         self.assertEqual(len(knxipframe.body.cemi.payload.value), 1)
@@ -57,7 +57,7 @@ class Test_KNXIP(unittest.TestCase):
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.init(KNXIPServiceType.ROUTING_INDICATION)
-        knxipframe.body.cemi.src_addr = PhysicalAddress("1.2.2")
+        knxipframe.body.cemi.src_addr = IndividualAddress("1.2.2")
 
         telegram = Telegram(
             destination_address=GroupAddress(337),
@@ -110,13 +110,13 @@ class Test_KNXIP(unittest.TestCase):
             Telegram(
                 destination_address=GroupAddress("329"),
                 payload=DPTBinary(1),
-                source_address=PhysicalAddress("15.15.249"),
+                source_address=IndividualAddress("15.15.249"),
             ),
         )
 
         knxipframe2 = KNXIPFrame(xknx)
         knxipframe2.init(KNXIPServiceType.ROUTING_INDICATION)
-        knxipframe2.body.cemi.src_addr = PhysicalAddress("15.15.249")
+        knxipframe2.body.cemi.src_addr = IndividualAddress("15.15.249")
         knxipframe2.body.cemi.telegram = telegram
         knxipframe2.body.cemi.set_hops(5)
         knxipframe2.normalize()
@@ -138,13 +138,13 @@ class Test_KNXIP(unittest.TestCase):
             Telegram(
                 destination_address=GroupAddress("329"),
                 payload=DPTBinary(0),
-                source_address=PhysicalAddress("15.15.249"),
+                source_address=IndividualAddress("15.15.249"),
             ),
         )
 
         knxipframe2 = KNXIPFrame(xknx)
         knxipframe2.init(KNXIPServiceType.ROUTING_INDICATION)
-        knxipframe2.body.cemi.src_addr = PhysicalAddress("15.15.249")
+        knxipframe2.body.cemi.src_addr = IndividualAddress("15.15.249")
         knxipframe2.body.cemi.telegram = telegram
         knxipframe2.body.cemi.set_hops(5)
         knxipframe2.normalize()
@@ -166,13 +166,13 @@ class Test_KNXIP(unittest.TestCase):
             Telegram(
                 destination_address=GroupAddress("331"),
                 payload=DPTArray(0x65),
-                source_address=PhysicalAddress("15.15.249"),
+                source_address=IndividualAddress("15.15.249"),
             ),
         )
 
         knxipframe2 = KNXIPFrame(xknx)
         knxipframe2.init(KNXIPServiceType.ROUTING_INDICATION)
-        knxipframe2.body.cemi.src_addr = PhysicalAddress("15.15.249")
+        knxipframe2.body.cemi.src_addr = IndividualAddress("15.15.249")
         knxipframe2.body.cemi.telegram = telegram
         knxipframe2.body.cemi.set_hops(5)
         knxipframe2.normalize()
@@ -194,13 +194,13 @@ class Test_KNXIP(unittest.TestCase):
             Telegram(
                 destination_address=GroupAddress("2049"),
                 payload=DPTArray(DPTTemperature().to_knx(19.85)),
-                source_address=PhysicalAddress("1.4.2"),
+                source_address=IndividualAddress("1.4.2"),
             ),
         )
 
         knxipframe2 = KNXIPFrame(xknx)
         knxipframe2.init(KNXIPServiceType.ROUTING_INDICATION)
-        knxipframe2.body.cemi.src_addr = PhysicalAddress("1.4.2")
+        knxipframe2.body.cemi.src_addr = IndividualAddress("1.4.2")
         knxipframe2.body.cemi.telegram = telegram
         knxipframe2.body.cemi.set_hops(5)
         knxipframe2.normalize()
@@ -222,13 +222,13 @@ class Test_KNXIP(unittest.TestCase):
             Telegram(
                 destination_address=GroupAddress("440"),
                 telegramtype=TelegramType.GROUP_READ,
-                source_address=PhysicalAddress("15.15.249"),
+                source_address=IndividualAddress("15.15.249"),
             ),
         )
 
         knxipframe2 = KNXIPFrame(xknx)
         knxipframe2.init(KNXIPServiceType.ROUTING_INDICATION)
-        knxipframe2.body.cemi.src_addr = PhysicalAddress("15.15.249")
+        knxipframe2.body.cemi.src_addr = IndividualAddress("15.15.249")
         knxipframe2.body.cemi.telegram = telegram
         knxipframe2.body.cemi.set_hops(5)
         knxipframe2.normalize()
@@ -251,13 +251,13 @@ class Test_KNXIP(unittest.TestCase):
                 destination_address=GroupAddress("392"),
                 telegramtype=TelegramType.GROUP_RESPONSE,
                 payload=DPTBinary(1),
-                source_address=PhysicalAddress("1.3.1"),
+                source_address=IndividualAddress("1.3.1"),
             ),
         )
 
         knxipframe2 = KNXIPFrame(xknx)
         knxipframe2.init(KNXIPServiceType.ROUTING_INDICATION)
-        knxipframe2.body.cemi.src_addr = PhysicalAddress("1.3.1")
+        knxipframe2.body.cemi.src_addr = IndividualAddress("1.3.1")
         knxipframe2.body.cemi.telegram = telegram
         knxipframe2.body.cemi.set_hops(5)
         knxipframe2.normalize()
@@ -271,12 +271,12 @@ class Test_KNXIP(unittest.TestCase):
         telegram = Telegram(
             destination_address=GroupAddress(337),
             payload=DPTBinary(DPTBinary.APCI_MAX_VALUE),
-            source_address=PhysicalAddress("1.3.1"),
+            source_address=IndividualAddress("1.3.1"),
         )
         xknx = XKNX()
         knxipframe = KNXIPFrame(xknx)
         knxipframe.init(KNXIPServiceType.ROUTING_INDICATION)
-        knxipframe.body.cemi.src_addr = PhysicalAddress("1.3.1")
+        knxipframe.body.cemi.src_addr = IndividualAddress("1.3.1")
         knxipframe.body.cemi.telegram = telegram
         knxipframe.body.cemi.set_hops(5)
         knxipframe.normalize()
diff --git a/test/str_test.py b/test/str_test.py
index 43e9f339..7972402f 100644
--- a/test/str_test.py
+++ b/test/str_test.py
@@ -55,7 +55,7 @@ from xknx.knxip import (
     TunnellingRequest,
 )
 from xknx.remote_value import RemoteValue
-from xknx.telegram import GroupAddress, PhysicalAddress, Telegram, TelegramDirection
+from xknx.telegram import GroupAddress, IndividualAddress, Telegram, TelegramDirection
 
 
 # pylint: disable=too-many-public-methods,invalid-name
@@ -473,8 +473,8 @@ class TestStringRepresentations(unittest.TestCase):
         )
         self.assertEqual(
             str(telegram),
-            '<Telegram direction="Outgoing" telegramtype="GroupValueWrite" source_address="PhysicalAddress("0.0.0")" '
-            'destination_address="GroupAddress("1/2/3")" payload="<DPTBinary value="7" />" />',
+            '<Telegram direction="Outgoing" telegramtype="GroupValueWrite" source_address="0.0.0" '
+            'destination_address="1/2/3" payload="<DPTBinary value="7" />" />',
         )
 
     def test_dib_generic(self):
@@ -501,7 +501,7 @@ class TestStringRepresentations(unittest.TestCase):
         dib = DIBDeviceInformation()
         dib.knx_medium = KNXMedium.TP1
         dib.programming_mode = False
-        dib.individual_address = PhysicalAddress("1.1.0")
+        dib.individual_address = IndividualAddress("1.1.0")
         dib.name = "Gira KNX/IP-Router"
         dib.mac_address = "00:01:02:03:04:05"
         dib.multicast_address = "224.0.23.12"
@@ -641,7 +641,7 @@ class TestStringRepresentations(unittest.TestCase):
         tunnelling_request.sequence_counter = 42
         self.assertEqual(
             str(tunnelling_request),
-            '<TunnellingRequest communication_channel_id="23" sequence_counter="42" cemi="<CEMIFrame SourceAddress="PhysicalAddress("0.0.0")"'
+            '<TunnellingRequest communication_channel_id="23" sequence_counter="42" cemi="<CEMIFrame SourceAddress="IndividualAddress("0.0.0")"'
             ' DestinationAddress="GroupAddress("0/0/0")" Flags="               0" Command="APCICommand.GROUP_READ" payload="None" />" />',
         )
 
@@ -710,5 +710,5 @@ class TestStringRepresentations(unittest.TestCase):
         ri = RoutingIndication(xknx)
         self.assertEqual(
             str(ri),
-            '<RoutingIndication cemi="<CEMIFrame SourceAddress="PhysicalAddress("0.0.0")" DestinationAddress="GroupAddress("0/0/0")" Flags="               0" Command="APCICommand.GROUP_READ" payload="None" />" />',
+            '<RoutingIndication cemi="<CEMIFrame SourceAddress="IndividualAddress("0.0.0")" DestinationAddress="GroupAddress("0/0/0")" Flags="               0" Command="APCICommand.GROUP_READ" payload="None" />" />',
         )
diff --git a/test/telegram_tests/address_test.py b/test/telegram_tests/address_test.py
index 15bb73f6..88338dce 100644
--- a/test/telegram_tests/address_test.py
+++ b/test/telegram_tests/address_test.py
@@ -2,11 +2,11 @@
 from unittest import TestCase
 
 from xknx.exceptions import CouldNotParseAddress
-from xknx.telegram import GroupAddress, GroupAddressType, PhysicalAddress
+from xknx.telegram import GroupAddress, GroupAddressType, IndividualAddress
 
 
-class TestPhysicalAddress(TestCase):
-    """Test class for PhysicalAddress."""
+class TestIndividualAddress(TestCase):
+    """Test class for IndividualAddress."""
 
     def test_with_valid(self):
         """Test with some valid addresses."""
@@ -20,7 +20,7 @@ class TestPhysicalAddress(TestCase):
             ("1.1.111", 4463),
             ("1.11.111", 7023),
             ("11.11.111", 47983),
-            (PhysicalAddress("11.11.111"), 47983),
+            (IndividualAddress("11.11.111"), 47983),
             ("15.15.255", 65535),
             ((0xFF, 0xFF), 65535),
             (0, 0),
@@ -28,7 +28,7 @@ class TestPhysicalAddress(TestCase):
         )
         for address in valid_addresses:
             with self.subTest(address=address):
-                self.assertEqual(PhysicalAddress(address[0]).raw, address[1])
+                self.assertEqual(IndividualAddress(address[0]).raw, address[1])
 
     def test_with_invalid(self):
         """Test with some invalid addresses."""
@@ -48,48 +48,48 @@ class TestPhysicalAddress(TestCase):
         for address in invalid_addresses:
             with self.subTest(address=address):
                 with self.assertRaises(CouldNotParseAddress):
-                    PhysicalAddress(address)
+                    IndividualAddress(address)
 
     def test_with_int(self):
         """Test initialization with free format address as integer."""
-        self.assertEqual(PhysicalAddress(49552).raw, 49552)
+        self.assertEqual(IndividualAddress(49552).raw, 49552)
 
     def test_with_bytes(self):
         """Test initialization with Bytes."""
-        self.assertEqual(PhysicalAddress((0x12, 0x34)).raw, 0x1234)
+        self.assertEqual(IndividualAddress((0x12, 0x34)).raw, 0x1234)
 
     def test_with_none(self):
         """Test initialization with None object."""
-        self.assertEqual(PhysicalAddress(None).raw, 0)
+        self.assertEqual(IndividualAddress(None).raw, 0)
 
     def test_is_line(self):
-        """Test if `PhysicalAddress.is_line` works like excepted."""
-        self.assertTrue(PhysicalAddress("1.0.0").is_line)
-        self.assertFalse(PhysicalAddress("1.0.1").is_line)
+        """Test if `IndividualAddress.is_line` works like excepted."""
+        self.assertTrue(IndividualAddress("1.0.0").is_line)
+        self.assertFalse(IndividualAddress("1.0.1").is_line)
 
     def test_is_device(self):
-        """Test if `PhysicalAddress.is_device` works like excepted."""
-        self.assertTrue(PhysicalAddress("1.0.1").is_device)
-        self.assertFalse(PhysicalAddress("1.0.0").is_device)
+        """Test if `IndividualAddress.is_device` works like excepted."""
+        self.assertTrue(IndividualAddress("1.0.1").is_device)
+        self.assertFalse(IndividualAddress("1.0.0").is_device)
 
     def test_to_knx(self):
-        """Test if `PhysicalAddress.to_knx()` generates valid byte tuples."""
-        self.assertEqual(PhysicalAddress("0.0.0").to_knx(), (0x0, 0x0))
-        self.assertEqual(PhysicalAddress("15.15.255").to_knx(), (0xFF, 0xFF))
+        """Test if `IndividualAddress.to_knx()` generates valid byte tuples."""
+        self.assertEqual(IndividualAddress("0.0.0").to_knx(), (0x0, 0x0))
+        self.assertEqual(IndividualAddress("15.15.255").to_knx(), (0xFF, 0xFF))
 
     def test_equal(self):
         """Test if the equal operator works in all cases."""
-        self.assertEqual(PhysicalAddress("1.0.0"), PhysicalAddress(4096))
-        self.assertNotEqual(PhysicalAddress("1.0.0"), PhysicalAddress("1.1.1"))
-        self.assertNotEqual(PhysicalAddress("1.0.0"), None)
+        self.assertEqual(IndividualAddress("1.0.0"), IndividualAddress(4096))
+        self.assertNotEqual(IndividualAddress("1.0.0"), IndividualAddress("1.1.1"))
+        self.assertNotEqual(IndividualAddress("1.0.0"), None)
         with self.assertRaises(TypeError):
-            PhysicalAddress(
+            IndividualAddress(
                 "1.0.0"
             ) == "example"  # pylint: disable=expression-not-assigned
 
     def test_representation(self):
         """Test string representation of address."""
-        self.assertEqual(repr(PhysicalAddress("2.3.4")), 'PhysicalAddress("2.3.4")')
+        self.assertEqual(repr(IndividualAddress("2.3.4")), 'IndividualAddress("2.3.4")')
 
 
 class TestGroupAddress(TestCase):