File: dpt_8.py

package info (click to toggle)
python-xknx 3.10.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 4,044 kB
  • sloc: python: 40,087; javascript: 8,556; makefile: 32; sh: 12
file content (152 lines) | stat: -rw-r--r-- 3,696 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
"""
Implementation of Basic KNX 2-Byte Signed Values.

They correspond the following KNX DPTs:
    8.*** 2-byte/octet signed (2's complement), i.e. percentV16, delta time
"""

from __future__ import annotations

import struct

from xknx.exceptions import ConversionError

from .dpt import DPTNumeric
from .payload import DPTArray, DPTBinary


class DPT2ByteSigned(DPTNumeric):
    """
    Abstraction for KNX 2 Byte signed values.

    DPT 8.***
    """

    dpt_main_number = 8
    dpt_sub_number: int | None = None
    value_type = "2byte_signed"
    payload_length = 2

    value_min = -32768
    value_max = 32767
    resolution: int | float = 1

    # not using DPTStructIntMixin because return type of from_knx can be float when resolution is < 1
    _struct_format = ">h"

    @classmethod
    def from_knx(cls, payload: DPTArray | DPTBinary) -> int | float:
        """Parse/deserialize from KNX/IP raw data."""
        raw = cls.validate_payload(payload)

        try:
            return struct.unpack(cls._struct_format, bytes(raw))[0] * cls.resolution  # type: ignore[no-any-return]
        except struct.error as err:
            raise ConversionError(f"Could not parse {cls.dpt_name()}", raw=raw) from err

    @classmethod
    def to_knx(cls, value: int | float) -> DPTArray:
        """Serialize to KNX/IP raw data."""
        try:
            knx_value = int(float(value) / cls.resolution)
            if not (cls.value_min <= knx_value <= cls.value_max):
                raise ValueError("Value out of range")
            return DPTArray(struct.pack(cls._struct_format, knx_value))
        except (ValueError, struct.error) as err:
            raise ConversionError(
                f"Could not serialize {cls.dpt_name()}", value=value
            ) from err


class DPTValue2Count(DPT2ByteSigned):
    """DPT 8.001 DPT_Value_2_Count (pulses)."""

    dpt_main_number = 8
    dpt_sub_number = 1
    value_type = "pulse_2byte_signed"
    unit = "pulses"


class DPTDeltaTimeMsec(DPT2ByteSigned):
    """DPT 8.002 DPT_DeltaTimeMsec (ms)."""

    dpt_main_number = 8
    dpt_sub_number = 2
    value_type = "delta_time_ms"
    unit = "ms"


class DPTDeltaTime10Msec(DPT2ByteSigned):
    """DPT 8.003 DPT_DeltaTime10Msec (ms)."""

    dpt_main_number = 8
    dpt_sub_number = 3
    value_type = "delta_time_10ms"
    resolution = 10
    unit = "ms"


class DPTDeltaTime100Msec(DPT2ByteSigned):
    """DPT 8.004 DPT_DeltaTime100Msec (ms)."""

    dpt_main_number = 8
    dpt_sub_number = 4
    value_type = "delta_time_100ms"
    resolution = 100
    unit = "ms"


class DPTDeltaTimeSec(DPT2ByteSigned):
    """DPT 8.005 DPT_DeltaTimeSec (s)."""

    dpt_main_number = 8
    dpt_sub_number = 5
    value_type = "delta_time_sec"
    unit = "s"


class DPTDeltaTimeMin(DPT2ByteSigned):
    """DPT 8.006 DPT_DeltaTimeMin (min)."""

    dpt_main_number = 8
    dpt_sub_number = 6
    value_type = "delta_time_min"
    unit = "min"


class DPTDeltaTimeHrs(DPT2ByteSigned):
    """DPT 8.007 DPT_DeltaTimeHrs (h)."""

    dpt_main_number = 8
    dpt_sub_number = 7
    value_type = "delta_time_hrs"
    unit = "h"


class DPTPercentV16(DPT2ByteSigned):
    """DPT 8.010 DPT_Percent_V16 (%)."""

    dpt_main_number = 8
    dpt_sub_number = 10
    value_type = "percentV16"
    resolution = 0.01
    unit = "%"


class DPTRotationAngle(DPT2ByteSigned):
    """DPT 8.011 DPT_Rotation_Angle (°)."""

    dpt_main_number = 8
    dpt_sub_number = 11
    value_type = "rotation_angle"
    unit = "°"


class DPTLengthM(DPT2ByteSigned):
    """DPT 8.012 DPT_Length_m."""

    dpt_main_number = 8
    dpt_sub_number = 12
    value_type = "length_m"
    unit = "m"
    ha_device_class = "distance"