예제 #1
0
 def test_slow_ramp(self):
     """test very slow ramps"""
     p1 = PointToMultipointPacket(
         sals=LightingRampSAL(1, 18 * 60, 255)).encode_packet()
     p2 = PointToMultipointPacket(
         sals=LightingRampSAL(1, 17 * 60, 255)).encode_packet()
     self.assertEqual(p1, p2)
예제 #2
0
    def lighting_group_off(self, group_addr: Union[int, Iterable[int]]):
        """
        Turns off the lights for the given group_id.

        :param group_addr: Group address(es) to turn the lights off for, up to
                           9
        :type group_addr: int, or iterable of ints of length <= 9.

        :returns: Single-byte string with code for the confirmation event.
        :rtype: string

        """
        if not isinstance(group_addr, Iterable):
            group_addr = [group_addr]

        group_addr = [int(g) for g in group_addr]
        group_addr_count = len(group_addr)

        if group_addr_count > 9:
            # maximum 9 group addresses per packet
            raise ValueError(
                f'group_addr iterable length is > 9 ({group_addr_count})')

        p = PointToMultipointPacket(
            sals=[LightingOffSAL(ga) for ga in group_addr])
        return self._send(p)
예제 #3
0
    def lighting_group_ramp(self,
                            group_addr: int,
                            duration: int,
                            level: int = 255):
        """
        Ramps (fades) a group address to a specified lighting level.

        Note: CBus only supports a limited number of fade durations, in
        decreasing accuracy up to 17 minutes (1020 seconds). Durations
        longer than this will throw an error.

        A duration of 0 will ramp "instantly" to the given level.

        :param group_addr: The group address to ramp.
        :type group_addr: int
        :param duration: Duration, in seconds, that the ramp should occur over.
        :type duration: int
        :param level: A value between 0 and 255 indicating the brightness.
        :type level: int

        :returns: Single-byte string with code for the confirmation event.
        :rtype: string

        """
        p = PointToMultipointPacket(
            sals=LightingRampSAL(group_addr, duration, level))
        return self._send(p)
예제 #4
0
    def lighting_group_terminate_ramp(self, group_addr):
        """
		Stops ramping a group address at the current point.
		
		:param group_addr: Group address to stop ramping of.
		:type group_addr: int
		
		:returns: Single-byte string with code for the confirmation event.
		:rtype: string
		"""

        if not isinstance(group_addr, Iterable):
            group_addr = [group_addr]

        group_addr = [int(g) for g in group_addr]

        if len(group_addr) > 9:
            # maximum 9 group addresses per packet
            raise ValueError, 'group_addr iterable length is > 9 (%r)' % len(
                group_addr)

        p = PointToMultipointPacket(application=APP_LIGHTING)
        for ga in group_addr:
            p.sal.append(LightingTerminateRampSAL(p, ga))

        return self._send(p)
예제 #5
0
	def lighting_group_ramp(self, source_addr, group_addr, duration, level=1.0):
		"""
		Ramps (fades) a group address to a specified lighting level.

		Note: CBus only supports a limited number of fade durations, in decreasing
		accuracy up to 17 minutes (1020 seconds).  Durations longer than this will
		throw an error.
		
		A duration of 0 will ramp "instantly" to the given level.

		:param source_addr: Source address of the event.
		:type source_addr: int

		:param group_id: The group address to ramp.
		:type group_id: int
		
		:param duration: Duration, in seconds, that the ramp should occur over.
		:type duration: int
		
		:param level: An amount between 0.0 and 1.0 indicating the brightness to set.
		:type level: float
		
		:returns: Single-byte string with code for the confirmation event.
		:rtype: string
		
		"""
		p = PointToMultipointPacket(application=APP_LIGHTING)
		p.source_address = source_addr
		p.sal.append(LightingRampSAL(p, group_addr, duration, level))
		p.checksum = self.checksum
		return self._send(p)
