Esempio n. 1
0
    async def var_abs(
        self,
        var: lcn_defs.Var,
        value_or_float: Union[float, lcn_defs.VarValue],
        unit: lcn_defs.VarUnit = lcn_defs.VarUnit.NATIVE,
        software_serial: Optional[int] = None,
    ) -> bool:
        """Send a command to set the absolute value to a variable.

        :param     Var        var:      Variable
        :param     float      value:    Absolute value to set
        :param     VarUnit    unit:     Unit of variable

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      bool
        """
        if isinstance(value_or_float, lcn_defs.VarValue):
            value = value_or_float
        else:
            value = lcn_defs.VarValue.from_var_unit(value_or_float, unit, True)

        if software_serial is None:
            await self.serial_known
            software_serial = self.software_serial

        if lcn_defs.Var.to_var_id(var) != -1:
            # Absolute commands for variables 1-12 are not supported
            if self.addr_id == 4 and self.is_group:
                # group 4 are status messages
                return await self.send_command(
                    not self.is_group,
                    PckGenerator.update_status_var(var, value.to_native()),
                )
            # We fake the missing command by using reset and relative
            # commands.
            success = await self.send_command(
                not self.is_group, PckGenerator.var_reset(var, software_serial)
            )
            if not success:
                return False
            return await self.send_command(
                not self.is_group,
                PckGenerator.var_rel(
                    var, lcn_defs.RelVarRef.CURRENT, value.to_native(), software_serial
                ),
            )
        return await self.send_command(
            not self.is_group, PckGenerator.var_abs(var, value.to_native())
        )
Esempio n. 2
0
 async def timeout(self, failed: bool = False, block_id: int = 0) -> None:
     """Is called on OEM text request timeout."""
     if not failed:
         await self.addr_conn.send_command(
             False, PckGenerator.request_oem_text(block_id))
     else:
         self.oem_text_known.set()
Esempio n. 3
0
    async def send_keys_hit_deferred(
            self, keys: List[List[bool]], delay_time: int,
            delay_unit: lcn_defs.TimeUnit) -> List[bool]:
        """Send a command to send keys deferred.

        :param    list(bool)[4][8]    keys:          2d-list with
                                                     [table_id][key_id] bool
                                                     values, if command should
                                                     be sent to specific key
        :param    int                 delay_time:    Delay time
        :param    TimeUnit            delay_unit:    Unit of time

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      list of bool
        """
        results: List[bool] = []
        for table_id, key_states in enumerate(keys):
            if True in key_states:
                results.append(
                    await self.send_command(
                        not self.is_group,
                        PckGenerator.send_keys_hit_deferred(
                            table_id, delay_time, delay_unit, key_states),
                    ), )
        return results
Esempio n. 4
0
 async def timeout(self, failed: bool = False) -> None:
     """Is called on dynamic group membership request timeout."""
     if not failed:
         await self.addr_conn.send_command(
             False, PckGenerator.request_group_membership_dynamic())
     else:
         self.groups_known.set()
Esempio n. 5
0
    async def var_rel(
        self,
        var: lcn_defs.Var,
        value: Union[float, lcn_defs.VarValue],
        unit: lcn_defs.VarUnit = lcn_defs.VarUnit.NATIVE,
        value_ref: lcn_defs.RelVarRef = lcn_defs.RelVarRef.CURRENT,
        software_serial: Optional[int] = None,
    ) -> bool:
        """Send a command to change the value of a variable.

        :param     Var        var:      Variable
        :param     float      value:    Relative value to add (may also be
                                        negative)
        :param     VarUnit    unit:     Unit of variable

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      bool
        """
        if not isinstance(value, lcn_defs.VarValue):
            value = lcn_defs.VarValue.from_var_unit(value, unit, True)

        if software_serial is None:
            await self.serial_known
            software_serial = self.software_serial

        return await self.send_command(
            not self.is_group,
            PckGenerator.var_rel(var, value_ref, value.to_native(),
                                 software_serial),
        )
Esempio n. 6
0
 async def request_status_outputs_timeout(self,
                                          failed: bool = False,
                                          output_port: int = 0) -> None:
     """Is called on output status request timeout."""
     if not failed:
         await self.addr_conn.send_command(
             False, PckGenerator.request_output_status(output_port))
Esempio n. 7
0
    async def send_keys(
        self, keys: List[List[bool]], cmd: lcn_defs.SendKeyCommand
    ) -> List[bool]:
        """Send a command to send keys.

        :param    list(bool)[4][8]    keys:    2d-list with [table_id][key_id]
                                               bool values, if command should
                                               be sent to specific key
        :param    SendKeyCommand      cmd:     command to send for each table

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      list of bool
        """
        coros = []
        for table_id, key_states in enumerate(keys):
            if True in key_states:
                cmds = [lcn_defs.SendKeyCommand.DONTSEND] * 4
                cmds[table_id] = cmd
                coros.append(
                    self.send_command(
                        not self.is_group, PckGenerator.send_keys(cmds, key_states)
                    )
                )
        results = await asyncio.gather(*coros)
        return results
