Пример #1
0
 def test_get_fault_list(self, hthp: HtHeatpump):
     fault_list = hthp.get_fault_list()
     # [ { "index": 29,  # fault list index
     #     "error": 20,  # error code
     #     "datetime": datetime.datetime(...),  # date and time of the entry
     #     "message": "EQ_Spreizung",  # error message
     #     },
     #   # ...
     #   ]
     assert isinstance(fault_list,
                       list), "'fault_list' must be of type list"
     assert len(fault_list) == hthp.get_fault_list_size()
     for entry in fault_list:
         assert isinstance(entry, dict), "'entry' must be of type dict"
         index = entry["index"]
         assert isinstance(index, int), "'index' must be of type int"
         assert 0 <= index < hthp.get_fault_list_size()
         error = entry["error"]
         assert isinstance(error, int), "'error' must be of type int"
         assert error >= 0
         dt = entry["datetime"]
         assert isinstance(
             dt, datetime.datetime), "'dt' must be of type datetime"
         msg = entry["message"]
         assert isinstance(msg, str), "'msg' must be of type str"
Пример #2
0
 def test_get_fault_list_in_several_pieces(self, hthp: HtHeatpump):
     args = []
     cmd = ""
     fault_list_size = hthp.get_fault_list_size()
     while len(cmd) < 255 * 2:  # request has do be done in 3 parts
         item = random.randint(0, fault_list_size - 1)
         cmd += ",{}".format(item)
         args.append(item)
     fault_list = hthp.get_fault_list(*args)
     # [ { "index": 29,  # fault list index
     #     "error": 20,  # error code
     #     "datetime": datetime.datetime(...),  # date and time of the entry
     #     "message": "EQ_Spreizung",  # error message
     #     },
     #   # ...
     #   ]
     assert isinstance(fault_list,
                       list), "'fault_list' must be of type list"
     assert len(fault_list) == len(args)
     for entry in fault_list:
         assert isinstance(entry, dict), "'entry' must be of type dict"
         index = entry["index"]
         assert isinstance(index, int), "'index' must be of type int"
         assert 0 <= index < fault_list_size
         error = entry["error"]
         assert isinstance(error, int), "'error' must be of type int"
         assert error >= 0
         dt = entry["datetime"]
         assert isinstance(
             dt, datetime.datetime), "'dt' must be of type datetime"
         msg = entry["message"]
         assert isinstance(msg, str), "'msg' must be of type str"
Пример #3
0
 def test_get_time_prog(self, hthp: HtHeatpump, index: int):
     time_prog = hthp.get_time_prog(index, with_entries=False)
     assert isinstance(
         time_prog, TimeProgram), "'time_prog' must be of type TimeProgram"
     time_prog = hthp.get_time_prog(index, with_entries=True)
     assert isinstance(
         time_prog, TimeProgram), "'time_prog' must be of type TimeProgram"
Пример #4
0
 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)
Пример #5
0
 def test_verify_param_action(self, cmdopt_device: str,
                              cmdopt_baudrate: int, action: set):
     hp = HtHeatpump(device=cmdopt_device, baudrate=cmdopt_baudrate)
     val = hp.verify_param_action
     assert isinstance(val, set)
     hp.verify_param_action = action
     assert hp.verify_param_action == action
     hp.verify_param_action = val
Пример #6
0
 def test_verify_param_error(self, cmdopt_device: str,
                             cmdopt_baudrate: int):
     hp = HtHeatpump(device=cmdopt_device, baudrate=cmdopt_baudrate)
     val = hp.verify_param_error
     assert isinstance(val, bool)
     hp.verify_param_error = True
     assert hp.verify_param_error is True
     hp.verify_param_error = False
     assert hp.verify_param_error is False
     hp.verify_param_error = val