예제 #6
0
    def lighting_group_off(self, group_addr):
        """
		Turns off the lights for the given group_id.
		
		:param group_addr: Group address(es) to turn the lights off for, up to 9.
		:type group_addr: int, or iterable of ints of length <= 9.
		
		:returns: Single-byte string with code for the confirmation event.
		:rtype: string
		
		"""
        if not isinstance(group_addr, Iterable):
            group_addr = [group_addr]

        group_addr = [int(g) for g in group_addr]

        if len(group_addr) > 9:
            # maximum 9 group addresses per packet
            raise ValueError, 'group_addr iterable length is > 9 (%r)' % len(
                group_addr)

        p = PointToMultipointPacket(application=APP_LIGHTING)
        for ga in group_addr:
            p.sal.append(LightingOffSAL(p, ga))

        return self._send(p)
예제 #7
0
    def lighting_group_terminate_ramp(self, group_addr: Union[int,
                                                              Iterable[int]]):
        """
        Stops ramping a group address at the current point.

        :param group_addr: Group address to stop ramping of.
        :type group_addr: int

        :returns: Single-byte string with code for the confirmation event.
        :rtype: string
        """

        if not isinstance(group_addr, Iterable):
            group_addr = [group_addr]

        group_addr = [int(g) for g in group_addr]
        group_addr_count = len(group_addr)

        if group_addr_count > 9:
            # maximum 9 group addresses per packet
            raise ValueError(
                f'group_addr iterable length is > 9 ({group_addr_count})')

        p = PointToMultipointPacket(
            sals=[LightingTerminateRampSAL(ga) for ga in group_addr])
        return self._send(p)
예제 #8
0
    def test_s23_13_1(self):
        """Example in s23.13.1 of decoding a time."""
        # Set network time to 10:43:23 with no DST offset
        expected_time = time(10, 43, 23)
        # Slight change from guide:
        p = self.decode_pm(b'\\05DF000D010A2B1700C2g\r', from_pci=False)
        self.assertEqual(len(p), 1)

        s = p[0]
        self.assertIsInstance(s, ClockUpdateSAL)
        self.assertTrue(s.is_time)
        self.assertFalse(s.is_date)
        self.assertEqual(s.val, expected_time)

        # Library doesn't handle DST offset, so this flag is dropped.

        # check that it encodes properly again
        # fuzzy match to allow packet that has no DST information
        self.assertIn(p.encode_packet(),
                      [b'05DF000D010A2B1700C2', b'05DF000D010A2B17FFC3'])
        self.assertEqual(p.confirmation, b'g')

        # check that the same value would encode
        p = PointToMultipointPacket(sals=clock_update_sal(expected_time))
        self.assertIn(p.encode_packet(),
                      [b'05DF000D010A2B1700C2', b'05DF000D010A2B17FFC3'])
예제 #9
0
    def test_invalid_sal(self):
        p = PointToMultipointPacket()
        with self.assertRaisesRegex(ValueError, 'application .+ None'):
            p.encode_packet()

        p.application = 0x100
        with self.assertRaisesRegex(ValueError, 'application .+ in range'):
            p.encode_packet()
예제 #10
0
 def test_invalid_multiple_application_sal(self):
     """Argument validation - SALs from different applications."""
     with self.assertRaisesRegex(
             ValueError, r'SAL .+ of application ff, .+ has application 38'):
         PointToMultipointPacket(sals=[
             LightingOffSAL(1),
             StatusRequestSAL(level_request=True, group_address=1,
                              child_application=Application.LIGHTING),
         ])
예제 #11
0
    def test_lighting_encode_decode_client(self):
        """test of encode then decode, with packets from a client"""

        orig = PointToMultipointPacket(sals=LightingOnSAL(27))

        data = b'\\' + orig.encode_packet() + b'\r'

        d = self.decode_pm(data, from_pci=False)
        self.assertEqual(len(orig), len(d))

        self.assertIsInstance(d[0], LightingOnSAL)
        self.assertEqual(orig[0].group_address, d[0].group_address)
예제 #12
0
    def clock_datetime(self, when: Optional[datetime] = None):
        """
        Sends the system's local time to the CBus network.

        :param when: The time and date to send to the CBus network. Defaults
                     to current local time.
        :type when: datetime.datetime

        """
        if when is None:
            when = datetime.now()

        p = PointToMultipointPacket(sals=clock_update_sal(when))
        return self._send(p)