Esempio n. 8
0
 async def timeout(self, failed: bool = False) -> None:
     """Is called on serial request timeout."""
     if not failed:
         await self.addr_conn.send_command(False,
                                           PckGenerator.request_serial())
     else:
         self.serial_known.set()
Esempio n. 9
0
    async def request_status_var_timeout(
            self,
            failed: bool = False,
            var: Optional[lcn_defs.Var] = None) -> None:
        """Is called on variable status request timeout."""
        assert var is not None
        # Detect if we can send immediately or if we have to wait for a
        # "typeless" response first
        has_type_in_response = lcn_defs.Var.has_type_in_response(
            var, self.addr_conn.software_serial)
        if not has_type_in_response:
            # Use the chance to remove a failed "typeless variable" request
            try:
                await asyncio.wait_for(self.last_var_lock.acquire(),
                                       timeout=3.0)
            except asyncio.TimeoutError:
                pass
            self.last_requested_var_without_type_in_response = var

        # Send variable request
        await self.addr_conn.send_command(
            False,
            PckGenerator.request_var_status(var,
                                            self.addr_conn.software_serial),
        )
Esempio n. 10
0
 async def on_successful_login(self) -> None:
     """Is called after connection to LCN bus system is established."""
     _LOGGER.debug("%s login successful.", self.connection_id)
     await self.send_command(
         PckGenerator.set_operation_mode(self.dim_mode, self.status_mode),
         to_host=True,
     )
     self.task_registry.create_task(self.ping())
Esempio n. 11
0
    async def store_scene(
        self,
        register_id: int,
        scene_id: int,
        output_ports: Sequence[lcn_defs.OutputPort] = (),
        relay_ports: Sequence[lcn_defs.RelayPort] = (),
        ramp: Optional[int] = None,
    ) -> bool:
        """Store states in the given scene.

        :param    int                register_id:    Register id 0..9
        :param    int                scene_id:       Scene id 0..9
        :param    list(OutputPort)   output_ports:   Output ports to store
                                                     as list
        :param    list(RelayPort)    relay_ports:    Relay ports to store
                                                     as list
        :param    int                ramp:           Ramp value

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      bool
        """
        success = await self.send_command(
            not self.is_group, PckGenerator.change_scene_register(register_id)
        )

        if not success:
            return False

        coros = []
        if output_ports:
            coros.append(
                self.send_command(
                    not self.is_group,
                    PckGenerator.store_scene_output(scene_id, output_ports, ramp),
                )
            )
        if relay_ports:
            coros.append(
                self.send_command(
                    not self.is_group,
                    PckGenerator.store_scene_relay(scene_id, relay_ports),
                )
            )
        results = await asyncio.gather(*coros)
        return all(results)
Esempio n. 12
0
    async def control_led(self, led: lcn_defs.LedPort,
                          state: lcn_defs.LedStatus) -> bool:
        """Send a command to control a led.

        :param    LedPort      led:        Led port
        :param    LedStatus    state:      Led status
        """
        return await self.send_command(
            not self.is_group, PckGenerator.control_led(led.value, state))
Esempio n. 13
0
 async def on_auth(self, success: bool) -> None:
     """Is called after successful authentication."""
     if success:
         _LOGGER.debug("%s authorization successful!", self.connection_id)
         self.authentication_completed_future.set_result(True)
         # Try to set the PCHK decimal mode
         await self.send_command(PckGenerator.set_dec_mode(), to_host=True)
     else:
         _LOGGER.debug("%s authorization failed!", self.connection_id)
         self.authentication_completed_future.set_exception(PchkAuthenticationError)
Esempio n. 14
0
    async def beep(self, sound: lcn_defs.BeepSound, count: int) -> bool:
        """Send a command to make count number of beep sounds.

        :param    BeepSound sound:  Beep sound style
        :param    int       count:  Number of beeps (1..15)

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      bool
        """
        return await self.send_command(not self.is_group,
                                       PckGenerator.beep(sound, count))
Esempio n. 15
0
    async def toggle_all_outputs(self, ramp: int) -> bool:
        """Generate a command that toggles all output-ports.

        Toggle Mode:  (on->off, off->on).

        :param    int    ramp:        Ramp time in milliseconds

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      bool
        """
        return await self.send_command(not self.is_group,
                                       PckGenerator.toggle_all_outputs(ramp))
