Exemple #1
0
 def test_get_version(self, hthp: HtHeatpump):
     version = hthp.get_version()
     # ( "3.0.20", 2321 )
     assert isinstance(version, tuple), "'version' must be of type tuple"
     assert len(version) == 2
     ver_str, ver_num = version
     assert isinstance(ver_str, str), "'ver_str' must be of type str"
     m = re.match(r"^(\d+).(\d+).(\d+)$", ver_str)
     assert m is not None, "invalid version string [{!r}]".format(ver_str)
     assert isinstance(ver_num, int), "'ver_num' must be of type int"
     assert ver_num > 0
     hthp.send_request(r"SP,NR=9")
     resp = hthp.read_response()
     m = re.match(r"^SP,NR=9,.*NAME=([^,]+).*VAL=([^,]+).*$", resp)
     assert m is not None, "invalid response for query of the software version [{!r}]".format(
         resp)
     assert ver_str == m.group(1).strip()
     assert ver_num == int(m.group(2))
 def test_validate_param(self, hthp: HtHeatpump, name: str, param: HtParam):
     hthp.send_request(param.cmd())
     resp = hthp.read_response()
     m = re.match(
         r"^{},.*NAME=([^,]+).*VAL=([^,]+).*MAX=([^,]+).*MIN=([^,]+).*$".
         format(param.cmd()), resp)
     assert m is not None, "invalid response for query of parameter {!r} [{!r}]".format(
         name, resp)
     dp_name = m.group(1).strip()
     assert dp_name == name,\
         "data point name doesn't match with the parameter name {!r} [{!r}]".format(name, dp_name)
     dp_value = param.from_str(m.group(2))
     assert dp_value is not None, "data point value must not be None [{}]".format(
         dp_value)
     dp_max = param.from_str(m.group(3))
     assert dp_max == param.max_val,\
         "data point max value doesn't match with the parameter's one {!s} [{!s}]".format(param.max_val, dp_max)
     dp_min = param.from_str(m.group(4))
     if name == "Verdichter laeuft seit" and dp_min == 10:
         dp_min = 0  # seems to be incorrect for the data point "Verdichter laeuft seit" [10 == 0]
     assert dp_min == param.min_val,\
         "data point min value doesn't match with the parameter's one {!s} [{!s}]".format(param.min_val, dp_min)
