Source code for sensirion_shdlc_driver.device

# -*- coding: utf-8 -*-
# (c) Copyright 2019 Sensirion AG, Switzerland

from __future__ import absolute_import, division, print_function
from .device_base import ShdlcDeviceBase
from .commands.device_info import ShdlcCmdGetProductType, \
    ShdlcCmdGetProductName, ShdlcCmdGetArticleCode, ShdlcCmdGetSerialNumber, \
    ShdlcCmdGetProductSubType
from .commands.device_version import ShdlcCmdGetVersion
from .commands.error_state import ShdlcCmdGetErrorState
from .commands.device_reset import ShdlcCmdDeviceReset
from .commands.slave_address import ShdlcCmdGetSlaveAddress, \
    ShdlcCmdSetSlaveAddress
from .commands.baudrate import ShdlcCmdGetBaudrate, ShdlcCmdSetBaudrate
from .commands.reply_delay import ShdlcCmdGetReplyDelay, ShdlcCmdSetReplyDelay
from .commands.system_up_time import ShdlcCmdGetSystemUpTime
from .commands.factory_reset import ShdlcCmdFactoryReset

import logging
log = logging.getLogger(__name__)


[docs]class ShdlcDevice(ShdlcDeviceBase): """ Generic SHDLC device, providing only common SHDLC commands. This class is intended only to communicate with devices which do not provide a corresponding device driver (yet). With this class you can for example read the serial number of a device even if no device specific driver exists. But if there exists a device specific driver, you should always use it instead of this driver. This is a low-level driver which just provides all SHDLC commands as Python methods. Typically, calling a method sends one SHDLC request to the device and interprets its response. There is no higher level functionality available, please look for other drivers if you need a higher level interface. There is no (or very few) caching functionality in this driver. For example if you call :py:meth:`~sensirion_shdlc_driver.device.ShdlcDevice.get_serial_number()` 100 times, it will send the command 100 times over the SHDLC interface to the device. This makes the driver (nearly) stateless. """
[docs] def __init__(self, connection, slave_address): """ Create an SHDLC device instance on an SHDLC connection. .. note:: This constructor does not communicate with the device, so it's possible to instantiate an object even if the device is not connected or powered yet. :param ~sensirion_shdlc_driver.connection.ShdlcConnection connection: The connection used for the communication. :param byte slave_address: The address of the device. """ super(ShdlcDevice, self).__init__(connection, slave_address)
[docs] def get_product_type(self, as_int=False): """ Get the product type. The product type (sometimes also called "device type") can be used to detect what kind of SHDLC product is connected. :param bool as_int: If ``True``, the product type is returned as an integer, otherwise as a string of hexadecimal digits (default). :return: The product type as an integer or string of hexadecimal digits. :rtype: string/int """ product_type = self.execute(ShdlcCmdGetProductType()) if as_int: product_type = int(product_type, 16) return product_type
[docs] def get_product_subtype(self): """ Get the product subtype. Some product types exist in multiple slightly different variants, this command allows to determine the exact variant of the connected device. Sometimes this is called "device subtype". .. note:: This command is not supported by every product type. :return: The product subtype as a byte (the interpretation depends on the connected product type). :rtype: byte """ return self.execute(ShdlcCmdGetProductSubType())
[docs] def get_product_name(self): """ Get the product name of the device. .. note:: This command is not supported by every product type. :return: The product name as an ASCII string. :rtype: string """ return self.execute(ShdlcCmdGetProductName())
[docs] def get_article_code(self): """ Get the article code of the device. .. note:: This command is not supported by every product type. :return: The article code as an ASCII string. :rtype: string """ return self.execute(ShdlcCmdGetArticleCode())
[docs] def get_serial_number(self): """ Get the serial number of the device. :return: The serial number as an ASCII string. :rtype: string """ return self.execute(ShdlcCmdGetSerialNumber())
[docs] def get_version(self): """ Get the version of the device firmware, hardware and SHDLC protocol. :return: The device version as a Version object. :rtype: Version """ return self.execute(ShdlcCmdGetVersion())
[docs] def get_error_state(self, clear=True, as_exception=False): """ Get and optionally clear the device error state and the last error. The state and error code interpretation depends on the connected device type. :param bool clear: If ``True``, the error state on the device gets cleared. :param bool as_exception: If ``True``, the error state is returned as an :py:class:`~sensirion_shdlc_driver.errors.ShdlcDeviceError` object instead of a byte. :return: The device state as a 32-bit unsigned integer containing all error flags, and the last error which occurred on the device. If ``as_exception`` is ``True``, it's returned as an :py:class:`~sensirion_shdlc_driver.errors.ShdlcDeviceError` object or ``None``, otherwise as a byte. :rtype: int, byte/ShdlcDeviceError/None """ state, error = self.execute(ShdlcCmdGetErrorState(clear=clear)) if as_exception: error = self._get_device_error(error) return state, error
[docs] def get_slave_address(self): """ Get the SHDLC slave address of the device. .. note:: See also the property :py:attr:`~sensirion_shdlc_driver.device.ShdlcDevice.slave_address` which returns the device's slave address without sending a command. This method really sends a command to the device, even though the slave address is actually already known by this object. :return: The slave address of the device. :rtype: byte """ return self.execute(ShdlcCmdGetSlaveAddress())
[docs] def set_slave_address(self, slave_address, update_driver=True): """ Set the SHDLC slave address of the device. .. note:: The slave address is stored in non-volatile memory of the device and thus persists after a device reset. So the next time connecting to the device, you have to use the new address. .. warning:: When changing the address of a slave, make sure there isn't already a slave with that address on the same bus! In that case you would get communication issues which can only be fixed by disconnecting one of the slaves. :param byte slave_address: The new slave address [0..254]. The address 255 is reserved for broadcasts. :param bool update_driver: If ``True``, the property :py:attr:`~sensirion_shdlc_driver.device.ShdlcDevice.slave_address` of this object is also updated with the new address. This is needed to allow further communication with the device, as its address has changed. """ self.execute(ShdlcCmdSetSlaveAddress(slave_address)) if update_driver: self._slave_address = slave_address
[docs] def get_baudrate(self): """ Get the SHDLC baudrate of the device. .. note:: This method really sends a command to the device, even though the baudrate is already known by the used :py:class:`~sensirion_shdlc_driver.port.ShdlcPort` object. :return: The baudrate of the device [bit/s]. :rtype: int """ return self.execute(ShdlcCmdGetBaudrate())
[docs] def set_baudrate(self, baudrate, update_driver=True): """ Set the SHDLC baudrate of the device. .. note:: The baudrate is stored in non-volatile memory of the device and thus persists after a device reset. So the next time connecting to the device, you have to use the new baudrate. .. warning:: If you pass ``True`` to the argument ``update_driver``, the baudrate of the underlaying :py:class:`~sensirion_shdlc_driver.port.ShdlcPort` object is changed. As the baudrate applies to the whole bus (with all its slaves), you might no longer be able to communicate with other slaves. Generally you should change the baudrate of all slaves consecutively, and only set ``update_driver`` to ``True`` the last time. :param int baudrate: The new baudrate. See device documentation for a list of supported baudrates. Many devices support the baudrates 9600, 19200 and 115200. :param bool update_driver: If true, the baudrate of the :py:class:`~sensirion_shdlc_driver.port.ShdlcPort` object is also updated with the baudrate. This is needed to allow further communication with the device, as its baudrate has changed. """ self.execute(ShdlcCmdSetBaudrate(baudrate)) if update_driver: self._connection.port.bitrate = baudrate
[docs] def get_reply_delay(self): """ Get the SHDLC reply delay of the device. See :py:meth:`~sensirion_shdlc_driver.device.ShdlcDevice.set_reply_delay()` for details. :return: The reply delay of the device [μs]. :rtype: byte """ return self.execute(ShdlcCmdGetReplyDelay())
[docs] def set_reply_delay(self, reply_delay): """ Set the SHDLC reply delay of the device. The reply delay allows to increase the minimum response time of the slave to a given value in Microseconds. This is needed for RS485 masters which require some time to switch from sending to receiving. If the slave starts sending the response while the master is still driving the bus lines, a conflict on the bus occurs and communication fails. If you use such a slow RS485 master, you can increase the reply delay of all slaves to avoid this issue. :param byte reply_delay: The new reply delay [μs]. """ self.execute(ShdlcCmdSetReplyDelay(reply_delay))
[docs] def get_system_up_time(self): """ Get the system up time of the device. :return: The time since the last power-on or device reset [s]. :rtype: int """ return self.execute(ShdlcCmdGetSystemUpTime())
[docs] def device_reset(self): """ Execute a device reset (reboot firmware, similar to power cycle). """ self.execute(ShdlcCmdDeviceReset())
[docs] def factory_reset(self): """ Perform a factory reset (restore the off-the-shelf factory configuration). .. warning:: This resets any configuration done after leaving the factory! Keep in mind that this command might also change communication parameters (i.e. baudrate and slave address) and thus you might have to adjust the driver's parameters to allow further communication with the device. """ self.execute(ShdlcCmdFactoryReset())