Esempio n. 16
0
    async def rel_output(self, output_id: int, percent: float) -> bool:
        """Send a command to change the value of an output-port.

        :param     int    output_id:    Output id 0..3
        :param     float    percent:      Relative brightness in percent
                                        -100..100

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      bool
        """
        return await self.send_command(
            not self.is_group, PckGenerator.rel_output(output_id, percent))
Esempio n. 17
0
    async def control_motors_relays(
            self, states: List[lcn_defs.MotorStateModifier]) -> bool:
        """Send a command to control motors via relays.

        :param    states:   The 4 modifiers for the cover states as a list
        :type     states:   list(:class: `~pypck.lcn-defs.MotorStateModifier`)

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      bool
        """
        return await self.send_command(
            not self.is_group, PckGenerator.control_motors_relays(states))
Esempio n. 18
0
    async def lock_regulator(self, reg_id: int, state: bool) -> bool:
        """Send a command to lock a regulator.

        :param    int        reg_id:        Regulator id
        :param    bool       state:         Lock state (locked=True,
                                            unlocked=False)

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      bool
        """
        return await self.send_command(
            not self.is_group, PckGenerator.lock_regulator(reg_id, state))
Esempio n. 19
0
    async def send_command(self, wants_ack: bool, pck: Union[str, bytes]) -> bool:
        """Send a command to the module represented by this class.

        :param    bool    wants_ack:    Also send a request for acknowledge.
        :param    str     pck:          PCK command (without header).
        """
        header = PckGenerator.generate_address_header(
            self.addr, self.conn.local_seg_id, wants_ack
        )
        if isinstance(pck, str):
            return await self.conn.send_command(header + pck)
        return await self.conn.send_command(header.encode() + pck)
Esempio n. 20
0
    async def toggle_output(self, output_id: int, ramp: int) -> bool:
        """Send a command that toggles a single output-port.

        Toggle mode: (on->off, off->on).

        :param    int    output_id:    Output id 0..3
        :param    int    ramp:         Ramp time in milliseconds

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      bool
        """
        return await self.send_command(
            not self.is_group, PckGenerator.toggle_output(output_id, ramp))
Esempio n. 21
0
    async def activate_scene(
        self,
        register_id: int,
        scene_id: int,
        output_ports: Sequence[lcn_defs.OutputPort] = (),
        relay_ports: Sequence[lcn_defs.RelayPort] = (),
        ramp: Optional[int] = None,
    ) -> bool:
        """Activate the stored states for the given scene.

        :param    int                register_id:    Register id 0..9
        :param    int                scene_id:       Scene id 0..9
        :param    list(OutputPort)   output_ports:   Output ports to activate
                                                     as list
        :param    list(RelayPort)    relay_ports:    Relay ports to activate
                                                     as list
        :param    int                ramp:           Ramp value

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      bool
        """
        success = await self.send_command(
            not self.is_group, PckGenerator.change_scene_register(register_id))
        if not success:
            return False

        result = True
        if output_ports:
            result &= await self.send_command(
                not self.is_group,
                PckGenerator.activate_scene_output(scene_id, output_ports,
                                                   ramp),
            )
        if relay_ports:
            result &= await self.send_command(
                not self.is_group,
                PckGenerator.activate_scene_relay(scene_id, relay_ports),
            )
        return result
Esempio n. 22
0
    async def scan_segment_couplers(
        self, num_tries: int = 3, timeout_msec: int = 1500
    ) -> None:
        """Scan for segment couplers on the bus.

        This is a convenience coroutine which handles all the logic when
        scanning segment couplers on the bus. Because of heavy bus traffic,
        not all segment couplers might respond to a scan command immediately.
        The coroutine will make 'num_tries' attempts to send a scan command
        and waits 'timeout_msec' after the last segment coupler response
        before proceeding to the next try.

        :param      int     num_tries:      Scan attempts (default=3)
        :param      int     timeout_msec:   Timeout in msec for each try
                                            (default=3000)
        """
        for _ in range(num_tries):
            await self.send_command(
                PckGenerator.generate_address_header(
                    LcnAddr(3, 3, True), self.local_seg_id, False
                )
                + PckGenerator.segment_coupler_scan()
            )

            # Wait loop which is extended on every segment coupler response
            while True:
                try:
                    await asyncio.wait_for(
                        self.segment_coupler_response_received.acquire(),
                        timeout_msec / 1000,
                    )
                except asyncio.TimeoutError:
                    break

        # No segment coupler expected (num_tries=0)
        if len(self.segment_coupler_ids) == 0:
            _LOGGER.debug("%s: No segment coupler found.", self.connection_id)

        self.segment_scan_completed_event.set()
Esempio n. 23
0
    async def dim_output(self, output_id: int, percent: float, ramp: int) -> bool:
        """Send a dim command for a single output-port.

        :param    int      output_id:    Output id 0..3
        :param    float    percent:      Brightness in percent 0..100
        :param    int      ramp:         Ramp time in milliseconds

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      bool
        """
        return await self.send_command(
            not self.is_group, PckGenerator.dim_output(output_id, percent, ramp)
        )