Exemple #3
0
def main():
    parser = argparse.ArgumentParser(
        description=textwrap.dedent('''\
            Command line tool to create a backup of the Heliotherm heat pump data points.

            Example:

              $ python3 %(prog)s --baudrate 9600 --csv backup.csv
              'SP,NR=0' [Language]: VAL='0', MIN='0', MAX='4'
              'SP,NR=1' [TBF_BIT]: VAL='0', MIN='0', MAX='1'
              'SP,NR=2' [Rueckruferlaubnis]: VAL='1', MIN='0', MAX='1'
              ...
            '''),
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=textwrap.dedent('''\
            DISCLAIMER
            ----------

              Please note that any incorrect or careless usage of this program as well as
              errors in the implementation can damage your heat pump!

              Therefore, the author does not provide any guarantee or warranty concerning
              to correctness, functionality or performance and does not accept any liability
              for damage caused by this program or mentioned information.

              Thus, use it on your own risk!
            ''') + "\r\n")

    parser.add_argument(
        "-d",
        "--device",
        default="/dev/ttyUSB0",
        type=str,
        help=
        "the serial device on which the heat pump is connected, default: %(default)s"
    )

    parser.add_argument(
        "-b",
        "--baudrate",
        default=115200,
        type=int,
        # the supported baudrates of the Heliotherm heat pump (HP08S10W-WEB):
        choices=[9600, 19200, 38400, 57600, 115200],
        help=
        "baudrate of the serial connection (same as configured on the heat pump), default: %(default)s"
    )

    parser.add_argument("-j",
                        "--json",
                        type=str,
                        help="write the result to the specified JSON file")

    parser.add_argument("-c",
                        "--csv",
                        type=str,
                        help="write the result to the specified CSV file")

    parser.add_argument("-t",
                        "--time",
                        action="store_true",
                        help="measure the execution time")

    parser.add_argument("-v",
                        "--verbose",
                        action="store_true",
                        help="increase output verbosity by activating logging")

    parser.add_argument(
        "--without-values",
        action="store_true",
        help=
        "store heat pump data points without their current value (keep it blank)"
    )

    parser.add_argument(
        "--max-retries",
        default=2,
        type=int,
        choices=range(0, 11),
        help=
        "maximum number of retries for a data point request (0..10), default: %(default)s"
    )

    args = parser.parse_args()

    # activate logging with level DEBUG in verbose mode
    if args.verbose:
        logging.basicConfig(level=logging.DEBUG)
    else:
        logging.basicConfig(level=logging.WARNING)

    hp = HtHeatpump(args.device, baudrate=args.baudrate)
    start = timer()
    try:
        hp.open_connection()
        hp.login()

        rid = hp.get_serial_number()
        print("connected successfully to heat pump with serial number {:d}".
              format(rid))
        ver = hp.get_version()
        print("software version = {} ({:d})".format(ver[0], ver[1]))

        result = {}
        for dp_type in ("SP", "MP"):  # for all known data point types
            result.update({dp_type: {}})
            i = 0  # start at zero for each data point type
            while True:
                success = False
                retry = 0
                while not success and retry <= args.max_retries:
                    data_point = "{},NR={:d}".format(dp_type, i)
                    # send request for data point to the heat pump
                    hp.send_request(data_point)
                    # ... and wait for the response
                    try:
                        resp = hp.read_response()
                        # search for pattern "NAME=...", "VAL=...", "MAX=..." and "MIN=..." inside the answer
                        m = re.match(
                            r"^{},.*NAME=([^,]+).*VAL=([^,]+).*MAX=([^,]+).*MIN=([^,]+).*$"
                            .format(data_point), resp)
                        if not m:
                            raise IOError(
                                "invalid response for query of data point {!r} [{}]"
                                .format(data_point, resp))
                        name, value, max, min = m.group(
                            1, 2, 3, 4)  # extract name, value, max and min
                        if args.without_values:
                            value = ""  # keep it blank (if desired)
                        print("{!r} [{}]: VAL={!r}, MIN={!r}, MAX={!r}".format(
                            data_point, name, value, min, max))
                        # store the determined data in the result dict
                        result[dp_type].update({
                            i: {
                                "name": name,
                                "value": value,
                                "min": min,
                                "max": max
                            }
                        })
                        success = True
                    except Exception as e:
                        retry += 1
                        _logger.warning(
                            "try #{:d}/{:d} for query of data point {!r} failed: {!s}"
                            .format(retry, args.max_retries + 1, data_point,
                                    e))
                        # try a reconnect, maybe this will help
                        hp.reconnect()  # perform a reconnect
                        try:
                            hp.login(0)  # and a new login
                        except Exception:
                            pass  # ignore a potential problem
                if not success:
                    _logger.error(
                        "query of data point {!r} failed after {:d} try/tries".
                        format(data_point, retry))
                    break
                else:
                    i += 1

        if args.json:  # write result to JSON file
            with open(args.json, 'w') as jsonfile:
                json.dump(result, jsonfile, indent=4, sort_keys=True)

        if args.csv:  # write result to CSV file
            with open(args.csv, 'w') as csvfile:
                fieldnames = ["type", "number", "name", "value", "min", "max"]
                writer = csv.DictWriter(csvfile,
                                        delimiter='\t',
                                        fieldnames=fieldnames)
                writer.writeheader()
                for dp_type, content in sorted(result.items(), reverse=True):
                    for i, data in content.items():
                        writer.writerow({
                            "type": dp_type,
                            "number": i,
                            "name": data["name"],
                            "value": data["value"],
                            "min": data["min"],
                            "max": data["max"]
                        })

    except Exception as ex:
        _logger.error(ex)
        sys.exit(1)
    finally:
        hp.logout()  # try to logout for an ordinary cancellation (if possible)
        hp.close_connection()
    end = timer()

    # print execution time only if desired
    if args.time:
        print("execution time: {:.2f} sec".format(end - start))

    sys.exit(0)
