Exemple #1
0
    def test_parse_transcoder_from_subclass(self):
        """Test parsing only subclasses of a DPT class."""
        assert DPTBase.parse_transcoder("string") == DPTString
        assert DPTNumeric.parse_transcoder("string") is None
        assert DPT2ByteFloat.parse_transcoder("string") is None

        assert DPTBase.parse_transcoder("percent") == DPTScaling
        assert DPTNumeric.parse_transcoder("percent") == DPTScaling
        assert DPT2ByteFloat.parse_transcoder("percent") is None

        assert DPTBase.parse_transcoder("temperature") == DPTTemperature
        assert DPTNumeric.parse_transcoder("temperature") == DPTTemperature
        assert DPT2ByteFloat.parse_transcoder("temperature") == DPTTemperature
Exemple #2
0
 def __init__(
     self,
     xknx,
     group_address=None,
     group_address_state=None,
     sync_state=True,
     value_type=None,
     device_name=None,
     feature_name="Value",
     after_update_cb=None,
 ):
     """Initialize RemoteValueSensor class."""
     # pylint: disable=too-many-arguments
     _dpt_class = DPTBase.parse_transcoder(value_type)
     if _dpt_class is None:
         raise ConversionError("invalid value type",
                               value_type=value_type,
                               device_name=device_name)
     self.dpt_class = _dpt_class
     super().__init__(
         xknx,
         group_address,
         group_address_state,
         sync_state=sync_state,
         device_name=device_name,
         feature_name=feature_name,
         after_update_cb=after_update_cb,
     )
Exemple #3
0
 def test_dpt_subclasses_no_duplicate_dpt_number(self):
     """Test for duplicate value_type values in subclasses of DPTBase."""
     dpt_tuples = []
     for dpt in DPTBase.__recursive_subclasses__():
         if dpt.dpt_main_number is not None and dpt.dpt_sub_number is not None:
             dpt_tuples.append((dpt.dpt_main_number, dpt.dpt_sub_number))
     assert len(dpt_tuples) == len(set(dpt_tuples))
Exemple #4
0
 def test_dpt_subclasses_no_duplicate_value_types(self):
     """Test for duplicate value_type values in subclasses of DPTBase."""
     value_types = []
     for dpt in DPTBase.__recursive_subclasses__():
         if hasattr(dpt, "value_type"):
             value_types.append(dpt.value_type)
     self.assertCountEqual(value_types, set(value_types))
 def __init__(
     self,
     xknx: "XKNX",
     group_address: Optional["GroupAddressableType"] = None,
     group_address_state: Optional["GroupAddressableType"] = None,
     sync_state: bool = True,
     value_type: Optional[str] = None,
     device_name: Optional[str] = None,
     feature_name: str = "Value",
     after_update_cb: Optional[AsyncCallbackType] = None,
     passive_group_addresses: Optional[List["GroupAddressableType"]] = None,
 ):
     """Initialize RemoteValueSensor class."""
     # pylint: disable=too-many-arguments
     if value_type is None:
         raise ConversionError("no value type given",
                               device_name=device_name)
     _dpt_class = DPTBase.parse_transcoder(value_type)
     if _dpt_class is None:
         raise ConversionError("invalid value type",
                               value_type=value_type,
                               device_name=device_name)
     self.dpt_class = _dpt_class
     super().__init__(
         xknx,
         group_address,
         group_address_state,
         sync_state=sync_state,
         device_name=device_name,
         feature_name=feature_name,
         after_update_cb=after_update_cb,
         passive_group_addresses=passive_group_addresses,
     )
 def __init__(
     self,
     xknx: "XKNX",
     group_address: Optional["GroupAddressableType"] = None,
     group_address_state: Optional["GroupAddressableType"] = None,
     sync_state: bool = True,
     value_type: Optional[str] = None,
     device_name: Optional[str] = None,
     feature_name: str = "Control",
     after_update_cb: Optional[AsyncCallbackType] = None,
     passive_group_addresses: Optional[List["GroupAddressableType"]] = None,
 ):
     """Initialize control remote value."""
     # pylint: disable=too-many-arguments
     if value_type is None:
         raise ConversionError("no value type given", device_name=device_name)
     # TODO: typing - parse from DPTControlStepCode when parse_transcoder is a classmethod
     _dpt_class = DPTBase.parse_transcoder(value_type)
     if _dpt_class is None or not isinstance(_dpt_class(), DPTControlStepCode):
         raise ConversionError(
             "invalid value type", value_type=value_type, device_name=device_name
         )
     self.dpt_class: Type[DPTControlStepCode] = _dpt_class  # type: ignore
     super().__init__(
         xknx,
         group_address,
         group_address_state,
         sync_state=sync_state,
         device_name=device_name,
         feature_name=feature_name,
         after_update_cb=after_update_cb,
         passive_group_addresses=passive_group_addresses,
     )