Пример #7
0
 def test_get_last_fault(self, hthp: HtHeatpump):
     fault = hthp.get_last_fault()
     # (29, 20, datetime.datetime(...), "EQ_Spreizung")
     assert isinstance(fault, tuple), "'fault' must be of type tuple"
     assert len(fault) == 4
     index, error, dt, msg = fault
     assert isinstance(index, int), "'index' must be of type int"
     assert 0 <= index < hthp.get_fault_list_size()
     assert isinstance(error, int), "'error' must be of type int"
     assert error >= 0
     assert isinstance(dt,
                       datetime.datetime), "'dt' must be of type datetime"
     assert isinstance(msg, str), "'msg' must be of type str"
Пример #8
0
 def test_get_time_progs(self, hthp: HtHeatpump):
     time_progs = hthp.get_time_progs()
     assert isinstance(time_progs,
                       List), "'time_progs' must be of type list"
     assert len(time_progs) > 0
     assert all(
         [isinstance(time_prog, TimeProgram) for time_prog in time_progs])
Пример #9
0
 def test_fast_query(self, hthp: HtHeatpump):
     values = hthp.fast_query()
     assert isinstance(values, dict), "'values' must be of type dict"
     assert len(values) == len(HtParams.of_type("MP"))
     for name, value in values.items():
         assert name in HtParams
         assert value is not None
         assert HtParams[name].in_limits(value)
Пример #10
0
 def test_fast_query_with_names(self, hthp: HtHeatpump, names: List[str]):
     values = hthp.fast_query(*names)
     assert isinstance(values, dict), "'values' must be of type dict"
     assert not names or len(values) == len(set(names))
     for name, value in values.items():
         assert name in HtParams
         assert not names or name in names
         assert value is not None
         assert HtParams[name].in_limits(value)
Пример #11
0
 def test_get_date_time(self, hthp: HtHeatpump):
     date_time = hthp.get_date_time()
     # (datetime.datetime(...), 2)  # 2 = Tuesday
     assert isinstance(date_time,
                       tuple), "'date_time' must be of type tuple"
     assert len(date_time) == 2
     dt, weekday = date_time
     assert isinstance(dt,
                       datetime.datetime), "'dt' must be of type datetime"
     assert isinstance(weekday, int), "'weekday' must be of type int"
     assert weekday in range(1, 8)
Пример #12
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))
Пример #13
0
 def test_get_fault_list_with_indices(self, hthp: HtHeatpump):
     size = hthp.get_fault_list_size()
     for cnt in range(size + 1):
         indices = random.sample(range(size), cnt)
         fault_list = hthp.get_fault_list(*indices)
         assert isinstance(fault_list,
                           list), "'fault_list' must be of type list"
         assert len(fault_list) == (cnt if cnt > 0 else size)
         for entry in fault_list:
             assert isinstance(entry, dict), "'entry' must be of type dict"
             index = entry["index"]
             assert isinstance(index, int), "'index' must be of type int"
             assert 0 <= index < hthp.get_fault_list_size()
             error = entry["error"]
             assert isinstance(error, int), "'error' must be of type int"
             assert error >= 0
             dt = entry["datetime"]
             assert isinstance(
                 dt, datetime.datetime), "'dt' must be of type datetime"
             msg = entry["message"]
             assert isinstance(msg, str), "'msg' must be of type str"
Пример #14
0
def hthp(cmdopt_device: str, cmdopt_baudrate: int):
    hthp = HtHeatpump(device=cmdopt_device, baudrate=cmdopt_baudrate)
    try:
        hthp.open_connection()
        yield hthp  # provide the heat pump instance
    finally:
        hthp.close_connection()
Пример #15
0
 def test_query(self, hthp: HtHeatpump):
     values = hthp.query()
     # { "HKR Soll_Raum": 21.0,
     #   "Stoerung": False,
     #   "Temp. Aussen": 8.8,
     #   # ...
     #   }
     assert isinstance(values, dict), "'values' must be of type dict"
     assert len(values) == len(HtParams)
     for name, value in values.items():
         assert name in HtParams
         assert value is not None
         assert HtParams[name].in_limits(value)