Esempio n. 24
0
    async def lock_keys(self, table_id: int,
                        states: List[lcn_defs.KeyLockStateModifier]) -> bool:
        """Send a command to lock keys.

        :param    int                     table_id:  Table id: 0..3
        :param    keyLockStateModifier    states:    The 8 modifiers for the
                                                     key lock states as a list

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      bool
        """
        return await self.send_command(
            not self.is_group, PckGenerator.lock_keys(table_id, states))
Esempio n. 25
0
    async def scan_modules(self, num_tries: int = 3, timeout_msec: int = 3000) -> None:
        """Scan for modules on the bus.

        This is a convenience coroutine which handles all the logic when
        scanning modules on the bus. Because of heavy bus traffic, not all
        modules might respond to a scan command immediately.
        The coroutine will make 'num_tries' attempts to send a scan command
        and waits 'timeout_msec' after the last module response before
        proceeding to the next try.

        :param      int     num_tries:      Scan attempts (default=3)
        :param      int     timeout_msec:   Timeout in msec for each try
                                            (default=3000)
        """
        segment_coupler_ids = (
            self.segment_coupler_ids if self.segment_coupler_ids else [0]
        )

        for _ in range(num_tries):
            for segment_id in segment_coupler_ids:
                if segment_id == self.local_seg_id:
                    segment_id = 0
                await self.send_command(
                    PckGenerator.generate_address_header(
                        LcnAddr(segment_id, 3, True), self.local_seg_id, True
                    )
                    + PckGenerator.empty()
                )

            # Wait loop which is extended on every serial number received
            while True:
                try:
                    await asyncio.wait_for(
                        self.module_serial_number_received.acquire(),
                        timeout_msec / 1000,
                    )
                except asyncio.TimeoutError:
                    break
Esempio n. 26
0
    async def control_relays_timer(
            self, time_msec: int,
            states: List[lcn_defs.RelayStateModifier]) -> bool:
        """Send a command to control relays.

        :param      int     time_msec:  Duration of timer in milliseconds
        :param    states:   The 8 modifiers for the relay states as alist
        :type     states:   list(:class:`~pypck.lcn_defs.RelayStateModifier`)

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      bool
        """
        return await self.send_command(
            not self.is_group,
            PckGenerator.control_relays_timer(time_msec, states))
Esempio n. 27
0
    async def var_reset(self,
                        var: lcn_defs.Var,
                        software_serial: Optional[int] = None) -> bool:
        """Send a command to reset the variable value.

        :param    Var    var:    Variable

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      bool
        """
        if software_serial is None:
            await self.serial_known
            software_serial = self.software_serial

        return await self.send_command(
            not self.is_group, PckGenerator.var_reset(var, software_serial))
Esempio n. 28
0
    async def lock_keys_tab_a_temporary(
        self, delay_time: int, delay_unit: lcn_defs.TimeUnit, states: List[bool]
    ) -> bool:
        """Send a command to lock keys in table A temporary.

        :param    int        delay_time:    Time to lock keys
        :param    TimeUnit   delay_unit:    Unit of time
        :param    list(bool) states:        The 8 lock states of the keys as
                                            list (locked=True, unlocked=False)

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      bool
        """
        return await self.send_command(
            not self.is_group,
            PckGenerator.lock_keys_tab_a_temporary(delay_time, delay_unit, states),
        )
Esempio n. 29
0
    async def dyn_text(self, row_id: int, text: str) -> bool:
        """Send dynamic text to a module.

        :param    int    row_id:    Row id 0..3
        :param    str    text:      Text to send (up to 60 bytes)

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      bool
        """
        encoded_text = text.encode(lcn_defs.LCN_ENCODING)
        parts = [encoded_text[12 * part:12 * part + 12] for part in range(5)]
        result = True
        for part_id, part in enumerate(parts):
            result &= await self.send_command(
                not self.is_group,
                PckGenerator.dyn_text_part(row_id, part_id, part),
            )
        return result
Esempio n. 30
0
    async def control_motors_outputs(
        self,
        state: lcn_defs.MotorStateModifier,
        reverse_time: Optional[lcn_defs.MotorReverseTime] = None,
    ) -> bool:
        """Send a command to control a motor via output ports 1+2.

        :param    MotorStateModifier  state: The modifier for the cover state
        :param    MotorReverseTime    reverse_time: Reverse time for modules
                                                    with FW<190C
        :type     state:   :class: `~pypck.lcn-defs.MotorStateModifier`

        :returns:    True if command was sent successfully, False otherwise
        :rtype:      bool
        """
        return await self.send_command(
            not self.is_group,
            PckGenerator.control_motors_outputs(state, reverse_time),
        )