Exemple #7
0
    async def service_send_to_knx_bus(self, call: ServiceCall) -> None:
        """Service for sending an arbitrary KNX message to the KNX bus."""
        attr_address = call.data[KNX_ADDRESS]
        attr_payload = call.data[SERVICE_KNX_ATTR_PAYLOAD]
        attr_type = call.data.get(SERVICE_KNX_ATTR_TYPE)
        attr_response = call.data[SERVICE_KNX_ATTR_RESPONSE]

        payload: DPTBinary | DPTArray
        if attr_type is not None:
            transcoder = DPTBase.parse_transcoder(attr_type)
            if transcoder is None:
                raise ValueError(
                    f"Invalid type for knx.send service: {attr_type}")
            payload = DPTArray(transcoder.to_knx(attr_payload))
        elif isinstance(attr_payload, int):
            payload = DPTBinary(attr_payload)
        else:
            payload = DPTArray(attr_payload)

        for address in attr_address:
            telegram = Telegram(
                destination_address=parse_device_group_address(address),
                payload=GroupValueResponse(payload)
                if attr_response else GroupValueWrite(payload),
            )
            await self.xknx.telegrams.put(telegram)
Exemple #8
0
 def test_dpt_subclasses_have_both_dpt_number_attributes(self):
     """Test DPTBase subclasses for having both dpt number attributes set."""
     for dpt in DPTBase.__recursive_subclasses__():
         if hasattr(dpt, "dpt_main_number"):
             self.assertTrue(
                 hasattr(dpt, "dpt_sub_number"), "No dpt_sub_number in %s" % dpt
             )
Exemple #9
0
 def __init__(
     self,
     xknx: XKNX,
     group_address: GroupAddressesType | None = None,
     group_address_state: GroupAddressesType | None = None,
     sync_state: bool = True,
     value_type: int | str | None = None,
     device_name: str | None = None,
     feature_name: str = "Value",
     after_update_cb: AsyncCallbackType | None = None,
 ):
     """Initialize RemoteValueSensor class."""
     if value_type is None:
         raise ConversionError("no value type given",
                               device_name=device_name)
     _dpt_class = DPTBase.parse_transcoder(value_type)
     if _dpt_class is None:
         raise ConversionError("invalid value type",
                               value_type=value_type,
                               device_name=device_name)
     self.dpt_class = _dpt_class
     super().__init__(
         xknx,
         group_address,
         group_address_state,
         sync_state=sync_state,
         device_name=device_name,
         feature_name=feature_name,
         after_update_cb=after_update_cb,
     )