Пример #16
0
 def test_get_fault_list_with_index(self, hthp: HtHeatpump):
     size = hthp.get_fault_list_size()
     assert isinstance(size, int), "'size' must be of type int"
     assert size >= 0
     for i in range(size):
         fault_list = hthp.get_fault_list(i)
         assert isinstance(fault_list,
                           list), "'fault_list' must be of type list"
         assert len(fault_list) == 1
         entry = fault_list[0]
         assert isinstance(entry, dict), "'entry' must be of type dict"
         index = entry["index"]
         assert isinstance(index, int), "'index' must be of type int"
         assert 0 <= index < hthp.get_fault_list_size()
         error = entry["error"]
         assert isinstance(error, int), "'error' must be of type int"
         assert error >= 0
         dt = entry["datetime"]
         assert isinstance(
             dt, datetime.datetime), "'dt' must be of type datetime"
         msg = entry["message"]
         assert isinstance(msg, str), "'msg' must be of type str"
Пример #17
0
 def test_fast_query_in_several_pieces(self, hthp: HtHeatpump):
     args = []
     cmd = ""
     mp_data_points = [(name, param) for name, param in HtParams.items()
                       if param.dp_type == "MP"]
     while len(cmd) < 255 * 2:  # request has do be done in 3 parts
         name, param = random.choice(mp_data_points)
         cmd += ",{}".format(param.dp_number)
         args.append(name)
     values = hthp.fast_query(*args)
     assert isinstance(values, dict), "'values' must be of type dict"
     assert not args or len(values) == len(set(args))
     for name, value in values.items():
         assert name in HtParams
         assert not args or name in args
         assert value is not None
         assert HtParams[name].in_limits(value)
Пример #18
0
def main():
    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s %(levelname)s [%(name)s|%(funcName)s]: %(message)s",
    )

    hp = HtHeatpump("/dev/ttyUSB0", baudrate=115200)
    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]))

    names = HtParams.of_type("MP").keys()

    t_query = t_fast_query = 0.0
    for i in range(10):
        start = timer()
        values = hp.query(*names)
        t_query += timer() - start
        start = timer()
        values = hp.fast_query(*names)
        t_fast_query += timer() - start
    i += 1
    t_query = t_query / i
    t_fast_query = t_fast_query / i

    print("\n" + "-" * 100)
    print("HtHeatpump.query({:d})      execution time: {:.3f} sec".format(
        len(names), t_query))
    print("HtHeatpump.fast_query({:d}) execution time: {:.3f} sec".format(
        len(names), t_fast_query))
    print("-> {:.3f} x faster".format(t_query / t_fast_query))

    while True:
        print("\n" + "-" * 100)
        rand_names = random.sample(names, random.randint(0, len(names)))
        print("{!s}".format(rand_names))
        # fast query for the given parameter(s)
        values = hp.fast_query(*rand_names)
        # print the current value(s) of the retrieved parameter(s)
        print(", ".join(
            map(lambda name: "{!r} = {}".format(name, values[name]), values)))
        # for name in sorted(values.keys()):
        #    print("{:{width}} [{},{:02d}]: {}".format(name,
        #                                              HtParams[name].dp_type,
        #                                              HtParams[name].dp_number,
        #                                              values[name],
        #                                              width=len(max(values.keys(), key=len))))
        for i in range(5, 0, -1):
            # print(i)
            sys.stdout.write("\rContinue in {:d}s ...".format(i))
            sys.stdout.flush()
            time.sleep(1)
        print("\rContinue in 0s ...")

    hp.logout()  # try to logout for an ordinary cancellation (if possible)
    hp.close_connection()

    sys.exit(0)
Пример #19
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 htshell.py --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",
                rid)
        ver = hp.get_version()
        if args.verbose:
            _LOGGER.info("software version = %s (%d)", *ver)

        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)
