Skip to content

Commit

Permalink
Add return_code for TunnellingFeatureResponse (#1600)
Browse files Browse the repository at this point in the history
  • Loading branch information
farmio authored Nov 10, 2024
1 parent e98b284 commit efef1b6
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 3 deletions.
5 changes: 4 additions & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ nav_order: 2

# Changelog

- Added rate limit (in packets per second) option to P2PConnection.
### Management

- Add rate limit (in packets per second) option to P2PConnection.
- Fix typo in management procedure (`nm_invididual_address_write` was renamed to `nm_individual_address_write`)
- Fix TunnellingFeatureResponse missing `return_code`

# 3.3.0 Climate humidity 2024-10-20

Expand Down
4 changes: 3 additions & 1 deletion test/knxip_tests/tunnelling_feature_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
TunnellingFeatureSet,
TunnellingFeatureType,
)
from xknx.management.application_layer_enum import ReturnCode


class TestKNXIPTunnellingFeature:
Expand Down Expand Up @@ -74,13 +75,14 @@ def test_response(self):
assert (
knxipframe.body.feature_type == TunnellingFeatureType.BUS_CONNECTION_STATUS
)
assert knxipframe.body.return_code == ReturnCode.E_SUCCESS
assert knxipframe.body.data == b"\x01\x00"

tunnelling_request = TunnellingFeatureResponse(
communication_channel_id=1,
sequence_counter=23,
status_code=ErrorCode.E_NO_ERROR,
feature_type=TunnellingFeatureType.BUS_CONNECTION_STATUS,
return_code=ReturnCode.E_SUCCESS,
data=b"\x01\x00",
)
knxipframe2 = KNXIPFrame.init_from_body(tunnelling_request)
Expand Down
2 changes: 1 addition & 1 deletion test/str_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ def test_tunnelling_feature_response(self):
assert (
str(tunnelling_feature)
== '<TunnellingFeatureResponse communication_channel_id="23" sequence_counter="42" status_code="ErrorCode.E_NO_ERROR" '
'feature_type="TunnellingFeatureType.BUS_CONNECTION_STATUS" data="0100" />'
'feature_type="TunnellingFeatureType.BUS_CONNECTION_STATUS" return_code="ReturnCode.E_SUCCESS" data="0100" />'
)

def test_tunnelling_feature_set(self):
Expand Down
49 changes: 49 additions & 0 deletions xknx/knxip/tunnelling_feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import struct

from xknx.exceptions import CouldNotParseKNXIP
from xknx.management.application_layer_enum import ReturnCode

from .body import KNXIPBody, KNXIPBodyResponse
from .error_code import ErrorCode
Expand Down Expand Up @@ -162,6 +163,53 @@ class TunnellingFeatureResponse(_TunnellingFeature, KNXIPBodyResponse):

SERVICE_TYPE = KNXIPServiceType.TUNNELLING_FEATURE_RESPONSE

def __init__(
self,
communication_channel_id: int = 1,
sequence_counter: int = 0,
feature_type: TunnellingFeatureType = TunnellingFeatureType.SUPPORTED_EMI_TYPE,
return_code: ReturnCode = ReturnCode.E_SUCCESS,
data: bytes = b"",
):
"""Initialize TunnellingFeatureSet object."""
super().__init__(
communication_channel_id=communication_channel_id,
sequence_counter=sequence_counter,
feature_type=feature_type,
data=data,
)
self.return_code = return_code

def from_knx(self, raw: bytes) -> int:
"""Parse/deserialize from KNX/IP raw data."""
if raw[0] != _TunnellingFeature.HEADER_LENGTH: # structure_length field
raise CouldNotParseKNXIP("TunnellingFeature header has invalid length")
self.communication_channel_id = raw[1]
self.sequence_counter = raw[2]
self.status_code = ErrorCode(raw[3])
self.feature_type = TunnellingFeatureType(raw[4])
self.return_code = ReturnCode(raw[5])
self.data = raw[6:]
if self.return_code is ReturnCode.E_SUCCESS and len(self.data) == 0:
# Data may be omitted by some servers when an error occurred
raise CouldNotParseKNXIP("TunnellingFeature missing data.")
return len(raw)

def to_knx(self) -> bytes:
"""Serialize to KNX/IP raw data."""
data_size = len(self.data) + (len(self.data) % 2) if self._has_data() else 0
return struct.pack(
# Append a NUL byte if data size is uneven
f"!BBBBBB{data_size}s",
_TunnellingFeature.HEADER_LENGTH,
self.communication_channel_id,
self.sequence_counter,
self.status_code.value,
self.feature_type.value,
self.return_code.value,
self.data,
)

def __repr__(self) -> str:
"""Return object as readable string."""
return (
Expand All @@ -170,5 +218,6 @@ def __repr__(self) -> str:
f'sequence_counter="{self.sequence_counter}" '
f'status_code="{self.status_code}" '
f'feature_type="{self.feature_type}" '
f'return_code="{self.return_code}" '
f'data="{self.data.hex()}" />'
)
46 changes: 46 additions & 0 deletions xknx/management/application_layer_enum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""Enums for KNX Application Layer."""

from enum import Enum


class ReturnCode(Enum):
"""Enum class for Generic device management Return Codes."""

## Basic positive Return Code
# The service, function or command is executed successfully, without additional information.
E_SUCCESS = 0x00
## Generic negative Return Codes
# Memory cannot be accessed or only with fault(s).
E_MEMORY_ERROR = 0xF1
# Requested data will not fit into a Frame supported by this server.
# This shall be used for Device limitations of the maximum supported Frame length
# by accessing resources (Properties, Function Properties, memory…) of the device.
E_LENGTH_EXCEEDS_MAX_APDU_LENGTH = 0xF4
# Writing data beyond what is reserved for the addressed Resource.
E_DATA_OVERFLOW = 0xF5
# Write value too low. Preferable to give this instead of “Value not supported”.
E_DATA_MIN = 0xF6
# Write value too high. Preferable to give this instead of “Value not supported”.
E_DATA_MAX = 0xF7
# The service or function is supported, but request data is not valid for this receiver.
E_DATA_VOID = 0xF8
# Data could generally be written, but not possible at this time.
E_TEMPORARILY_NOT_AVAILABLE = 0xF9
# Read access attempted to a “write only” service or Resource.
E_ACCESS_WRITE_ONLY = 0xFA
# Write access attempted to a “read only” service or Resource.
E_ACCESS_READ_ONLY = 0xFB
# Access denied due to authorization reasons. A_Authorize as well as KNX Security
E_ACCESS_DENIED = 0xFC
# Interface Object or Property is not present, or index is out of range.
E_ADDRESS_VOID = 0xFD
# Write access with a wrong datatype (Datapoint length).
E_DATA_TYPE_CONFLICT = 0xFE
# The service, function or command has failed without a closer indication of the problem.
E_ERROR = 0xFF
## Generic positive Return Codes
# (01h-1Fh - None proposed)
## Specific positive Return Codes
# (20h-5Fh - None proposed)
## Specific negative Return Codes
# (A0h-DFh - None proposed)

0 comments on commit efef1b6

Please sign in to comment.