Exemple #10
0
 def test_dpt_subclasses_no_duplicate_dpt_number(self):
     """Test for duplicate value_type values in subclasses of DPTBase."""
     dpt_tuples = []
     for dpt in DPTBase.__recursive_subclasses__():
         if hasattr(dpt, "dpt_main_number") and hasattr(dpt, "dpt_sub_number"):
             dpt_tuples.append((dpt.dpt_main_number, dpt.dpt_sub_number))
     self.assertCountEqual(dpt_tuples, set(dpt_tuples))
 def __init__(
     self,
     xknx,
     group_address=None,
     group_address_state=None,
     sync_state=True,
     value_type=None,
     device_name=None,
     feature_name="Control",
     after_update_cb=None,
     invert=False,
     passive_group_addresses: List[str] = None,
 ):
     """Initialize control remote value."""
     # pylint: disable=too-many-arguments
     self.invert = invert
     _dpt_class = DPTBase.parse_transcoder(value_type)
     if _dpt_class is None:
         raise ConversionError("invalid value type",
                               value_type=value_type,
                               device_name=device_name)
     self.dpt_class = _dpt_class
     super().__init__(
         xknx,
         group_address,
         group_address_state,
         sync_state=sync_state,
         device_name=device_name,
         feature_name=feature_name,
         after_update_cb=after_update_cb,
         passive_group_addresses=passive_group_addresses,
     )
Exemple #12
0
def ensure_knx_dpt(value: str) -> str:
    """Ensure value is a valid KNX DPT."""
    dpt_class = DPTBase.parse_transcoder(value)
    if dpt_class is None:
        raise vol.Invalid(f"{value!r} is not a valid KNX DPT")

    return value
Exemple #13
0
 def test_dpt_subclasses_no_duplicate_value_types(self):
     """Test for duplicate value_type values in subclasses of DPTBase."""
     value_types = []
     for dpt in DPTBase.__recursive_subclasses__():
         if dpt.value_type is not None:
             value_types.append(dpt.value_type)
     assert len(value_types) == len(set(value_types))
 def __init__(
     self,
     xknx: XKNX,
     group_address: GroupAddressesType | None = None,
     group_address_state: GroupAddressesType | None = None,
     sync_state: bool = True,
     value_type: str | None = None,
     device_name: str | None = None,
     feature_name: str = "Control",
     after_update_cb: AsyncCallbackType | None = None,
 ):
     """Initialize control remote value."""
     if value_type is None:
         raise ConversionError("no value type given",
                               device_name=device_name)
     # TODO: typing - parse from DPTControlStepCode when parse_transcoder is a classmethod
     _dpt_class = DPTBase.parse_transcoder(value_type)
     if _dpt_class is None or not isinstance(_dpt_class(),
                                             DPTControlStepCode):
         raise ConversionError("invalid value type",
                               value_type=value_type,
                               device_name=device_name)
     self.dpt_class: type[DPTControlStepCode] = _dpt_class  # type: ignore
     super().__init__(
         xknx,
         group_address,
         group_address_state,
         sync_state=sync_state,
         device_name=device_name,
         feature_name=feature_name,
         after_update_cb=after_update_cb,
     )
Exemple #15
0
 def register_event_callback(self) -> TelegramQueue.Callback:
     """Register callback for knx_event within XKNX TelegramQueue."""
     address_filters = []
     for filter_set in self.config[CONF_EVENT]:
         _filters = list(map(AddressFilter, filter_set[KNX_ADDRESS]))
         address_filters.extend(_filters)
         if (dpt := filter_set.get(CONF_TYPE)) and (
                 transcoder := DPTBase.parse_transcoder(dpt)):
Exemple #16
0
def sensor_value_type(value: T) -> T:
    """Validate sensor type."""
    if DPTBase.parse_transcoder(value):
        return value

    if value.lower() in ["binary", "time", "datetime", "date"]:
        return str(value)

    raise vol.Invalid(f"invalid value type {value}")
Exemple #17
0
 def calculate_payload(attr_payload):
     """Calculate payload depending on type of attribute."""
     if attr_type is not None:
         transcoder = DPTBase.parse_transcoder(attr_type)
         if transcoder is None:
             raise ValueError(f"Invalid type for knx.send service: {attr_type}")
         return DPTArray(transcoder.to_knx(attr_payload))
     if isinstance(attr_payload, int):
         return DPTBinary(attr_payload)
     return DPTArray(attr_payload)
 def register_event_callback(self) -> TelegramQueue.Callback:
     """Register callback for knx_event within XKNX TelegramQueue."""
     # backwards compatibility for deprecated CONF_KNX_EVENT_FILTER
     # use `address_filters = []` when this is not needed anymore
     address_filters = list(map(AddressFilter, self.config[CONF_KNX_EVENT_FILTER]))
     for filter_set in self.config[CONF_EVENT]:
         _filters = list(map(AddressFilter, filter_set[KNX_ADDRESS]))
         address_filters.extend(_filters)
         if (dpt := filter_set.get(CONF_TYPE)) and (
             transcoder := DPTBase.parse_transcoder(dpt)
         ):
