diff --git a/enocean/protocol/EEP.xml b/enocean/protocol/EEP.xml index b66aef1..7998c58 100644 --- a/enocean/protocol/EEP.xml +++ b/enocean/protocol/EEP.xml @@ -187,7 +187,7 @@ - + 0 @@ -241,6 +241,60 @@ + + + + 0 + 100 + + + 0 + 100 + + + + + 0 + 255 + + + 0 + 40 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/enocean/protocol/eep.py b/enocean/protocol/eep.py index 152684f..a9f9445 100644 --- a/enocean/protocol/eep.py +++ b/enocean/protocol/eep.py @@ -11,7 +11,6 @@ class EEP(object): - _profile = None _data_description = None def __init__(self): @@ -89,6 +88,7 @@ def _set_raw(self, target, raw_value, bitarray): return bitarray def _set_value(self, target, value, bitarray): + ''' set given numeric value to target field in bitarray ''' # derive raw value rng = target.find('range') rng_min = float(rng.find('min').text) @@ -101,23 +101,31 @@ def _set_value(self, target, value, bitarray): return self._set_raw(target, int(raw_value), bitarray) def _set_enum(self, target, value, bitarray): - if isinstance(value, int): - raise ValueError('No integers here, use the description string provided in EEP.') - + ''' set given enum value (by string or integer value) to target field in bitarray ''' # derive raw value - value_item = target.find('item', {'description': value}) - if value_item is None: - raise ValueError('Enum description for value "%s" not found in EEP.' % (value)) - raw_value = int(value_item['value']) + if isinstance(value, int): + # check whether this value exists + if target.find('item', {'value': value}): + # set integer values directly + raw_value = value + else: + raise ValueError('Enum value "%s" not found in EEP.' % (value)) + else: + value_item = target.find('item', {'description': value}) + if value_item is None: + raise ValueError('Enum description for value "%s" not found in EEP.' % (value)) + raw_value = int(value_item['value']) return self._set_raw(target, raw_value, bitarray) def _set_boolean(self, target, data, bitarray): + ''' set given value to target bit in bitarray ''' bitarray[int(target['offset'])] = data return bitarray - def find_profile(self, rorg, func, type): + def find_profile(self, rorg, func, type, direction=None): ''' Find profile and data description, matching RORG, FUNC and TYPE ''' if not self.ok: + logging.warning("Not ready.") return False rorg = self.soup.find('telegram', {'rorg': self._get_hex(rorg)}) @@ -135,11 +143,13 @@ def find_profile(self, rorg, func, type): logger.warn('Cannot find type in EEP!') return False - # store identified profile - self._profile = profile - # extract data description - self._data_description = self._profile.find('data') + # the direction tag is optional + if direction is None: + self._data_description = profile.find('data') + else: + self._data_description = profile.find('data', {'direction': direction}) + if not self._data_description: logger.warn('Cannot find data description in EEP!') self._data_description = [] @@ -151,7 +161,7 @@ def get_values(self, bitarray, status): if not self.ok: return [], {} - if not self._profile or not self._data_description: + if not self._data_description: return [], {} output = {} @@ -171,7 +181,7 @@ def set_values(self, rorg, data, status, properties): if not self.ok: return data, status - if not self._profile or not self._data_description: + if not self._data_description: return data, status for property, value in properties.items(): diff --git a/enocean/protocol/packet.py b/enocean/protocol/packet.py index c2380ea..423d133 100644 --- a/enocean/protocol/packet.py +++ b/enocean/protocol/packet.py @@ -144,7 +144,7 @@ def parse_msg(buf): return PARSE_RESULT.OK, buf, p @staticmethod - def create(packet_type, rorg, func, type, + def create(packet_type, rorg, func, type, direction=None, destination=[0xFF, 0xFF, 0xFF, 0xFF], sender=[0xDE, 0xAD, 0xBE, 0xEF], learn=False, **kwargs): @@ -187,7 +187,7 @@ def create(packet_type, rorg, func, type, # and no security (security not supported as per EnOcean Serial Protocol). p.optional = [3] + destination + [0xFF] + [0] - p.select_eep(func, type) + p.select_eep(func, type, direction) p.set_eep(kwargs) if rorg in [RORG.BS1, RORG.BS4] and not learn: if rorg == RORG.BS1: @@ -200,7 +200,7 @@ def create(packet_type, rorg, func, type, # For example, stuff like checking RadioPacket.learn should be set. p = Packet.parse_msg(p.build())[2] p.rorg = rorg - p.parse_eep(func, type) + p.parse_eep(func, type, direction) return p def parse(self): @@ -217,18 +217,18 @@ def parse(self): self.repeater_count = self._from_bitarray(self.bit_status[4:]) return self.parsed - def select_eep(self, func, type): + def select_eep(self, func, type, direction=None): ''' Set EEP based on FUNC and TYPE ''' # set EEP profile self.rorg_func = func self.rorg_type = type - return self.eep.find_profile(self.rorg, func, type) + return self.eep.find_profile(self.rorg, func, type, direction) - def parse_eep(self, func=None, type=None): + def parse_eep(self, func=None, type=None, direction=None): ''' Parse EEP based on FUNC and TYPE ''' # set EEP profile, if demanded if func is not None and type is not None: - self.select_eep(func, type) + self.select_eep(func, type, direction) # parse data provides, values = self.eep.get_values(self.bit_data, self.bit_status) self.parsed.update(values) @@ -269,11 +269,11 @@ def __str__(self): return '%s->%s (%d dBm): %s' % (self.sender_hex, self.destination_hex, self.dBm, packet_str) @staticmethod - def create(rorg, func, type, + def create(rorg, func, type, direction=None, destination=[0xFF, 0xFF, 0xFF, 0xFF], sender=[0xDE, 0xAD, 0xBE, 0xEF], learn=False, **kwargs): - return Packet.create(PACKET.RADIO, rorg, func, type, destination, sender, learn, **kwargs) + return Packet.create(PACKET.RADIO, rorg, func, type, direction, destination, sender, learn, **kwargs) def parse(self): self.destination = self._combine_hex(self.optional[1:5]) diff --git a/tests/test_eep.py b/tests/test_eep.py index 863768e..d4cf2f1 100644 --- a/tests/test_eep.py +++ b/tests/test_eep.py @@ -112,3 +112,18 @@ def test_eep_parsing(): assert p.rorg_type == 0x05 assert p.status == 0x00 assert p.repeater_count == 0 + + +def test_eep_direction(): + status, buf, p = Packet.parse_msg(bytearray([ + 0x55, + 0x00, 0x0A, 0x07, 0x01, + 0xEB, + 0xA5, 0x32, 0x20, 0x89, 0x00, 0xDE, 0xAD, 0xBE, 0xEF, 0x00, + 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x43 + ])) + assert sorted(p.parse_eep(0x20, 0x01, 1)) == ['ACO', 'BCAP', 'CCO', 'CV', 'DWO', 'ENIE', 'ES', 'FTS', 'SO', 'TMP'] + assert p.parsed['CV']['value'] == 50 + assert sorted(p.parse_eep(0x20, 0x01, 2)) == ['LFS', 'RCU', 'RIN', 'SB', 'SP', 'SPI', 'SPS', 'TMP', 'VC', 'VO'] + assert p.parsed['SP']['value'] == 50 diff --git a/tests/test_packet_creation.py b/tests/test_packet_creation.py index b9433e9..ce963fa 100644 --- a/tests/test_packet_creation.py +++ b/tests/test_packet_creation.py @@ -1,5 +1,6 @@ # -*- encoding: utf-8 -*- from __future__ import print_function, unicode_literals, division +from nose.tools import raises from enocean.protocol.packet import Packet, RadioPacket from enocean.protocol.constants import PACKET, RORG @@ -29,6 +30,14 @@ def test_packet_assembly(): 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x43 ]) + PACKET_CONTENT_4 = bytearray([ + 0x55, + 0x00, 0x0A, 0x07, 0x01, + 0xEB, + 0xA5, 0x32, 0x00, 0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF, 0x00, + 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x80 + ]) # manually assemble packet p = Packet(PACKET.RADIO) @@ -55,7 +64,7 @@ def test_packet_assembly(): assert list(packet_serialized) == list(PACKET_CONTENT_2) # update data based on EEP - p.select_eep(0x20, 0x01) + p.select_eep(0x20, 0x01, 1) prop = { 'CV': 50, 'TMP': 21.5, @@ -71,7 +80,7 @@ def test_packet_assembly(): assert p.rorg_type == 0x01 # Test the easier method of sending packets. - p = Packet.create(PACKET.RADIO, rorg=RORG.BS4, func=0x20, learn=True, type=0x01, **prop) + p = Packet.create(PACKET.RADIO, rorg=RORG.BS4, func=0x20, learn=True, type=0x01, direction=1, **prop) packet_serialized = p.build() assert len(packet_serialized) == len(PACKET_CONTENT_3) assert list(packet_serialized) == list(PACKET_CONTENT_3) @@ -79,10 +88,10 @@ def test_packet_assembly(): assert p.rorg_type == 0x01 # Test creating RadioPacket directly. - p = RadioPacket.create(rorg=RORG.BS4, func=0x20, learn=True, type=0x01, **prop) + p = RadioPacket.create(rorg=RORG.BS4, func=0x20, learn=True, type=0x01, direction=2, SP=50) packet_serialized = p.build() - assert len(packet_serialized) == len(PACKET_CONTENT_3) - assert list(packet_serialized) == list(PACKET_CONTENT_3) + assert len(packet_serialized) == len(PACKET_CONTENT_4) + assert list(packet_serialized) == list(PACKET_CONTENT_4) assert p.rorg_func == 0x20 assert p.rorg_type == 0x01 @@ -188,9 +197,10 @@ def test_switch(): 0x61 ]) + # test also enum setting by integer value with EB0 p = RadioPacket.create(rorg=RORG.RPS, func=0x02, type=0x02, sender=[0x00, 0x29, 0x89, 0x79], SA='No 2nd action', - EBO='pressed', + EBO=1, R1='Button BI', T21=True, NU=True, @@ -217,3 +227,16 @@ def test_switch(): packet_serialized = p.build() assert len(packet_serialized) == len(SWITCH) assert list(packet_serialized) == list(SWITCH) + + +@raises(ValueError) +def test_illegal_eep_enum1(): + p = RadioPacket.create(rorg=RORG.RPS, func=0x02, type=0x02, sender=[0x00, 0x29, 0x89, 0x79], + EBO='inexisting', + ) + +@raises(ValueError) +def test_illegal_eep_enum2(): + p = RadioPacket.create(rorg=RORG.RPS, func=0x02, type=0x02, sender=[0x00, 0x29, 0x89, 0x79], + EBO=2, + )