Пример #20
0
def main():
    parser = argparse.ArgumentParser(
        description=textwrap.dedent("""\
            Command line tool to get and set date and time on the Heliotherm heat pump.

            To change date and/or time on the heat pump the date and time has to be passed
            in ISO 8601 format (YYYY-MM-DDTHH:MM:SS) to the program. It is also possible to
            pass an empty string, therefore the current date and time of the host will be
            used. If nothing is passed to the program the current date and time on the heat
            pump will be returned.

            Example:

              $ python3 htdatetime.py --device /dev/ttyUSB1 --baudrate 9600
              Tuesday, 2017-11-21T21:48:04
              $ python3 htdatetime.py -d /dev/ttyUSB1 -b 9600 "2008-09-03T20:56:35"
              Wednesday, 2008-09-03T20:56:35
            """),
        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("-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(
        "datetime",
        type=str,
        nargs="?",
        help=
        "date and time in ISO 8601 format (YYYY-MM-DDTHH:MM:SS), if empty current date and time will be used, "
        "if not specified current date and time on the heat pump will be returned",
    )

    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",
                rid)
        ver = hp.get_version()
        if args.verbose:
            _LOGGER.info("software version = %s (%d)", *ver)

        if args.datetime is None:
            # get current date and time on the heat pump
            with Timer() as timer:
                dt, wd = hp.get_date_time()
            exec_time = timer.elapsed
            print("{}, {}".format(WEEKDAYS[wd - 1], dt.isoformat()))
        else:
            # set current date and time on the heat pump
            if not args.datetime:
                # no date and time given, so use the current date and time on the host
                dt = datetime.datetime.now()
            else:
                # otherwise translate the given string to a valid datetime object
                dt = datetime.datetime.strptime(args.datetime,
                                                "%Y-%m-%dT%H:%M:%S")
            with Timer() as timer:
                dt, wd = hp.set_date_time(dt)
            exec_time = timer.elapsed
            print("{}, {}".format(WEEKDAYS[wd - 1], dt.isoformat()))

        # 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)
Пример #21
0
def main():
    parser = argparse.ArgumentParser(
        description=textwrap.dedent("""\
            Command line tool to query for parameters of the Heliotherm heat pump the fast way.
            Note: Only parameters representing a "MP" data point are supported!

            Example:

              $ python3 htfastquery.py --device /dev/ttyUSB1 "Temp. Vorlauf" "Temp. Ruecklauf"
              Temp. Ruecklauf [MP,04]: 25.2
              Temp. Vorlauf   [MP,03]: 25.3
            """),
        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",
                        action="store_true",
                        help="output will be in JSON format")

    parser.add_argument(
        "--bool-as-int",
        action="store_true",
        help="boolean values will be stored as '0' and '1'",
    )

    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(
        "name",
        type=str,
        nargs="*",
        help=
        "parameter name(s) to query for (as defined in htparams.csv) or omit to query for "
        "all known parameters representing a MP data point",
    )

    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",
                rid)
        ver = hp.get_version()
        if args.verbose:
            _LOGGER.info("software version = %s (%d)", *ver)

        # fast query for the given parameter(s)
        with Timer() as timer:
            values = hp.fast_query(*args.name)
        exec_time = timer.elapsed
        for name, val in values.items():
            if args.bool_as_int and HtParams[
                    name].data_type == HtDataTypes.BOOL:
                values[name] = 1 if val else 0

        # print the current value(s) of the retrieved parameter(s)
        if args.json:
            print(json.dumps(values, indent=4, sort_keys=True))
        else:
            if len(values) > 1:
                for name in sorted(values.keys()):
                    print("{:{width}} [{},{:02d}]: {}".format(
                        name,
                        HtParams[name].dp_type,
                        HtParams[name].dp_number,
                        values[name],
                        width=len(max(values.keys(), key=len)),
                    ))
            elif len(values) == 1:
                print(next(iter(values.values())))

        # 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)