예제 #13
0
    def test_lighting_encode_decode(self):
        """test of encode then decode"""

        orig = PointToMultipointPacket(sals=LightingOnSAL(27))
        orig.source_address = 5

        data = orig.encode_packet() + b'\r\n'

        d = self.decode_pm(data)
        self.assertEqual(orig.source_address, d.source_address)
        self.assertEqual(len(orig), len(d))

        self.assertIsInstance(d[0], LightingOnSAL)
        self.assertEqual(orig[0].group_address, d[0].group_address)
예제 #14
0
    def on_clock_update(self, val):
        """
        Event called when a clock application "update time" is recieved.

        :param variable: Clock variable to update.
        :type variable: int

        :param val: Clock value
        :type variable: datetime.date or datetime.time
        """
        logger.debug("recv: clock update: %r" % val)

        # DEBUG: randomly trigger lights
        p = PointToMultipointPacket(self.checksum,
                                    sals=LightingOnSAL(random.randint(1, 100)))
        p.source_address = random.randint(1, 100)

        self._send_later(p)

        p = PointToMultipointPacket(self.checksum,
                                    sals=LightingOffSAL(random.randint(1,
                                                                       100)))
        p.source_address = random.randint(1, 100)
        self._send_later(p)
예제 #15
0
def lighting_encode_decode_client_test():
    "self-made tests of encode then decode, with packets from a client."

    orig = PointToMultipointPacket(application=APP_LIGHTING)
    orig.sal.append(LightingOnSAL(orig, 27))

    data = orig.encode()

    d, r = decode_packet(data, server_packet=False)
    assert isinstance(orig, PointToMultipointPacket)
    assert len(orig.sal) == len(d.sal)

    assert isinstance(d.sal[0], LightingOnSAL)
    assert orig.sal[0].group_address == d.sal[0].group_address

    # ensure there is no remaining data to be parsed
    assert r == None
예제 #16
0
    def clock_datetime(self, when=None):
        """
		Sends the system's local time to the CBus network.

		:param when: The time and date to send to the CBus network.  Defaults to current local time.
		:type when: datetime.datetime
		
		"""
        if when == None:
            when = datetime.now()

        p = PointToMultipointPacket(application=APP_CLOCK)

        p.sal.append(ClockUpdateSAL(p, CLOCK_DATE, when.date()))
        p.sal.append(ClockUpdateSAL(p, CLOCK_TIME, when.time()))

        return self._send(p)
예제 #17
0
    def lighting_group_terminate_ramp(self, source_addr, group_addr):
        """
        Stops ramping a group address at the current point.

        :param source_addr: Source address of the event.
        :type source_addr: int

        :param group_addr: Group address to stop ramping of.
        :type group_addr: int

        :returns: Single-byte string with code for the confirmation event.
        :rtype: string
        """
        p = PointToMultipointPacket(checksum=self.checksum,
                                    sals=LightingTerminateRampSAL(group_addr))
        p.source_address = source_addr
        return self._send(p)
예제 #18
0
    def test_remove_sals(self):
        # create a packet
        p = PointToMultipointPacket(sals=LightingOffSAL(1))
        self.assertEqual(1, len(p))

        p.clear_sal()
        self.assertEqual(0, len(p))

        # We should be able to add a different app
        p.append_sal(StatusRequestSAL(level_request=True, group_address=1,
                                      child_application=Application.LIGHTING))
        self.assertEqual(1, len(p))

        # Adding another lighting SAL should fail
        with self.assertRaisesRegex(ValueError, r'has application ff$'):
            p.append_sal(LightingOffSAL(1))
        self.assertEqual(1, len(p))
예제 #19
0
    def lighting_group_off(self, source_addr, group_addr):
        """
        Turns off the lights for the given group_addr.

        :param source_addr: Source address of the event.
        :type source_addr: int

        :param group_addr: Group address to turn the lights on for.
        :type group_addr: int

        :returns: Single-byte string with code for the confirmation event.
        :rtype: string

        """
        p = PointToMultipointPacket(checksum=self.checksum,
                                    sals=LightingOffSAL(group_addr))
        p.source_address = source_addr
        return self._send(p)