Exemple #4
0
def main():
    parser = argparse.ArgumentParser(
        description=textwrap.dedent('''\
            Command shell tool to send raw commands to the Heliotherm heat pump.

            For commands which deliver more than one response from the heat pump
            the expected number of responses can be defined by the argument "-r"
            or "--responses".

            Example:

              $ python3 %(prog)s --device /dev/ttyUSB1 "AR,28,29,30" -r 3
              > 'AR,28,29,30'
              < 'AA,28,19,14.09.14-02:08:56,EQ_Spreizung'
              < 'AA,29,20,14.09.14-11:52:08,EQ_Spreizung'
              < 'AA,30,65534,15.09.14-09:17:12,Keine Stoerung'
            '''),
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=textwrap.dedent('''\
            DISCLAIMER
            ----------

              Please note that any incorrect or careless usage of this program as well as
              errors in the implementation can damage your heat pump!

              Therefore, the author does not provide any guarantee or warranty concerning
              to correctness, functionality or performance and does not accept any liability
              for damage caused by this program or mentioned information.

              Thus, use it on your own risk!
            ''') + "\r\n")

    parser.add_argument(
        "-d",
        "--device",
        default="/dev/ttyUSB0",
        type=str,
        help=
        "the serial device on which the heat pump is connected, default: %(default)s"
    )

    parser.add_argument(
        "-b",
        "--baudrate",
        default=115200,
        type=int,
        # the supported baudrates of the Heliotherm heat pump (HP08S10W-WEB):
        choices=[9600, 19200, 38400, 57600, 115200],
        help=
        "baudrate of the serial connection (same as configured on the heat pump), default: %(default)s"
    )

    parser.add_argument(
        "-r",
        "--responses",
        default=1,
        type=int,
        help=
        "number of expected responses for each given command, default: %(default)s"
    )

    parser.add_argument("-t",
                        "--time",
                        action="store_true",
                        help="measure the execution time")

    parser.add_argument("-v",
                        "--verbose",
                        action="store_true",
                        help="increase output verbosity by activating logging")

    parser.add_argument(
        "cmd",
        type=str,
        nargs='+',
        help=
        "command(s) to send to the heat pump (without the preceding '~' and the trailing ';')"
    )

    args = parser.parse_args()

    # activate logging with level DEBUG in verbose mode
    log_format = "%(asctime)s %(levelname)s [%(name)s|%(funcName)s]: %(message)s"
    if args.verbose:
        logging.basicConfig(level=logging.DEBUG, format=log_format)
    else:
        logging.basicConfig(level=logging.WARNING, format=log_format)

    hp = HtHeatpump(args.device, baudrate=args.baudrate)
    try:
        hp.open_connection()
        hp.login()

        rid = hp.get_serial_number()
        if args.verbose:
            _logger.info(
                "connected successfully to heat pump with serial number {:d}".
                format(rid))
        ver = hp.get_version()
        if args.verbose:
            _logger.info("software version = {} ({:d})".format(ver[0], ver[1]))

        with Timer() as timer:
            for cmd in args.cmd:
                # write the given command to the heat pump
                print("> {!r}".format(cmd))
                hp.send_request(cmd)
                # and read all expected responses for this command
                for _ in range(args.responses):
                    resp = hp.read_response()
                    print("< {!r}".format(resp))
        exec_time = timer.elapsed

        # print execution time only if desired
        if args.time:
            print("execution time: {:.2f} sec".format(exec_time))

    except Exception as ex:
        _logger.exception(ex)
        sys.exit(1)
    finally:
        hp.logout()  # try to logout for an ordinary cancellation (if possible)
        hp.close_connection()

    sys.exit(0)
Exemple #5
0
 def test_send_request(self, cmdopt_device: str, cmdopt_baudrate: int):
     hp = HtHeatpump(device=cmdopt_device, baudrate=cmdopt_baudrate)
     with pytest.raises(IOError):
         hp.send_request(r"LIN")