Пример #22
0
def main():
    parser = argparse.ArgumentParser(
        description=textwrap.dedent("""\
            Command line tool to create a complete list of all Heliotherm heat pump parameters.

            Example:

              $ python3 htcomplparams.py --device /dev/ttyUSB1 --baudrate 9600 --csv
              connected successfully to heat pump with serial number 123456
              software version = 3.0.20 (273)
              'SP,NR=0' [Language]: VAL=0, MIN=0, MAX=4 (dtype=INT)
              'SP,NR=1' [TBF_BIT]: VAL=0, MIN=0, MAX=1 (dtype=BOOL)
              'SP,NR=2' [Rueckruferlaubnis]: VAL=1, MIN=0, MAX=1 (dtype=BOOL)
              ...
              write data to: /home/pi/prog/htheatpump/htparams-123456-3_0_20-273.csv
            """),
        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(
        "-c",
        "--csv",
        type=str,
        help="write the result to the specified CSV file",
        nargs="?",
        const="",
    )

    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(
        "--max-retries",
        default=2,
        type=int,
        choices=range(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
    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()
        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 = {}
        with Timer() as timer:
            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))
                            # extract name, value, min and max
                            name, value, min_val, max_val = (
                                g.strip() for g in m.group(1, 2, 4, 3))
                            # determine the data type of the data point
                            dtype = None
                            try:
                                min_val = HtParam.from_str(
                                    min_val, HtDataTypes.INT)
                                max_val = HtParam.from_str(
                                    max_val, HtDataTypes.INT)
                                value = HtParam.from_str(
                                    value, HtDataTypes.INT)
                                dtype = "INT"
                                if min_val == 0 and max_val == 1 and value in (
                                        0, 1):
                                    dtype = "BOOL"
                            except ValueError:
                                min_val = HtParam.from_str(min_val,
                                                           HtDataTypes.FLOAT,
                                                           strict=False)
                                max_val = HtParam.from_str(max_val,
                                                           HtDataTypes.FLOAT,
                                                           strict=False)
                                value = HtParam.from_str(value,
                                                         HtDataTypes.FLOAT,
                                                         strict=False)
                                dtype = "FLOAT"
                            assert dtype is not None
                            # print the determined values
                            print(
                                "{!r} [{}]: VAL={}, MIN={}, MAX={} (dtype={})".
                                format(data_point, name, value, min_val,
                                       max_val, dtype))
                            # store the determined data in the result dict
                            result[dp_type].update({
                                i: {
                                    "name": name,
                                    "value": value,
                                    "min": min_val,
                                    "max": max_val,
                                    "dtype": dtype,
                                }
                            })
                            success = True
                        except Exception as e:
                            retry += 1
                            _LOGGER.warning(
                                "try #%d/%d for query of data point '%s' failed: %s",
                                retry,
                                args.max_retries + 1,
                                data_point,
                                e,
                            )
                            # try a reconnect, maybe this will help
                            hp.reconnect()  # perform a reconnect
                            try:
                                hp.login(max_retries=0)  # ... and a new login
                            except Exception:
                                pass  # ignore a potential problem
                    if not success:
                        _LOGGER.error(
                            "query of data point '%s' failed after %d try/tries",
                            data_point,
                            retry,
                        )
                        break
                    else:
                        i += 1
        exec_time = timer.elapsed

        if args.csv is not None:  # write result to CSV file
            filename = args.csv.strip()
            if filename == "":
                filename = os.path.join(
                    os.getcwd(),
                    "htparams-{}-{}-{}.csv".format(rid,
                                                   ver[0].replace(".", "_"),
                                                   ver[1]),
                )
            print("write data to: " + filename)
            with open(filename, "w") as csvfile:
                header = (
                    "# name",
                    "data point type (MP;SP)",
                    "data point number",
                    "acl (r-;-w;rw)",
                    "dtype (BOOL;INT;FLOAT)",
                    "min",
                    "max",
                )
                writer = csv.writer(csvfile, delimiter=",")
                writer.writerow(header)
                for dp_type, content in sorted(result.items()):
                    for i, data in content.items():
                        row_data = (
                            data["name"],
                            dp_type,
                            str(i),
                            "r-",
                            data["dtype"],
                            str(data["min"]),
                            str(data["max"]),
                        )
                        writer.writerow(row_data)

        # 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)
Пример #23
0
 def test_get_time_prog_entry_raises_IOError(self, hthp: HtHeatpump,
                                             index: int, day: int,
                                             num: int):
     with pytest.raises(IOError):
         hthp.get_time_prog_entry(index, day, num)
Пример #24
0
 def test_get_time_prog_entry(self, hthp: HtHeatpump, index: int, day: int,
                              num: int):
     entry = hthp.get_time_prog_entry(index, day, num)
     assert isinstance(
         entry, TimeProgEntry), "'entry' must be of type TimeProgEntry"
Пример #25
0
 def test_get_time_prog_raises_IOError(self, hthp: HtHeatpump, index: int):
     with pytest.raises(IOError):
         hthp.get_time_prog(index, with_entries=False)
     with pytest.raises(IOError):
         hthp.get_time_prog(index, with_entries=True)
Пример #26
0
 def test_in_error(self, hthp: HtHeatpump):
     in_error = hthp.in_error
     assert isinstance(in_error, bool), "'in_error' must be of type bool"
     assert in_error == hthp.get_param("Stoerung")
Пример #27
0
 def test_fast_query_with_names_raises_ValueError(self, cmdopt_device: str,
                                                  cmdopt_baudrate: int,
                                                  names: List[str]):
     hp = HtHeatpump(device=cmdopt_device, baudrate=cmdopt_baudrate)
     with pytest.raises(ValueError):
         hp.fast_query(*names)
Пример #28
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 htbackup.py --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(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
    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()
        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 = {}
        with Timer() as timer:
            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))
                            # extract name, value, min and max
                            name, value, min_val, max_val = (
                                g.strip() for g in m.group(1, 2, 4, 3))
                            if args.without_values:
                                value = ""  # keep it blank (if desired)
                            print("{!r} [{}]: VAL={!r}, MIN={!r}, MAX={!r}".
                                  format(data_point, name, value, min_val,
                                         max_val))
                            # store the determined data in the result dict
                            result[dp_type].update({
                                i: {
                                    "name": name,
                                    "value": value,
                                    "min": min_val,
                                    "max": max_val,
                                }
                            })
                            success = True
                        except Exception as e:
                            retry += 1
                            _LOGGER.warning(
                                "try #%d/%d for query of data point '%s' failed: %s",
                                retry,
                                args.max_retries + 1,
                                data_point,
                                e,
                            )
                            # try a reconnect, maybe this will help
                            hp.reconnect()  # perform a reconnect
                            try:
                                hp.login(max_retries=0)  # ... and a new login
                            except Exception:
                                pass  # ignore a potential problem
                    if not success:
                        _LOGGER.error(
                            "query of data point '%s' failed after %s try/tries",
                            data_point,
                            retry,
                        )
                        break
                    else:
                        i += 1
        exec_time = timer.elapsed

        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=",",
                                        fieldnames=fieldnames)
                writer.writeheader()
                for dp_type, content in sorted(result.items(), reverse=True):
                    for i, data in content.items():
                        row_data = {"type": dp_type, "number": i}
                        row_data.update(data)
                        writer.writerow({n: row_data[n] for n in fieldnames})

        # 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)
Пример #29
0
 def run(self):
     _LOGGER.info("=== HtHttpDaemon.run() %s", "=" * 100)
     global hp
     try:
         hp = HtHeatpump(args.device, baudrate=args.baudrate)
         hp.open_connection()
         hp.login()
         rid = hp.get_serial_number()
         _LOGGER.info(
             "Connected successfully to heat pump with serial number: %d", rid
         )
         ver = hp.get_version()
         _LOGGER.info("Software version: %s (%d)", *ver)
         hp.logout()
         server = HTTPServer((args.ip, args.port), HttpGetHandler)
         _LOGGER.info("Starting server at: %s", server.server_address)
         server.serve_forever()  # start the server and wait for requests
     except Exception as ex:
         _LOGGER.exception(ex)
         sys.exit(2)
     finally:
         hp.logout()  # try to logout for an ordinary cancellation (if possible)
         hp.close_connection()
Пример #30
0
 def test_fast_query_with_names_raises_KeyError(self, cmdopt_device: str,
                                                cmdopt_baudrate: int):
     hp = HtHeatpump(device=cmdopt_device, baudrate=cmdopt_baudrate)
     with pytest.raises(KeyError):
         hp.fast_query("BlaBlaBla")