예제 #20
0
    def test_datetime_object(self):
        moment = datetime(2019, 12, 31, 23, 59, 13)
        p = PointToMultipointPacket(sals=clock_update_sal(moment))

        p = self.decode_pm(b'\\' + p.encode_packet() + b'g\r', from_pci=False)
        d = t = None
        self.assertEqual(2, len(p))
        for sal in p:
            self.assertIsInstance(sal, ClockUpdateSAL)
            if sal.is_time and not sal.is_date:
                self.assertIsNone(t)
                t = sal.val
            elif sal.is_date and not sal.is_time:
                self.assertIsNone(d)
                d = sal.val

        self.assertEqual(moment.date(), d)
        self.assertEqual(moment.time(), t)
예제 #21
0
    def test_temperature_encode_decode(self):
        """self-made tests of encode then decode"""

        orig = PointToMultipointPacket(sals=[
            TemperatureBroadcastSAL(10, 0.5),
            TemperatureBroadcastSAL(11, 56)
        ])
        orig.source_address = 5
        data = orig.encode_packet() + b'\r\n'

        d = self.decode_pm(data)
        self.assertIsInstance(orig, PointToMultipointPacket)
        self.assertEqual(orig.source_address, d.source_address)
        self.assertEqual(len(orig), len(d))

        for x in range(len(d)):
            self.assertIsInstance(d[x], TemperatureBroadcastSAL)
            self.assertEqual(orig[x].group_address, d[x].group_address)
            self.assertEqual(orig[x].temperature, d[x].temperature)
예제 #22
0
	def lighting_group_off(self, source_addr, group_addr):
		"""
		Turns off the lights for the given group_id.
		
		:param source_addr: Source address of the event.
		:type source_addr: int	
		
		:param group_id: Group address to turn the lights on for.
		:type group_id: int
		
		:returns: Single-byte string with code for the confirmation event.
		:rtype: string
				
		"""
		p = PointToMultipointPacket(application=APP_LIGHTING)
		p.source_address = source_addr
		p.sal.append(LightingOffSAL(p, group_addr))
		p.checksum = self.checksum
		return self._send(p)
예제 #23
0
    def test_s23_13_2(self):
        """Example in s23.13.2 of decoding a date."""
        # Set network date to 2005-02-25 (Friday)
        expected_date = date(2005, 2, 25)
        p = self.decode_pm(b'\\05DF000E0207D502190411g\r', from_pci=False)
        self.assertEqual(len(p), 1)

        s = p[0]
        self.assertIsInstance(s, ClockUpdateSAL)
        self.assertTrue(s.is_date)
        self.assertFalse(s.is_time)
        self.assertEqual(s.val, expected_date)

        # check that it encodes properly again
        self.assertEqual(p.encode_packet(), b'05DF000E0207D502190411')
        self.assertEqual(p.confirmation, b'g')

        # check that the same value would encode
        p = PointToMultipointPacket(sals=clock_update_sal(expected_date))
        self.assertEqual(p.encode_packet(), b'05DF000E0207D502190411')
예제 #24
0
def temperature_encode_decode_test():
    "self-made tests of encode then decode"

    orig = PointToMultipointPacket(application=APP_TEMPERATURE)
    orig.source_address = 5
    orig.sal.append(TemperatureBroadcastSAL(orig, 10, 0.5))
    orig.sal.append(TemperatureBroadcastSAL(orig, 11, 56))

    data = orig.encode()

    d, r = decode_packet(data)
    assert isinstance(orig, PointToMultipointPacket)
    assert orig.source_address == d.source_address
    assert len(orig.sal) == len(d.sal)

    for x in range(len(d.sal)):
        assert isinstance(d.sal[x], TemperatureBroadcastSAL)
        assert orig.sal[x].group_address == d.sal[x].group_address
        assert orig.sal[x].temperature == d.sal[x].temperature

    # ensure there is no remaining data to be parsed
    assert r == None
예제 #25
0
 def test_invalid_ga(self):
     """test argument validation"""
     with self.assertRaises(ValueError):
         PointToMultipointPacket(sals=LightingOnSAL(999))
     with self.assertRaises(ValueError):
         PointToMultipointPacket(sals=LightingOffSAL(-1))