Exemple #19
0
 def calculate_payload(attr_payload):
     """Calculate payload depending on type of attribute."""
     if attr_type:
         try:
             transcoder = DPTBase.parse_transcoder(attr_type)
             return DPTArray(transcoder.to_knx(attr_payload))
         except AttributeError as ex:
             _LOGGER.error("Invalid type for knx.send service: %s",
                           attr_type)
             raise ex
     if isinstance(attr_payload, int):
         return DPTBinary(attr_payload)
     return DPTArray(attr_payload)
Exemple #20
0
 def test_dpt_alternative_notations(self):
     """Test the parser for accepting alternateive notations for the same DPT class."""
     dpt1 = DPTBase.parse_transcoder("2byte_unsigned")
     dpt2 = DPTBase.parse_transcoder(7)
     dpt3 = DPTBase.parse_transcoder("DPT-7")
     self.assertEqual(dpt1, dpt2)
     self.assertEqual(dpt2, dpt3)
     dpt4 = DPTBase.parse_transcoder("temperature")
     dpt5 = DPTBase.parse_transcoder(9.001)
     dpt6 = DPTBase.parse_transcoder("9.001")
     self.assertEqual(dpt4, dpt5)
     self.assertEqual(dpt5, dpt6)
     dpt7 = DPTBase.parse_transcoder("active_energy")
     dpt8 = DPTBase.parse_transcoder(13.010)
     self.assertEqual(dpt7, dpt8, "parsing float failed")
Exemple #21
0
 def test_dpt_alternative_notations(self):
     """Test the parser for accepting alternateive notations for the same DPT class."""
     dpt1 = DPTBase.parse_transcoder("2byte_unsigned")
     dpt2 = DPTBase.parse_transcoder(7)
     dpt3 = DPTBase.parse_transcoder("DPT-7")
     assert dpt1 == dpt2
     assert dpt2 == dpt3
     dpt4 = DPTBase.parse_transcoder("temperature")
     dpt5 = DPTBase.parse_transcoder("9.001")
     assert dpt4 == dpt5
     dpt7 = DPTBase.parse_transcoder("active_energy")
     dpt8 = DPTBase.parse_transcoder("13.010")
     assert dpt7 == dpt8
Exemple #22
0
 def test_dpt_subclasses_definition_types(self):
     """Test value_type and dpt_*_number values for correct type in subclasses of DPTBase."""
     for dpt in DPTBase.__recursive_subclasses__():
         if dpt.value_type is not None:
             assert isinstance(
                 dpt.value_type, str
             ), f"Wrong type for value_type in {dpt} : {type(dpt.value_type)} - str `None` expected"
         if dpt.dpt_main_number is not None:
             assert isinstance(
                 dpt.dpt_main_number, int
             ), f"Wrong type for dpt_main_number in {dpt} : {type(dpt.dpt_main_number)} - int or `None` expected"
         if dpt.dpt_sub_number is not None:
             assert isinstance(
                 dpt.dpt_sub_number, int
             ), f"Wrong type for dpt_sub_number in {dpt} : {type(dpt.dpt_sub_number)} - int or `None` expected"
def print_table():
    """Read the values and print the table to stdout."""
    rows = []
    for dpt in DPTBase.__recursive_subclasses__():
        if dpt.has_distinct_value_type():
            rows.append(DPTRow(dpt_class=dpt))

    rows.sort(key=lambda row: row.dpt_number_int())

    table_header = Row()
    rows.insert(0, table_header)

    # Insert at last to have correct column_widths.
    rows.insert(1, table_delimiter())

    for row in rows:
        print(row)
