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
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)
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, )
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
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, )
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, 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, )
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, )
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, )
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)):
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}")
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) ):
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)
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")
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
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) ):
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 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.")