Exemple #6
0
def main():
    parser = argparse.ArgumentParser(
        description=textwrap.dedent('''\
            Command line tool to create a backup of the Heliotherm heat pump settings.

            Example:

              $ python3 %(prog)s --baudrate 9600 --csv backup.csv
              'SP,NR=0' [Language]: 0
              'SP,NR=1' [TBF_BIT]: 0
              'SP,NR=2' [Rueckruferlaubnis]: 1
              ...
            '''),
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=textwrap.dedent('''\
            DISCLAIMER
            ----------

              Please note that any incorrect or careless usage of this program as well as
              errors in the implementation can damage your heat pump!

              Therefore, the author does not provide any guarantee or warranty concerning
              to correctness, functionality or performance and does not accept any liability
              for damage caused by this program or mentioned information.

              Thus, use it on your own risk!
            ''') + "\r\n")

    parser.add_argument(
        "-d",
        "--device",
        default="/dev/ttyUSB0",
        type=str,
        help=
        "the serial device on which the heat pump is connected, default: %(default)s"
    )

    parser.add_argument(
        "-b",
        "--baudrate",
        default=115200,
        type=int,
        # the supported baudrates of the Heliotherm heat pump (HP08S10W-WEB):
        choices=[9600, 19200, 38400, 57600, 115200],
        help=
        "baudrate of the serial connection (same as configured on the heat pump), default: %(default)s"
    )

    parser.add_argument("-j",
                        "--json",
                        type=str,
                        help="write the result to the specified JSON file")

    parser.add_argument("-c",
                        "--csv",
                        type=str,
                        help="write the result to the specified CSV file")

    parser.add_argument("-t",
                        "--time",
                        action="store_true",
                        help="measure the execution time")

    parser.add_argument("-v",
                        "--verbose",
                        action="store_true",
                        help="increase output verbosity by activating logging")

    args = parser.parse_args()

    # activate logging with level INFO in verbose mode
    if args.verbose:
        logging.basicConfig(level=logging.INFO)
    else:
        logging.basicConfig(level=logging.ERROR)

    hp = HtHeatpump(args.device, baudrate=args.baudrate)
    start = timer()
    try:
        hp.open_connection()
        hp.login()

        rid = hp.get_serial_number()
        if args.verbose:
            _logger.info(
                "connected successfully to heat pump with serial number {:d}".
                format(rid))
        ver = hp.get_version()
        if args.verbose:
            _logger.info("software version = {} ({:d})".format(ver[0], ver[1]))

        result = {}
        for dp_type in ("SP", "MP"):  # for all known data point types
            result.update({dp_type: {}})
            i = -1
            while True:
                i += 1
                data_point = "{},NR={:d}".format(dp_type, i)
                # send request for data point to the heat pump
                hp.send_request(data_point)
                # ... and wait for the response
                try:
                    resp = hp.read_response()
                    # search for pattern "NAME=..." and "VAL=..." inside the answer
                    m = re.match(
                        "^{},.*NAME=([^,]+).*VAL=([^,]+).*$".format(
                            data_point), resp)
                    if not m:
                        raise IOError(
                            "invalid response for query of data point {!r} [{}]"
                            .format(data_point, resp))
                    name, value = m.group(1, 2)  # extract name and value
                    print("{!r} [{}]: {}".format(data_point, name, value))
                    # store the determined data in the result dict
                    result[dp_type].update({i: {"name": name, "value": value}})
                except Exception as e:
                    _logger.warning(
                        "query of data point {!r} failed: {!s}".format(
                            data_point, e))
                    # hp.reconnect()  # perform a reconnect
                    break

        if args.json:  # write result to JSON file
            with open(args.json, 'w') as jsonfile:
                json.dump(result, jsonfile, indent=4, sort_keys=True)

        if args.csv:  # write result to CSV file
            with open(args.csv, 'w') as csvfile:
                fieldnames = ["type", "number", "name", "value"]
                writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
                writer.writeheader()
                for dp_type, content in result.items():
                    for i, data in content.items():
                        writer.writerow({
                            "type": dp_type,
                            "number": i,
                            "name": data["name"],
                            "value": data["value"]
                        })

    except Exception as ex:
        _logger.error(ex)
        sys.exit(1)
    finally:
        hp.logout()  # try to logout for an ordinary cancellation (if possible)
        hp.close_connection()
    end = timer()

    # print execution time only if desired
    if args.time:
        print("execution time: {:.2f} sec".format(end - start))

    sys.exit(0)