Exemple #24
0
 def test_dpt_subclasses_definition_types(self):
     """Test value_type and dpt_*_number values for correct type in subclasses of DPTBase."""
     for dpt in DPTBase.__recursive_subclasses__():
         if hasattr(dpt, 'value_type'):
             self.assertTrue(
                 isinstance(dpt.value_type, str),
                 msg="Wrong type for value_type in %s - str expected" % dpt)
         if hasattr(dpt, 'dpt_main_number'):
             self.assertTrue(
                 isinstance(dpt.dpt_main_number, int),
                 msg="Wrong type for dpt_main_number in %s - int expected" %
                 dpt)
         if hasattr(dpt, 'dpt_sub_number'):
             self.assertTrue(
                 isinstance(dpt.dpt_sub_number, (int, type(None))),
                 msg=
                 "Wrong type for dpt_sub_number in %s - int or `None` expected"
                 % dpt)
    async def service_event_register_modify(self, call: ServiceCall) -> None:
        """Service for adding or removing a GroupAddress to the knx_event filter."""
        attr_address = call.data[KNX_ADDRESS]
        group_addresses = list(map(parse_device_group_address, attr_address))

        if call.data.get(SERVICE_KNX_ATTR_REMOVE):
            for group_address in group_addresses:
                try:
                    self._knx_event_callback.group_addresses.remove(group_address)
                except ValueError:
                    _LOGGER.warning(
                        "Service event_register could not remove event for '%s'",
                        str(group_address),
                    )
                if group_address in self._group_address_transcoder:
                    del self._group_address_transcoder[group_address]
            return

        if (dpt := call.data.get(CONF_TYPE)) and (
            transcoder := DPTBase.parse_transcoder(dpt)
        ):
Exemple #26
0
 def test_dpt_subclasses_definition_types(self):
     """Test value_type and dpt_*_number values for correct type in subclasses of DPTBase."""
     for dpt in DPTBase.__recursive_subclasses__():
         if dpt.value_type is not None:
             self.assertTrue(
                 isinstance(dpt.value_type, str),
                 msg=
                 "Wrong type for value_type in %s : %s - str `None` expected"
                 % (dpt, type(dpt.value_type)),
             )
         if dpt.dpt_main_number is not None:
             self.assertTrue(
                 isinstance(dpt.dpt_main_number, int),
                 msg=
                 "Wrong type for dpt_main_number in %s : %s - int or `None` expected"
                 % (dpt, type(dpt.dpt_main_number)),
             )
         if dpt.dpt_sub_number is not None:
             self.assertTrue(
                 isinstance(dpt.dpt_sub_number, int),
                 msg=
                 "Wrong type for dpt_sub_number in %s : %s - int or `None` expected"
                 % (dpt, type(dpt.dpt_sub_number)),
             )
Exemple #27
0
 def test_dpt_abstract_subclasses_ignored(self):
     """Test if abstract base classes are ignored by __recursive_subclasses__."""
     for dpt in DPTBase.__recursive_subclasses__():
         assert dpt is not DPTNumeric
Exemple #28
0
def sensor_type_validator(value: Any) -> str | int:
    """Validate that value is parsable as sensor type."""
    if isinstance(value, (str, int)) and DPTBase.parse_transcoder(value) is not None:
        return value
    raise vol.Invalid(f"value '{value}' is not a valid sensor type.")
Exemple #29
0
def button_payload_sub_validator(entity_config: OrderedDict) -> OrderedDict:
    """Validate a button entity payload configuration."""
    if _type := entity_config.get(CONF_TYPE):
        _payload = entity_config[ButtonSchema.CONF_VALUE]
        if (transcoder := DPTBase.parse_transcoder(_type)) is None:
            raise vol.Invalid(f"'type: {_type}' is not a valid sensor type.")
 def test_payload_length_defined(self):
     """Test if all members of DPTMAP implement payload_length."""
     for dpt_class in DPTBase.__recursive_subclasses__():
         assert isinstance(dpt_class.payload_length, int)