File vcontrold-v0.98.10+git20210418.977e6f5.obscpio of Package vcontrold

07070100000000000081A4000028240000003200000001607BF448000007EE000000000000000000000000000000000000003500000000vcontrold-v0.98.10+git20210418.977e6f5/.appveyor.ymlenvironment:
  matrix:
    - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
      ARCH: x86
      CYGWIN: C:\Cygwin
      CYGSH: C:\Cygwin\bin\bash -lc
    - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
      ARCH: x86_64
      CYGWIN: C:\Cygwin64
      CYGSH: C:\Cygwin64\bin\bash -lc

init:
  # Set "build version number" to "short-commit-hash" or when tagged to "tag name" (Travis style)
  - ps: >-
      Try
      {
          if ($env:APPVEYOR_REPO_TAG -eq "true")
          {
            Update-AppveyorBuild -Version "$env:APPVEYOR_REPO_TAG_NAME"
          }
          else
          {
            Update-AppveyorBuild -Version "$($env:APPVEYOR_REPO_COMMIT.substring(0,7))-dirty"
          }
      }
      Catch
      {
            Update-AppveyorBuild -Version "$($env:APPVEYOR_REPO_COMMIT.substring(0,7))-dirty"
      }

install:
  - "%CYGWIN%\\setup-%ARCH%.exe -q --no-shortcuts -P cygwin-devel,git,gcc-core,libxml2,libxml2-devel,cmake,p7zip,libiconv,libiconv-devel"
  - '%CYGWIN%\bin\cygcheck -dc cygwin'

build_script:
  - '%CYGSH% ". /etc/profile && cd $APPVEYOR_BUILD_FOLDER && mkdir build"'
  - '%CYGSH% ". /etc/profile && cd $APPVEYOR_BUILD_FOLDER/build && cmake -DMANPAGES=OFF -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON .."'
  - '%CYGSH% ". /etc/profile && cd $APPVEYOR_BUILD_FOLDER/build && cmake --build ."'

after_build:
  - '%CYGSH% "echo $APPVEYOR_BUILD_VERSION > $APPVEYOR_BUILD_FOLDER/build/VERSION"'
  - '%CYGSH% "cd $APPVEYOR_BUILD_FOLDER/build && cp ../COPYING . && 7z a $APPVEYOR_BUILD_FOLDER/vcontrold_$(echo -n $APPVEYOR_BUILD_VERSION | sed -e s/^v//)-$APPVEYOR_BUILD_NUMBER-cygwin_$ARCH.zip *.exe COPYING VERSION"'

artifacts:
  - name: vcontrold-cygwin_$(ARCH)
    path: '**\*.zip'
    type: zip

deploy:
  - provider: GitHub
    artifact: /.*\.zip/
    description: '$(APPVEYOR_REPO_COMMIT_MESSAGE)'
    draft: false
    prerelease: false
    auth_token:
      secure: qE6hexTUPfCElvkSDkIo9fflU6YweIcrGZ5x2wbw7qHDR2HIYczkMMiSUwvC5y9+
    on:
      appveyor_repo_tag: true       # deploy on tag push only
07070100000001000081A4000028240000003200000001607BF44800001648000000000000000000000000000000000000003300000000vcontrold-v0.98.10+git20210418.977e6f5/.travis.ymllanguage: c

compiler:
  - gcc

include: &toolchain_amd64
  sudo: required
  addons:
    apt:
      packages:
      - cmake
      - libxml2-dev
      - python3-docutils
      - zip
      - checkinstall

include: &toolchain_cross
  sudo: required
  addons:
    apt:
      packages:
      - cmake
      - libxml2-dev
      - python3-docutils
      - gcc-arm-linux-gnueabihf
      - gcc-multilib-arm-linux-gnueabihf
      - libc6-dev-armhf-cross
      - zip
      - checkinstall
      - debootstrap
      - qemu-user-static
      - binfmt-support

include: &toolchain_linux_amd64
  <<: *toolchain_amd64
  env:
    - ARCH=$(dpkg --print-architecture)
    - TARGET=$(gcc -dumpmachine)
    - CC="gcc"
    - CMAKE_OPTS="-Wall"

include: &toolchain_linux_armhf
  <<: *toolchain_cross
  env:
    - CROSS_TOOLCHAIN="true"
    - ARCH="armhf"
    - RELEASE="stable"
    - TARGET="arm-linux-gnueabihf"
    - CC="${TARGET}-gcc"
    - CMAKE_OPTS="-Wall -DCMAKE_TOOLCHAIN_FILE=${TRAVIS_BUILD_DIR}/cmake/Toolchain-rpi.cmake"

env:
  global:
  - GH_REPO=$(echo -n "${TRAVIS_REPO_SLUG}" | cut -d "/" -f 2)
  - GH_USER=$(echo -n "${TRAVIS_REPO_SLUG}" | cut -d "/" -f 1)
  - CHROOT_DIR=${TRAVIS_BUILD_DIR}/chroot-raspbian-armhf

matrix:
  include:
  - os: linux
    <<: *toolchain_linux_amd64
  - os: linux
    <<: *toolchain_linux_armhf

before_cache:
  - if [[ "${CROSS_TOOLCHAIN}" == "true" ]] ; then sudo chown -R ${USER} ${CHROOT_DIR} ; fi

cache:
  directories:
    - ${CHROOT_DIR}

before_script:
  - test -d ${CHROOT_DIR} || mkdir ${CHROOT_DIR}
  - mkdir build
  - cd build
  - if [[ "${CROSS_TOOLCHAIN}" == "true" ]] ; then
      if  [[ "${CHROOT_DIR}" != "" ]] && find ${CHROOT_DIR} -iname "libxml2.so" 2>/dev/null | grep -q libxml2.so && find ${CHROOT_DIR} -iname "parser.h" 2>/dev/null | grep -q libxml ; then
        echo "Cache found" ;
      else
        sudo debootstrap  --foreign --no-check-gpg --variant=minbase --include=libxml2,libxml2-dev,libc6-dev --arch=armhf ${RELEASE} ${CHROOT_DIR} http://archive.raspbian.org/raspbian ;
        sudo cp /usr/bin/qemu-arm-static ${CHROOT_DIR}/usr/bin/ ;
        sudo chroot ${CHROOT_DIR} ./debootstrap/debootstrap --second-stage;
        for FILE in $(find ${CHROOT_DIR} -xtype l) ; do sudo ln -sf $(realpath --relative-to=$(dirname ${FILE}) ${CHROOT_DIR}/$(readlink ${FILE})) ${FILE} ; done;
      fi 
    fi

script:
  - cmake ${CMAKE_OPTS} ..
  - VERBOSE=1 cmake --build .
  - VERSION=$(sed -n -e 's/#define VERSION "\(.*\)"/\1/p' ${TRAVIS_BUILD_DIR}/src/version.h)
  - DEPLOYFILEPREFIX="${GH_REPO}_${VERSION}"
  - DEPLOYFILEPREFIX_BIN="${DEPLOYFILEPREFIX}-${TRAVIS_BUILD_NUMBER}_${ARCH}"

after_success:
  - cd ${TRAVIS_BUILD_DIR}
  - zip -j ${TRAVIS_BUILD_DIR}/${DEPLOYFILEPREFIX_BIN}.zip ${TRAVIS_BUILD_DIR}/build/vcontrold ${TRAVIS_BUILD_DIR}/build/vclient ${TRAVIS_BUILD_DIR}/build/doc/man/vcontrold.1.gz ${TRAVIS_BUILD_DIR}/build/doc/man/vclient.1.gz ${TRAVIS_BUILD_DIR}/COPYING ${TRAVIS_BUILD_DIR}/README.md
  - md5sum "${DEPLOYFILEPREFIX_BIN}.zip" > "${DEPLOYFILEPREFIX_BIN}.zip.md5sum"
  - sha256sum "${DEPLOYFILEPREFIX_BIN}.zip" > "${DEPLOYFILEPREFIX_BIN}.zip.sha256sum"
  - unzip -l ${TRAVIS_BUILD_DIR}/${DEPLOYFILEPREFIX_BIN}.zip
  - if [[ "${TRAVIS_BUILD_NUMBER}.1" == "${TRAVIS_JOB_NUMBER}" ]] ; then
      git archive --prefix "${GH_REPO}-${VERSION}/" -o "${DEPLOYFILEPREFIX}.orig.tar" ${TRAVIS_COMMIT} ;
      tar -rf "${DEPLOYFILEPREFIX}.orig.tar" --owner=root --group=root --transform="s#^#${GH_REPO}-${VERSION}/#" src/version.h ;
      tar --delete -f "${DEPLOYFILEPREFIX}.orig.tar" "${GH_REPO}-${VERSION}/.gitignore" "${GH_REPO}-${VERSION}/.travis.yml" ;
      gzip --best "${DEPLOYFILEPREFIX}.orig.tar" ;
      md5sum "${DEPLOYFILEPREFIX}.orig.tar.gz" > "${DEPLOYFILEPREFIX}.orig.tar.gz.md5sum" ;
      sha256sum "${DEPLOYFILEPREFIX}.orig.tar.gz" > "${DEPLOYFILEPREFIX}.orig.tar.gz.sha256sum" ;
      tar tzvf "${DEPLOYFILEPREFIX}.orig.tar.gz" --force-local ;
    fi
  - if [[ -n "${TRAVIS_TAG}" ]] ; then
      sudo checkinstall --strip=no --install=no --pkgname="$GH_REPO" --pkgversion="$VERSION" --pkgrelease=$TRAVIS_BUILD_NUMBER --arch="$ARCH" --maintainer="${GH_USER}@GitHub" --pkgsource="https://github.com/${GH_USER}/${GH_REPO}/releases/download/v${VERSION}/${DEPLOYFILEPREFIX}.orig.tar.gz" --pkgaltsource="https://github.com/${GH_USER}/${GH_REPO}/" --pkglicense="GPLv3" --pakdir=${TRAVIS_BUILD_DIR} -D -y cmake --build ./build/ --target install ;
      sudo chown travis "${DEPLOYFILEPREFIX_BIN}.deb" ;
      dpkg --info "${DEPLOYFILEPREFIX_BIN}.deb" ;
      dpkg --contents "${DEPLOYFILEPREFIX_BIN}.deb" ;
      md5sum "${DEPLOYFILEPREFIX_BIN}.deb" > "${DEPLOYFILEPREFIX_BIN}.deb.md5sum" ;
      sha256sum "${DEPLOYFILEPREFIX_BIN}.deb" > "${DEPLOYFILEPREFIX_BIN}.deb.sha256sum" ;
    fi
  - ls -laF ${TRAVIS_BUILD_DIR}/${DEPLOYFILEPREFIX}*

deploy:
  provider: releases
  api_key:
    secure: dzTS/n6w0WXczRZZ/4lbW+zX79aVgsY223d9EEeOneqSsekll9ilU+akEhY0z2SvZPMdeo4ywdhdtopEK+9q7yjeU7qFPC0hbXUIqDc1c78UqNl7X3/n+est7IR2yLdKUSLcLi6E35D0Wr5QEu1sJo2ktVkozaCb69Ql7VajZggyuL/vykJMHU15EVQ4jWVdY5vWJZ/m2gN93JD0FZl/TEdnJX9NkStCbrUFveM411nwwiG7sjh3aSCKqXIERACy6uK70nstMFKZeF+go48V60TVS46iATD5oq64X6Qa8HpfeVR/+307JhcpXd1cWw1MmfUxmKWjcM8A6nED1AY5K/GRPTOLs004N6NjmY5B4svzC2tWt6PQEX/rvOSvQg8d3lcgOBcTznRVhpOuj3O63U/TjNd9WaD5NPeUogqV9lNVXg8UgZ9hrxMs80qNJGAZyid8x7XfDQ5bVnuuAqpSlUuWsyPKdDolq5RMsOUQ/o/yvZdB45C/EX6gZLUPOeiR+GffnXDnniYQKQYm+wL5UcDjqSKZdLCgZMrKKd7rCVIcSqs0E36y8BubK3CeiLOMUxmjcrbSE7YHmYDcnwohczm6QRUDS/fPbzd/XfIuIRzRkc9mliDVCE2JXaRJQA5U+TINBHUMUfnyVSENLrQaOweg/pi96B5XoJKSs6vl9YE=
  file_glob: true
  file: ${TRAVIS_BUILD_DIR}/${DEPLOYFILEPREFIX}*
  skip_cleanup: true
  on:
    repo: openv/vcontrold
    branch: master
    tags: true
07070100000002000081A4000028240000003200000001607BF44800000CD5000000000000000000000000000000000000003600000000vcontrold-v0.98.10+git20210418.977e6f5/CMakeLists.txtcmake_minimum_required(VERSION 3.2.0)
project(vcontrold C)

if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
    # CMAKE_INSTALL_PREFIX defaults to /usr/local, but it should go to /usr
    set(CMAKE_INSTALL_PREFIX /usr)
endif()

option(MANPAGES "Build man pages via rst2man" ON)
option(VCLIENT "Build the vclient helper program (for communication with vcontrold)" ON)
option(VSIM "Build the vsim helper program (for development and testing purposes)" OFF)

# Default to -fcommon for GCC 10
if (CMAKE_C_COMPILER_ID MATCHES "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER 10)
    set (CMAKE_C_FLAGS "-fcommon ${CMAKE_C_FLAGS}")
endif ()

# additional define to compile on macOS Catalina by hmueller01
if (APPLE)
    set (CMAKE_C_FLAGS "-D_DARWIN_C_SOURCE ${CMAKE_C_FLAGS}")
endif (APPLE)

find_package(LibXml2 REQUIRED)

if(MANPAGES)
    find_program(RST2MAN NAMES rst2man rst2man.py)
    if(RST2MAN)
        message(STATUS "Found rst2man: ${RST2MAN}")
    else()
        message(FATAL_ERROR "Could not find a rst2man executable. Either set the \"MANPAGES\" "
                            "option to \"OFF\" (via cmake -DMANPAGES=OFF) or install Python's "
                            "Docutils (cf. http://docutils.sourceforge.net/).")
    endif()
endif()

add_custom_target(
    UpdateVersion ALL
    COMMAND ${CMAKE_COMMAND}
        -DBASE_DIR=${CMAKE_CURRENT_SOURCE_DIR}
        -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
        -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/UpdateVersion.cmake
    COMMENT "Updating version header."
    BYPRODUCTS ${CMAKE_CURRENT_SOURCE_DIR}/src/version.h
)

add_definitions(-D_XOPEN_SOURCE=700)

include_directories(
    ${LIBXML2_INCLUDE_DIR}
)

set(vcontrold_SRCS
    ${CMAKE_CURRENT_SOURCE_DIR}/src/io.c
    ${CMAKE_CURRENT_SOURCE_DIR}/src/common.c
    ${CMAKE_CURRENT_SOURCE_DIR}/src/xmlconfig.c
    ${CMAKE_CURRENT_SOURCE_DIR}/src/parser.c
    ${CMAKE_CURRENT_SOURCE_DIR}/src/socket.c
    ${CMAKE_CURRENT_SOURCE_DIR}/src/semaphore.c
    ${CMAKE_CURRENT_SOURCE_DIR}/src/framer.c
    ${CMAKE_CURRENT_SOURCE_DIR}/src/unit.c
    ${CMAKE_CURRENT_SOURCE_DIR}/src/arithmetic.c
    ${CMAKE_CURRENT_SOURCE_DIR}/src/vcontrold.c
)

set(vclient_SRCS
    ${CMAKE_CURRENT_SOURCE_DIR}/src/common.c
    ${CMAKE_CURRENT_SOURCE_DIR}/src/socket.c
    ${CMAKE_CURRENT_SOURCE_DIR}/src/io.c
    ${CMAKE_CURRENT_SOURCE_DIR}/src/client.c
    ${CMAKE_CURRENT_SOURCE_DIR}/src/vclient.c
)

set(vsim_SRCS
    ${CMAKE_CURRENT_SOURCE_DIR}/src/socket.c
    ${CMAKE_CURRENT_SOURCE_DIR}/src/vsim.c
)


find_package(Threads)
set(LIBS
    ${LIBS}
    ${LIBXML2_LIBRARIES}
    ${CMAKE_THREAD_LIBS_INIT}
    ${CMAKE_DL_LIBS}
)

add_executable(vcontrold ${vcontrold_SRCS})
target_link_libraries(vcontrold ${LIBS})
add_dependencies(vcontrold UpdateVersion)

if(VCLIENT)
    add_executable(vclient ${vclient_SRCS})
    add_dependencies(vclient UpdateVersion)
endif()

if(VSIM)
    add_executable(vsim ${vsim_SRCS})
    add_dependencies(vsim UpdateVersion)
endif()

if(MANPAGES)
    add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/doc/man)
endif()

install(TARGETS vcontrold DESTINATION ${CMAKE_INSTALL_PREFIX}/sbin)
if(VCLIENT)
    install(TARGETS vclient DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
endif()
if(VSIM)
    install(TARGETS vsim DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
endif()
07070100000003000081A4000028240000003200000001607BF4480000894B000000000000000000000000000000000000002F00000000vcontrold-v0.98.10+git20210418.977e6f5/COPYING                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
07070100000004000081A4000028240000003200000001607BF44800000382000000000000000000000000000000000000003100000000vcontrold-v0.98.10+git20210418.977e6f5/README.md# :fire: vcontrold

_vcontrold_ is a software daemon written in C for communication with the "Optolink" interface of Viessmann Vito heating controllers.

For building and installation instructions see `doc/INSTALL.md`.

[Binary packages](https://github.com/openv/vcontrold/releases) are available for different platforms.

Please visit the [OpenV Wiki](https://github.com/openv/openv/wiki/) for in-depth info and examples.

## DE

_vcontrold_ ist ein in C geschriebener Software-Daemon zur Kommunikation mit der „Optolink“-Schnittstelle von Viessmann-Vito-Heizungssteuerungen.

Der Build-Prozess und die Installation sind kurz unter `doc/INSTALL.md` beschrieben.

Für einige Plattformen sind kompilierte [Installations-Pakete](https://github.com/openv/vcontrold/releases) verfügbar.

Infos zur Einrichtung und Benutzung sind im [OpenV-Wiki](https://github.com/openv/openv/wiki/) beschrieben.
07070100000005000041ED000028240000003200000003607BF44800000000000000000000000000000000000000000000002D00000000vcontrold-v0.98.10+git20210418.977e6f5/cmake07070100000006000081A4000028240000003200000001607BF4480000042F000000000000000000000000000000000000004100000000vcontrold-v0.98.10+git20210418.977e6f5/cmake/Toolchain-rpi.cmake# CMAKE_TOOLCHAIN_FILE for cross-compiling for RPi et al.

SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_SYSTEM_VERSION 1)

if(DEFINED ENV{CHROOT_DIR})
    SET(CHROOT_DIR $ENV{CHROOT_DIR})
else(DEFINED ENV{CHROOT_DIR})
    SET(CHROOT_DIR /mnt/raspbian_lite)
endif(DEFINED ENV{CHROOT_DIR})

SET(CMAKE_C_COMPILER   arm-linux-gnueabihf-gcc)
SET(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)

SET(CMAKE_SYSROOT ${CHROOT_DIR})
SET(CMAKE_FIND_ROOT_PATH ${CHROOT_DIR})

# don't search for programs on CHROOT_DIR, use those on build host
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)

# search libraries and headers on CHROOT_DIR
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)

SET(FLAGS "-Wl,-rpath-link,${CHROOT_DIR}/lib/arm-linux-gnueabihf -Wl,-rpath-link,${CHROOT_DIR}/usr/lib/arm-linux-gnueabihf -Wl,-rpath-link,${CHROOT_DIR}/usr/local/lib,-unresolved-symbols=ignore-in-shared-libs")

UNSET(CMAKE_C_FLAGS CACHE)
UNSET(CMAKE_CXX_FLAGS CACHE)
SET(CMAKE_C_FLAGS ${FLAGS} CACHE STRING "" FORCE)
SET(CMAKE_CXX_FLAGS ${FLAGS} CACHE STRING "" FORCE)
07070100000007000041ED000028240000003200000002607BF44800000000000000000000000000000000000000000000003500000000vcontrold-v0.98.10+git20210418.977e6f5/cmake/modules07070100000008000081A4000028240000003200000001607BF4480000057F000000000000000000000000000000000000004A00000000vcontrold-v0.98.10+git20210418.977e6f5/cmake/modules/GitDescription.cmake# Copyright 2012 Johannes Zarl-Zierl <johannes@zarl-zierl.at>
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# This file is under the public domain and can be reused without restrictions.

include (CMakeParseArguments)

function (git_get_description DESCVAR)
    cmake_parse_arguments (_GGD "SEND_ERROR" "GIT_ARGS" "" "${ARGN}")
    if (SEND_ERROR)
        set (_severity SEND_ERROR)
    else()
        set (_severity WARNING)
    endif()

    find_package (Git QUIET)

    if (NOT GIT_FOUND)
        message (${severity} "git_get_description: could not find package git!")
        set (${DESCVAR} "-NOTFOUND" PARENT_SCOPE)
        return()
    endif()

    execute_process (COMMAND "${GIT_EXECUTABLE}" describe ${_GGD_GIT_ARGS}
                     WORKING_DIRECTORY "${BASE_DIR}"
                     RESULT_VARIABLE _gitresult
                     OUTPUT_VARIABLE _gitdesc
                     ERROR_VARIABLE  _giterror
                     OUTPUT_STRIP_TRAILING_WHITESPACE )

    if (NOT _gitresult EQUAL 0)
        message (${_severity} "git_get_description: error during execution of git describe!")
        message ( ${_severity} "Error was: ${_giterror}" )
        set (${DESCVAR} "-NOTFOUND" PARENT_SCOPE)
    else()
        set (${DESCVAR} "${_gitdesc}" PARENT_SCOPE)
    endif()

endfunction()
07070100000009000081A4000028240000003200000001607BF448000008B0000000000000000000000000000000000000004900000000vcontrold-v0.98.10+git20210418.977e6f5/cmake/modules/UpdateVersion.cmake# Copyright 2012 Johannes Zarl <johannes@zarl-zierl.at>
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# This file is under the public domain and can be reused without restrictions.

if (NOT DEFINED BASE_DIR)
    message (FATAL_ERROR "UpdateVersion.cmake: BASE_DIR not set. Please supply base working directory!")
endif()

function (createVersionH version)
    # write version info to a temporary file
    configure_file (${BASE_DIR}/src/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h~)
    # update info if changed
    execute_process (COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/version.h~ ${BASE_DIR}/src/version.h)
    # make sure info doesn't get stale
    file (REMOVE ${CMAKE_CURRENT_BINARY_DIR}/version.h~)
endfunction(createVersionH)

# git or tarball?
if (EXISTS ${BASE_DIR}/.git)
    # --> git:
    include (${CMAKE_CURRENT_LIST_DIR}/GitDescription.cmake)
    git_get_description (VERSION GIT_ARGS --dirty)
    if (NOT VERSION)
        set (VERSION "unknown")
    else()
        # Remove a trailing "v" from the version.
        string(REGEX REPLACE "^v" "" VERSION ${VERSION})
    endif()

    message (STATUS "Updating version information to ${VERSION} ...")
    createVersionH(${VERSION})

else()
    # --> tarball
    if (NOT EXISTS ${BASE_DIR}/src/version.h)
        message (WARNING "The generated file \"version.h\" does not exist!")
        message (WARNING "Either, something went wrong when releasing this tarball, or this is some GitHub snapshot.")
        get_filename_component(BASE_DIR_L "${BASE_DIR}" NAME)
        string(REGEX REPLACE "[^-]+-([0-9]+)\\.([0-9]+)\\.([0-9]+)(.*)" "\\1.\\2.\\3\\4" VERSION "${BASE_DIR_L}")
        if ("${VERSION}" STREQUAL "${BASE_DIR_L}")
            message(WARNING "Could not derive version from name of project directory, set version as \"unknown\".")
            set (VERSION "unknown")
        else()
            message(WARNING "Version guessed from project directory name as \"${VERSION}\".")
        endif()
        message (WARNING "Generating a dummy version.h.")
        createVersionH(${VERSION})
    endif()
endif()
0707010000000A000041ED000028240000003200000004607BF44800000000000000000000000000000000000000000000002B00000000vcontrold-v0.98.10+git20210418.977e6f5/doc0707010000000B000081A4000028240000003200000001607BF44800000092000000000000000000000000000000000000003300000000vcontrold-v0.98.10+git20210418.977e6f5/doc/AUTHORSThis software has many authors. See the source and the repository for some infos.

Original authors:
 * marcust
 * brainhunter
 * vheat
 * fnobis
0707010000000C000081A4000028240000003200000001607BF448000005E7000000000000000000000000000000000000003600000000vcontrold-v0.98.10+git20210418.977e6f5/doc/INSTALL.md# Building via cmake

The `vcontrold` package depends on `libxml2`.

For the build process on \*nix machines, usually the `libxml2-dev` package is needed. To get the documentation converted from Restructured Text (similar to Markdown) to the format used by `man`, the `rst2man` utility has to be installed.

The package can be built with the normal cmake procedure. Simply execute the following steps in your source directory:

```
mkdir build
cd build
cmake ..
make
```

### Build options

There are three options for the build process with their defaults:

* _MANPAGES=ON_ Build man pages via `rst2man`
* _VCLIENT=ON_  Build the `vclient` helper program (for communication with vcontrold)
* _VSIM=OFF_ Build the `vsim` helper program (for development and testing purposes)

The installation path can be altered by
 
 * _CMAKE_INSTALL_PREFIX=`/usr/local`_
   This directory is prepended onto all install directories. This variable defaults to `/usr/local` on UNIX and `c:/Program Files` on Windows

Invocation is as follows:

```
cmake -DVSIM=ON -DMANPAGES=OFF -DCMAKE_INSTALL_PREFIX=/usr/local ..
```

### Installation

To install the package, execute
```
sudo make install
```
The whole installation can be relocated to a different directory by supplying a `DESTDIR` variable:
```
sudo make DESTDIR=<DESTDIR> install
```
In this case, the entire package will be installed in a directory with the installation prefix prepended with the `DESTDIR` value, which finally gives `<DESTDIR>/<CMAKE_INSTALL_PREFIX>`.
0707010000000D000081A4000028240000003200000001607BF448000003C9000000000000000000000000000000000000003800000000vcontrold-v0.98.10+git20210418.977e6f5/doc/README-de.md***********************************************************
Aktuelle Informationen und Dokus finden sich im Wiki:
https://github.com/openv/vcontrold
***********************************************************
Die Software wird unter der GPL veroeffentlicht.
***********************************************************


vcontrold ist ein in C geschriebener Daemon, der die Kommunikation mit 
der Vito-Steuerung übernimmt.

Die Konfiguration erfolgt über XML-Dateien.
Der Daemon bietet eine ASCII-Socketschnittstelle, die mit telnet oder 
dem vclient Programm angespochen werden kann.

Der Quelltext kann im git Repository heruntergeladen werden.

	git clone https://github.com/openv/vcontrold.git vcontrold-code
    cd vcontrold-code
	mkdir ./build && cd ./build
    cmake ..
	make && make install


Die Konfiguration des Programms wird in zwei XML-Dateien vorgenommen:
vcontrold.xml : Programmspezifische Definitionen
vito.xml: Definition der Kommandos und Devices

0707010000000E000041ED000028240000003200000003607BF44800000000000000000000000000000000000000000000003400000000vcontrold-v0.98.10+git20210418.977e6f5/doc/examples0707010000000F000081A4000028240000003200000001607BF44800000190000000000000000000000000000000000000003F00000000vcontrold-v0.98.10+git20210418.977e6f5/doc/examples/README.txt***********************************************************
Aktuelle Informationen und Dokus finden sich im Wiki:
https://github.com/openv/openv/wiki
***********************************************************
Die Software wird unter der GPL veroeffentlicht.
***********************************************************

Die Beispiel Scripte veranschaulichen das Loggen von Betriebsdaten in eine RRD.
07070100000010000081A4000028240000003200000001607BF448000000D3000000000000000000000000000000000000004100000000vcontrold-v0.98.10+git20210418.977e6f5/doc/examples/rrd.min.cmdsgetTempA
getTempWWist
getTempKist
getTempKsoll
getTempVListM2
getTempKol
getTempSpu
getBrennerStatus
getPumpeStatusA1M1
getPumpeStatusM2
getPumpeStatusSp
getPumpeStatusZirku
getPumpeStatusSolar
getSolarStatusWW
07070100000011000081A4000028240000003200000001607BF44800000206000000000000000000000000000000000000003D00000000vcontrold-v0.98.10+git20210418.977e6f5/doc/examples/rrd.tmpl#/bin/sh
# Script Template
# solarix@pingos.de
#
db="/root/vitotools/vitodb.rrd"
BrennerStatus=`expr \( $R8 \* 10 \)`
PumpeStatusA1M1=`expr \( $R9 \* 20 \)`
PumpeStatusM2=`expr \( $R10 \* 30 \)`
PumpeStatusSp=`expr \( $R11 \* 40 \)`
PumpeStatusZirku=`expr \( $R12 \* 50 \)`
PumpeStatusSolar=`expr \( $R13 \* 60 \)`
SolarStatusWW=`expr \( $R14 \* 70 \)`
/opt/bin/rrdtool update \$db N:$1:$2:$3:$4:$5:$6:$7:$BrennerStatus:$PumpeStatusA1M1:$PumpeStatusM2:$PumpeStatusSp:$PumpeStatusZirku:$PumpeStatusSolar:$SolarStatusWW
07070100000012000041ED000028240000003200000002607BF44800000000000000000000000000000000000000000000003900000000vcontrold-v0.98.10+git20210418.977e6f5/doc/examples/rrdb07070100000013000081ED000028240000003200000001607BF4480000028F000000000000000000000000000000000000004700000000vcontrold-v0.98.10+git20210418.977e6f5/doc/examples/rrdb/createRRDB.sh/opt/usr/bin/rrdtool create /extern/rrdb/vito.rrd --step 300 \
DS:tempA:GAUGE:700:-50:100 \
DS:tempWWist:GAUGE:700:0:100 \
DS:tempWWsoll:GAUGE:700:0:100 \
DS:tempKist:GAUGE:700:0:100 \
DS:tempKsoll:GAUGE:700:0:100 \
DS:BetriebsArt:GAUGE:700:0:5 \
DS:PumpeSollM1:GAUGE:700:0:100 \
DS:ExtBA:GAUGE:700:0:1 \
DS:PumpeStatusM1:GAUGE:700:0:1 \
DS:BrennerStunden1:COUNTER:700:0:100000 \
DS:TempRaumNorSollM1:GAUGE:700:0:100 \
DS:TempRaumRedSollM1:GAUGE:700:0:100 \
DS:BrennerStatus:GAUGE:700:0:1 \
DS:PumpeStatusZirku:GAUGE:700:0:1 \
DS:getVentilStatus:GAUGE:700:0:1 \
RRA:AVERAGE:0.5:1:2020 \
RRA:MIN:0.5:12:2400 \
RRA:MAX:0.5:12:2400 \
RRA:AVERAGE:0.5:12:2400
07070100000014000081ED000028240000003200000001607BF44800000705000000000000000000000000000000000000004200000000vcontrold-v0.98.10+git20210418.977e6f5/doc/examples/rrdb/graph.shLD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/usr/lib
export LD_LIBRARY_PATH

/opt/usr/bin/rrdtool graph /tmp/vito.png \
--start end-1d  --end now --width 800 --height 300 \
--title 'Vito-Messwerte' \
DEF:tempA=/extern/rrdb/vito.rrd:tempA:AVERAGE \
DEF:tempWWist=/extern/rrdb/vito.rrd:tempWWist:AVERAGE \
DEF:tempKist=/extern/rrdb/vito.rrd:tempKist:AVERAGE \
DEF:tempKsoll=/extern/rrdb/vito.rrd:tempKsoll:AVERAGE \
DEF:PumpeSollM1=/extern/rrdb/vito.rrd:PumpeSollM1:AVERAGE \
DEF:PumpeStatusM1=/extern/rrdb/vito.rrd:PumpeStatusM1:AVERAGE \
DEF:BetriebsArt=/extern/rrdb/vito.rrd:BetriebsArt:AVERAGE \
DEF:ExtBA=/extern/rrdb/vito.rrd:ExtBA:AVERAGE \
DEF:BrennerStatus=/extern/rrdb/vito.rrd:BrennerStatus:AVERAGE \
DEF:PumpeStatusZirku=/extern/rrdb/vito.rrd:PumpeStatusZirku:AVERAGE \
DEF:VentilStatus=/extern/rrdb/vito.rrd:getVentilStatus:AVERAGE \
CDEF:PSM1=PumpeStatusM1,30,* \
CDEF:BA=BetriebsArt,10,* \
CDEF:EBA=ExtBA,25,* \
CDEF:BS=BrennerStatus,40,* \
CDEF:VS=VentilStatus,60,* \
CDEF:PS=PumpeStatusZirku,70,* \
LINE1:tempA#0000FF:tempA \
LINE1:tempWWist#FF00FF:tempWWist \
LINE1:tempKist#00FFFF:tempKist \
LINE1:tempKsoll#CCCCCC:tempKsoll \
LINE1:BA#555555:Betriebsart \
LINE1:EBA#779922:Ext.Umschaltung \
LINE1:BS#11ABCD:Brennerstatus \
LINE1:VS#23AA77:Ventilstatus \
LINE1:PS#880044:Zirku-Pumpe \
VDEF:tempAmax=tempA,MAXIMUM \
VDEF:tempAmin=tempA,MINIMUM \
VDEF:tempWWmax=tempWWist,MAXIMUM \
VDEF:tempWWmin=tempWWist,MINIMUM \
VDEF:BSmax=BS,MAXIMUM \
VDEF:VSmax=VS,MAXIMUM \
COMMENT:"\l" \
COMMENT:"Aussentemp." \
GPRINT:tempAmin:"    Min. %02.1lf Grad C" \
GPRINT:tempAmax:"Max. %02.1lf Grad C" \
COMMENT:"\l" \
COMMENT:"Warmwassertemp." \
GPRINT:tempWWmin:"Min. %02.1lf Grad C" \
GPRINT:tempWWmax:"Max. %02.1lf Grad C" \
COMMENT:"\l" \
GPRINT:BSmax:"Brenner %02lf" \
GPRINT:VSmax:"Venitl %02lf" 

07070100000015000081ED000028240000003200000001607BF44800000692000000000000000000000000000000000000004800000000vcontrold-v0.98.10+git20210418.977e6f5/doc/examples/rrdb/rrdb-update.shif  [ "x" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: getDevType:" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: getTempA:" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: getTempWWist:" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: getTempWWsoll:" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: getTempKist:" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: getTempKsoll:" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: unit off:" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: getBetriebArtM1:" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: unit on:" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: getPumpeSollM1:" >>/tmp/vc-err.txt
	exit 1;
fi
BA=`echo 03 |cut -d ' ' -f 1` 
ST=`echo 21639.000000 h|cut -d '.' -f 1` 

LD_LIBRARY_PATH=':/opt/lib:/opt/usr/lib:/opt/usr/lib:/opt/usr/lib'
export LD_LIBRARY_PATH


rrdtool update /home/marcus/rrdb/vito.rrd N:15.500000:56.500000:50.000000:33.500000:39.000000:$BA:33.000000:0:0:$ST
07070100000016000081A4000028240000003200000001607BF44800000ADB000000000000000000000000000000000000004A00000000vcontrold-v0.98.10+git20210418.977e6f5/doc/examples/rrdb/rrdb-update.tmplif  [ "x$E1" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C1:$E1" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x$E2" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C2:$E2" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x$E3" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C3:$E3" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x$E4" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C4:$E4" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x$E5" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C5:$E5" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x$E6" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C6:$E6" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x$E7" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C7:$E7" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x$E8" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C8:$E8" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x$E9" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C9:$E9" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x$E10" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C10:$E10" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x$E11" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C11:$E11" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x$E12" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C12:$E12" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x$E13" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C13:$E13" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x$E14" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C14:$E14" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x$E15" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C15:$E15" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x$E16" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C16:$E16" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x$E17" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C17:$E17" >>/tmp/vc-err.txt
	exit 1;
fi
if  [ "x$E18" != x ]; then
	echo -n `date`  >>/tmp/vc-err.txt
	echo "es ist ein Fehler aufgetreten: $C18:$E18" >>/tmp/vc-err.txt
	exit 1;
fi
BA=`echo $R8|cut -d ' ' -f 1` 
ST=`echo $R13|cut -d '.' -f 1` 

LD_LIBRARY_PATH=':/opt/lib:/opt/usr/lib:/opt/usr/lib:/opt/usr/lib'
export LD_LIBRARY_PATH


/opt/usr/bin/rrdtool update /extern/rrdb/vito.rrd N:$2:$3:$4:$5:$6:$BA:$10:$R11:$R12:$ST:$14:$15:$R16:$R17:$R18
07070100000017000081A4000028240000003200000001607BF4480000005C000000000000000000000000000000000000004700000000vcontrold-v0.98.10+git20210418.977e6f5/doc/examples/rrdb/vc-aufruf.txtvclient -h 192.168.1.4:3002 -f ./vc-commands.txt -t ./rrdb-update.tmpl -x ./rrdb-update.sh

07070100000018000081A4000028240000003200000001607BF44800000105000000000000000000000000000000000000004900000000vcontrold-v0.98.10+git20210418.977e6f5/doc/examples/rrdb/vc-commands.txtgetDevType
getTempA
getTempWWist
getTempWWsoll
getTempKist
getTempKsoll
unit off
getBetriebArtM1
unit on
getPumpeSollM1
getExtBA
getPumpeStatusM1
getBrennerStunden1
getTempRaumNorSollM1
getTempRaumRedSollM1
getBrennerStatus
getPumpeStatusZirku
getVentilStatus

07070100000019000081A4000028240000003200000001607BF44800000F12000000000000000000000000000000000000004100000000vcontrold-v0.98.10+git20210418.977e6f5/doc/examples/sim-2098.ini[DATA]
;getTempA
04 = 05 
01 F7 08 00 02 = 3C 00  
;getTempWWist
04 = 05 
01 F7 08 04 02 = 1E 02  
;getTempWWsoll
04 = 05 
01 F7 63 00 01 = 37  
;getTempKist
04 = 05 
01 F7 08 02 02 = BF 01  
;getTempKsoll
04 = 05 
01 F7 55 02 02 = 46 00  
;getTempVListM2
04 = 05 
01 F7 08 0C 02 = E0 00  
;getTempVLsollA1M1
04 = 05 
01 F7 25 44 02 = FF FF  
;getTempVLsollM2
04 = 05 
01 F7 35 44 02 = FF FF  
;getTempVLsollM3
04 = 05 
01 F7 45 44 02 = FF FF  
;getTempKol
04 = 05 
01 F7 65 64 02 = 3A 00  
;getTempSpu
04 = 05 
01 F7 65 66 02 = A5 00  
;getTempRaumNorSoll1
04 = 05 
01 F7 23 06 01 = 16  
;getTempRaumNorSoll2
04 = 05 
01 F7 33 06 01 = 16  
;getTempRaumRedSoll1
04 = 05 
01 F7 23 07 01 = 03  
;getTempRaumRedSoll2
04 = 05 
01 F7 33 07 01 = 03  
;getBrennerStatus
04 = 05 
01 F7 55 1E 01 = 00  
;getBrennerStarts
04 = 05 
01 F7 08 8A 02 = 5E 06  
;getBrennerStunden
04 = 05 
01 F7 08 A7 04 = 87 03 1A 00  
;getPumpeStatusA1M1
04 = 05 
01 F7 29 06 01 = 00  
;getPumpeStatusSp
04 = 05 
01 F7 08 45 01 = 00  
;getPumpeStatusZirku
04 = 05 
01 F7 08 46 01 = 00  
;getPumpeStatusSolar
04 = 05 
01 F7 65 52 01 = 00  
;getPumpeStatusM2
04 = 05 
01 F7 39 06 01 = 00  
;getMischerA1M1
04 = 05 
01 F7 25 4C 01 = FF  
;getMischerM2
04 = 05 
01 F7 35 4C 01 = FF  
;getMischerM3
04 = 05 
01 F7 45 4C 01 = FF  
;getSolarStatusWW
04 = 05 
01 F7 65 51 01 = 00  
;getTimerM1Mo
04 = 05 
01 F7 20 00 08 = 93 A0 FF FF FF FF FF FF  
;getTimerM1Di
04 = 05 
01 F7 20 08 08 = 93 A0 FF FF FF FF FF FF  
;getTimerM1Mi
04 = 05 
01 F7 20 10 08 = 93 A0 FF FF FF FF FF FF  
;getTimerM1Do
04 = 05 
01 F7 20 18 08 = 93 A0 FF FF FF FF FF FF  
;getTimerM1Fr
04 = 05 
01 F7 20 20 08 = 93 A0 FF FF FF FF FF FF  
;getTimerM1Sa
04 = 05 
01 F7 20 28 08 = 93 A0 FF FF FF FF FF FF  
;getTimerM1So
04 = 05 
01 F7 20 30 08 = 93 A0 FF FF FF FF FF FF  
;getTimerM2Mo
04 = 05 
01 F7 30 00 08 = 30 48 FF FF FF FF FF FF  
;getTimerM2Di
04 = 05 
01 F7 30 08 08 = 30 48 FF FF FF FF FF FF  
;getTimerM2Mi
04 = 05 
01 F7 30 10 08 = 30 48 FF FF FF FF FF FF  
;getTimerM2Do
04 = 05 
01 F7 30 18 08 = 30 48 FF FF FF FF FF FF  
;getTimerM2Fr
04 = 05 
01 F7 30 20 08 = 30 48 FF FF FF FF FF FF  
;getTimerM2Sa
04 = 05 
01 F7 30 28 08 = 88 9B FF FF FF FF FF FF  
;getTimerM2So
04 = 05 
01 F7 30 30 08 = FF FF FF FF FF FF FF FF  
;getTimerWWMo
04 = 05 
01 F7 21 00 08 = 30 48 93 9B FF FF FF FF  
;getTimerWWDi
04 = 05 
01 F7 21 08 08 = 30 48 93 9B FF FF FF FF  
;getTimerWWMi
04 = 05 
01 F7 21 10 08 = 30 48 93 9B FF FF FF FF  
;getTimerWWDo
04 = 05 
01 F7 21 18 08 = 30 48 93 9B FF FF FF FF  
;getTimerWWFr
04 = 05 
01 F7 21 20 08 = 30 48 93 9B FF FF FF FF  
;getTimerWWSa
04 = 05 
01 F7 21 28 08 = 38 48 90 9B FF FF FF FF  
;getTimerWWSo
04 = 05 
01 F7 21 30 08 = 38 48 93 9B FF FF FF FF  
;getTimerZirkuMo
04 = 05 
01 F7 22 00 08 = 28 2A 58 5D 95 A2 FF FF  
;getTimerZirkuDi
04 = 05 
01 F7 22 08 08 = 28 2A 58 5D 95 A2 FF FF  
;getTimerZirkuMi
04 = 05 
01 F7 22 10 08 = 28 2A 58 5D 95 A2 FF FF  
;getTimerZirkuDo
04 = 05 
01 F7 22 18 08 = 28 2A 58 5D 95 A2 FF FF  
;getTimerZirkuFr
04 = 05 
01 F7 22 20 08 = 28 2A 58 5D 95 A2 FF FF  
;getTimerZirkuSa
04 = 05 
01 F7 22 28 08 = 3B 45 58 5D 92 A2 FF FF  
;getTimerZirkuSo
04 = 05 
01 F7 22 30 08 = 40 45 58 5D 95 A2 FF FF  
;getBetriebArtM1
04 = 05 
01 F7 23 01 01 = 04  
;getBetriebArtM2
04 = 05 
01 F7 33 01 01 = 04  
;getBetriebSparM1
04 = 05 
01 F7 23 02 01 = 00  
;getBetriebSparM2
04 = 05 
01 F7 33 02 01 = 00  
;getBetriebPartyM1
04 = 05 
01 F7 23 03 01 = 00  
;getBetriebPartyM2
04 = 05 
01 F7 33 03 01 = 00  
;getSolarStunden
04 = 05 
01 F7 65 68 02 = D1 05  
;getSolarLeistung
04 = 05 
01 F7 65 60 04 = A0 06 00 00  
;getStatusFrostM1
04 = 05 
01 F7 25 00 01 = 02  
;getStatusFrostM2
04 = 05 
01 F7 35 00 01 = 02  
;getStatusStoerung
04 = 05 
01 F7 75 79 01 = 00  
;getTempPartyM1
04 = 05 
01 F7 23 08 01 = 18  
;getTempPartyM2
04 = 05 
01 F7 33 08 01 = 16  
0707010000001A000081A4000028240000003200000001607BF44800000033000000000000000000000000000000000000003D00000000vcontrold-v0.98.10+git20210418.977e6f5/doc/examples/sql.tmplINSERT INTO messwerte values (CURRENT_DATE,$1,$2);
0707010000001B000081A4000028240000003200000001607BF4480000108A000000000000000000000000000000000000004700000000vcontrold-v0.98.10+git20210418.977e6f5/doc/examples/vcontrold.initd.sh#! /bin/sh
### BEGIN INIT INFO
# Provides:          vcontrold
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Initscript to start Viessmann vcontrold deamon
# Description:       This file should be used to construct scripts to be
#                    placed in /etc/init.d.
### END INIT INFO

# Author: Michael Pucher <tech@michaelpucher.net>
#
# Please remove the "Author" lines above and replace them
# with your own name if you copy and modify this script.

# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Viessmann vcontrold deamon"
NAME=vcontrold
DAEMON=/usr/local/sbin/$NAME
#DAEMON_ARGS="--options args"
DAEMON_ARGS=""
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions

#
# Function that starts the daemon/service
#
do_start()
{
	# Return
	#   0 if daemon has been started
	#   1 if daemon was already running
	#   2 if daemon could not be started
	start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
	|| return 1
	start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
	$DAEMON_ARGS \
	|| return 2
	# Add code here, if necessary, that waits for the process to be ready
	# to handle requests from services started subsequently which depend
	# on this one.  As a last resort, sleep for some time.
}

#
# Function that stops the daemon/service
#
do_stop()
{
# Return
#   0 if daemon has been stopped
#   1 if daemon was already stopped
#   2 if daemon could not be stopped
#   other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be
# needed by services started subsequently.  A last resort is to
# sleep for some time.
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE
return "$RETVAL"
}

#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
#
# If the daemon can reload its configuration without
# restarting (for example, when it is sent a SIGHUP),
# then implement that here.
#
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
return 0
}

case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
#reload|force-reload)
#
# If do_reload() is not implemented then leave this commented out
# and leave 'force-reload' as an alias for 'restart'.
#
#log_daemon_msg "Reloading $DESC" "$NAME"
#do_reload
#log_end_msg $?
#;;
restart|force-reload)
#
# If the "reload" option is implemented then remove the
# 'force-reload' alias
#
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 3
;;
esac

:
0707010000001C000081A4000028240000003200000001607BF44800000B62000000000000000000000000000000000000004200000000vcontrold-v0.98.10+git20210418.977e6f5/doc/examples/vcontrold.xml<?xml version="1.0"?>
<V-Control xmlns:vcontrol="http://www.openv.de/vcontrol">
  <unix>
     <config>
	<serial>
		<tty>192.168.1.112:3000</tty>
	</serial>
	<net>
		<port>3002</port>
	</net>
	<logging>
		<file>/tmp/vcontrold.log</file>
		<syslog>n</syslog>
		<debug>y</debug>
	</logging>
	<device ID="2098"/>
     </config>
  </unix>
  <units>
	<unit name='Temperatur'>
	   <abbrev>UT</abbrev>
	   <calc get='V/10' set='V*10'/>
	   <type>short</type>
	   <entity>Grad Celsius</entity>
	</unit>
	<unit name='Status'>
	   <abbrev>ST</abbrev>
	   <calc get='V' set='V'/>
	   <type>char</type>
	   <entity></entity>
	</unit>
	<unit name='Counter'>
	   <abbrev>CO</abbrev>
	   <calc get='V' set='V'/>
	   <type>int</type>
	   <entity></entity>
	</unit>
	<unit name='Prozent'>
	   <abbrev>PR</abbrev>
	   <calc get='V/2' set='V*2'/>
	   <type>short</type>
	   <entity>%</entity>
	</unit>
	<unit name='CounterS'>
	   <abbrev>CS</abbrev>
	   <calc get='V/3600' set='V*3600'/>
	   <type>int</type>
	   <entity>Stunden</entity>
	</unit>
	<unit name='CycleTime'>
	   <abbrev>CT</abbrev>
	   <type>cycletime</type>
	</unit>
	<unit name='ReturnStatus'>
	   <abbrev>RT</abbrev>
	   <type>enum</type>
	   <enum bytes='00' text='0'/>
	   <enum bytes='01' text='1'/>
	   <enum text='NOT OK'/>
	</unit>
	<unit name='BetriebsArt'>
	   <abbrev>BA</abbrev>
	   <type>enum</type>
	   <enum bytes='00' text='AUS'/>
	   <enum bytes='01' text='WW'/>
	   <enum bytes='02' text='H+WW'/>
	   <enum bytes='03' text='RED'/>
	   <enum bytes='04' text='NORM'/>
	   <enum text='UNKNOWN'/>
	</unit>	   
	 <unit name='SetReturnStatus'>
           <abbrev>SR</abbrev>
           <type>enum</type>
           <enum bytes='00' text='OK'/>
           <enum bytes='05' text='SYNC (NOT OK)'/>
           <enum text='NOT OK'/>
        </unit>
  </units>
  <protocols>
      <protocol name='KW2'>
	  <macros>
		<macro name='SYNC'>
		    <command>SEND 04;WAIT 05</command>
		</macro>
		<macro name='GETADDR'>
		    <command>SEND 01 F7</command>
		</macro>
		<macro name='SETADDR'>
		    <command>SEND 01 F4</command>
		</macro>
	  </macros>
	  <commands>
		<command name="getaddr">
		     <send>SYNC;GETADDR $addr $len;RECV $len $unit</send>
		</command>
		<command name="setaddr">
		     <send>SYNC;SETADDR $addr $len;SEND BYTES $unit;RECV 1 SR</send>
		</command>
	  </commands>
      </protocol>
      <protocol name='GWG'>
	  <macros>
		<macro name='SYNC'>
		    <command>SEND 04;WAIT 05</command>
		</macro>
		<macro name='GETADDR'>
		    <command>SEND 01 CB</command>
		</macro>
	  </macros>
	  <commands>
		<command name="getaddr">
		     <send>SYNC;GETADDR $addr $len;RECV $len $unit</send>
		</command>
		<command name="setaddr">
		     <send>SYNC;RECV 1</send>
		</command>
	  </commands>
      </protocol>
  </protocols>
  <extern xmlns:xi="http://www.w3.org/2003/XInclude">
     <xi:include href="vito.xml" parse="xml"/>
  </extern>
</V-Control>
0707010000001D000081A4000028240000003200000001607BF448000000C3000000000000000000000000000000000000003F00000000vcontrold-v0.98.10+git20210418.977e6f5/doc/examples/vupdate.sh#/bin/sh
# RDD Update V200KW2
# solarix@pingos.de
#
/root/vitotools/vclient -h 192.168.1.153:3002 -f /root/vitotools/rrd.min.cmds -t /root/vitotools/rrd.tmpl -x /root/vitotools/rrd.sh
exit
0707010000001E000041ED000028240000003200000002607BF44800000000000000000000000000000000000000000000002F00000000vcontrold-v0.98.10+git20210418.977e6f5/doc/man0707010000001F000081A4000028240000003200000001607BF448000003F5000000000000000000000000000000000000003E00000000vcontrold-v0.98.10+git20210418.977e6f5/doc/man/CMakeLists.txt# Generate classic *roff manual pages from ReStructuredText.
# This requires Python's Docutils (http://docutils.sourceforge.net/)

cmake_minimum_required(VERSION 3.2.0)

set(RST2MAN_OPTS)

set(MANUALS vcontrold)
if(VCLIENT)
    list(APPEND MANUALS vclient)
endif(VCLIENT)
if(VSIM)
    list(APPEND MANUALS vsim)
endif(VSIM)

foreach(MANUAL IN LISTS MANUALS)
    set(MANPAGE_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${MANUAL}.1)
    set(MANPAGE_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${MANUAL}.rst)
    add_custom_command(OUTPUT ${MANPAGE_OUTPUT}
        COMMAND ${RST2MAN} ${RST2MAN_OPTS} ${MANPAGE_SOURCE} ${MANPAGE_OUTPUT}
        DEPENDS ${MANPAGE_SOURCE}
        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
        COMMENT "Converting ${MANUAL}.rst from ReStructuredText into manpage ${MANUAL}.1"
        VERBATIM
    )
    list(APPEND MANPAGES_LIST ${MANPAGE_OUTPUT})
endforeach()

add_custom_target(man ALL DEPENDS ${MANPAGES_LIST})
install(FILES ${MANPAGES_LIST} DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1)
07070100000020000081A4000028240000003200000001607BF44800001027000000000000000000000000000000000000003B00000000vcontrold-v0.98.10+git20210418.977e6f5/doc/man/vclient.rst=========
 vclient
=========

--------------------------------------
tool for getting values from vcontrold
--------------------------------------

:Author: Frank Nobis fn@radio-do.de,
         other contributors see `vcontrold @GitHub <https://github.com/openv/vcontrold>`__
:Copyright: GPLv3
:Manual section: 1

SYNOPSIS
========

    vclient [-h <ip:port>] [-c <command1,command2,...>] [-f <commandfile>] [-s <csv-file>] [-t <template-file>] [-o <outputfile>] [-x <executablescript>] [-k] [-m] [-v] [-j] [-J]

DESCRIPTION
===========

vclient is the client program to communicate with the vcontrold daemon.
It features a template mode to prepare received data for logging or database input.

OPTIONS
=======

-h, \--host
    <IPv4:port> or <IPv6> of vcontrold. Defaults to localhost

-p, \--port
    <port> of vcontrold when using IPv6. Defaults to 3002 

-c, \--command <commandlist>
    comma-separated list of commands

-f, \-commandfile <commandfile>
    optional file with commands, one command per line

-s, \--csvfile
    output results in CSV format

-t, \--template <templatefile>
    template file, variables get replaced by returned values

-o, \--output <outputfile>
    redirect stdout into <outputfile>

-x, \--execute <scriptfile>
    write template output to <scriptfile>, then run the generated <scriptfile>

-m, \--munin
    Munin datalogger compatible output, omits units and error details

-k, \--cacti
    Cacti datalogger compatible output, omits units and error details

-j, \--json-short
    JSON output short format (obeject with cmd as key and result as value)

-J, \--json-long
    JSON output long (list with objects per command, contains cmd, raw, value and error)
 
-v, \--verbose
    verbose mode

-V, \--Version
    print version info, then exit

-4, \--inet4
    IPv4 preferred

-6, \--inet6
    IPv6 preferred. If no option provided, us OS defaults.

\--help
    usage information 

TEMPLATE MODE
=============

In template mode, variables contained in the <templatefile> get
replaced with the values returned by a command issued in vclient.

Variables $1, $2, ..., $n gets replaced with 1st, 2nd, ..., nth return value.

The following variable types exist for conversion of return values:

+------------+-----------------------------------+
| variable   | function                          |
+------------+-----------------------------------+
| $1..$n     | return value converted to float   |
+------------+-----------------------------------+
| $R1..$Rn   | return value converted to text    |
+------------+-----------------------------------+
| $C1..$Cn   | issued command                    |
+------------+-----------------------------------+
| $E1..$En   | error message of command          |
+------------+-----------------------------------+

EXAMPLES
========

Preparation of a simple template for a database statement to insert some values in database:

::

    $ cat > sql.tmpl <<EOF
    INSERT INTO messwerte values (CURRENT_DATE,$1,$2);
    EOF

Calling ``vclient`` with the given template shows the variables being replaced by their values:

::

    $ vclient -h 127.0.0.1:1234 -t sql.tmpl -c gettempA,gettempWW
    INSERT INTO messwerte values (CURRENT\_DATE,-2.600000,54.299999);

These lines could be written directly into a database by piping the
output to a DB cli:

``$ vclient -h 127.0.0.1:1234 -t sql.tmpl -c gettempA,gettempWW 2>/dev/null | mysql -D vito``

The -o <outputfile> option writes (overwrites, not appends) the
output to the given file.

With the option -x <scriptfile*, the generated output is treated
like a script, which is run after output generation.

Example:

::

    $ cat > sh-example.tmpl <<EOF
    #!/bin/sh
    DB=vitodb.rrd
    echo "command 1: $C1 ; command 2: $C2"
    echo "rrdtool update $DB N:$1:$2"
    EOF

``$ vclient -h 127.0.0.1:1234 -c getTempA,getTempWW -t sh-example.tmpl -x sh-example.sh``

Output:

::

    command 1: getTempA ; command 2: getTempWW
    rrdb update vitodb.rrd N:-2.600000:54.299999

SEE ALSO
========

* man 1 vcontrold
* vcontrold @GitHub: `https://github.com/openv/vcontrold <https://github.com/openv/vcontrold>`__
07070100000021000081A4000028240000003200000001607BF44800000CE7000000000000000000000000000000000000003D00000000vcontrold-v0.98.10+git20210418.977e6f5/doc/man/vcontrold.rst===========
 vcontrold
===========

----------------------------------------------------------
Unix daemon for communication with Viessmann Vito heatings
----------------------------------------------------------

:Author: Frank Nobis fn@radio-do.de,
         other contributors see `vcontrold @GitHub <https://github.com/openv/vcontrold>`__
:Copyright: GPLv3
:Manual section: 1

SYNOPSIS
========

  vcontrold [-x <xml-file>] [-d <device>] [-l <logfile>] [-p <port>] [-s] [-n]
    [-c <command-file>] [-P <pid-file>] [-U <username>] [-G <groupname>]
    [-i] [-g] [-4] [-6] [-v] [-V] [-?]

DESCRIPTION
===========

vcontrold uses a serial optical link or an IP connection to communicate with
a Viessmann vito heating controller.

OPTIONS
=======

-x <xml-file>, \--xmlfile <xml-file>
    location of the main config file

-d <device>, \--device <device>
    serial device to use.
    This option overrides corresponding entry in the config file.

-l <logfile>, \--logfile <logfile>
    use <logfile> instead of syslog.

-p <port>, \--port <port>
    TCP <port> to use for remote connections.
    The default is 3002 and can be specified in the config file.
    This option overrides the corresponding entry in the config file.

-s, \--syslog
    use syslog

-n, \--nodaemon
    do not fork. This is for testing purpose only. Normaly vcontrold
    will detach from the controlling terminal and put itself into the
    background.

-c <command-file>, \--commandfile <command-file>
    file with lines containing sequences of icmds (WAIT, SEND, RECV, PAUSE)
    as used in protocol definitions.
    Lines get executed in order
    (developer option)

-P <pid-file>, \--pidfile <pid-file>
    write process id to <pid-file> when started as a daemon.
    When started as root, <pid-file> is written prior to dropping privileges.
    This overrides the corresponding entry in the config file.

-U <username>, \--username <username>
    when started by root, drop privileges to user <username>
    instead of user nobody. This overrides the corresponding entry in the config file.
    If using a serial link, ensure that user or group has access rights to the serial device.

-G <groupname>, \--groupname <groupname>
    when started by root, drop privileges to group <groupname>
    instead of group dialout. This overrides the corresponding entry in the config file.
    If using a serial link, ensure that user or group has access rights to the serial device.

-i, \--vsim
    use a temp file in ``/tmp/sim-devid.ini`` for use with the vsim simulator
    (developer option)

-g, \--debug
    enable debug mode

-4, --inet4
    use IP v4 socket

-6, --inet6
    use IP v6 socket

-v, \--verbose
    verbose mode

-V, \--Version
    print version information, then exit

-?, \--help
    usage information

FILES
=====

``/etc/vcontrold/vcontrold.xml``
    These are the programm specific configurations. E.g. device, baudrate,
    IP etc.

``/etc/vcontrold/vito.xml``
    These are the command definitions for the devices in use.

LICENSE
=======

The vcontrold software, its accompanying files and documentation
are licensed under the **GPLv3**.
See COPYING in the package.

SEE ALSO
========

* man 1 vclient
* vcontrold @GitHub: `https://github.com/openv/vcontrold <https://github.com/openv/vcontrold>`__
07070100000022000081A4000028240000003200000001607BF44800000296000000000000000000000000000000000000003800000000vcontrold-v0.98.10+git20210418.977e6f5/doc/man/vsim.rst======
 vsim
======

-----------------------------
simulation tool for vcontrold
-----------------------------

:Author: Frank Nobis fn@radio-do.de,
         other contributors see `vcontrold @GitHub <https://github.com/openv/vcontrold>`__
:Copyright: GPLv3
:Manual section: 1


DESCRIPTION
===========

``vsim`` is a simulation tool for the ``vcontrold`` daemon to simulate a connected Viessmann device.

It is a simple developer tool, and features only a limited commandset which is not runtime configurable.

SEE ALSO
========

* man 1 vcontrold
* man 1 vclient
* vcontrold @GitHub: `https://github.com/openv/vcontrold <https://github.com/openv/vcontrold>`__
07070100000023000041ED000028240000003200000002607BF44800000000000000000000000000000000000000000000002B00000000vcontrold-v0.98.10+git20210418.977e6f5/src07070100000024000081A4000028240000003200000001607BF44800003C3A000000000000000000000000000000000000003800000000vcontrold-v0.98.10+git20210418.977e6f5/src/arithmetic.c/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// Calculation of arithmetic expressions

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define HEX 8
#define HEXDIGIT 10
#define DIGIT 11
#define PUNKT 12
#define END 0
#define ERROR -100
#define PLUS 100
#define MINUS 101
#define MAL 102
#define GETEILT 103
#define MODULO 104
#define KAUF 110
#define KZU 111
#define BYTE0 200
#define BYTE1 201
#define BYTE2 202
#define BYTE3 203
#define BYTE4 204
#define BYTE5 205
#define BYTE6 206
#define BYTE7 207
#define BYTE8 208
#define BYTE9 209
#define PBYTE0 210
#define PBYTE1 211
#define PBYTE2 212
#define PBYTE3 213
#define PBYTE4 214
#define PBYTE5 215
#define PBYTE6 216
#define PBYTE7 217
#define PBYTE8 218
#define PBYTE9 219
#define BITPOS 220
#define VALUE 300
#define NICHT 400
#define UND 401
#define ODER 402
#define XOR 403
#define SHL 404
#define SHR 405

int nextToken(char **str, char **c, int *count);
void  pushBack(char **str, int n);
float execExpression(char **str, unsigned char *bPtr, float floatV, char *err);
float execTerm(char **str, unsigned char *bPtr, float floatV, char *err);
float execFactor(char **str, unsigned  char *bPtr, float floatV, char *err);
int execIExpression(char **str, unsigned char *bPtr, char bitpos, char *pPtr, char *err);
int execITerm(char **str, unsigned char *bPtr, char bitpos, char *pPtr, char *err);
int execIFactor(char **str, unsigned char *bPtr, char bitpos, char *pPtr, char *err);

float execExpression(char **str, unsigned char *bInPtr, float floatV, char *err)
{
    int f = 1;
    float term1, term2;
    //float exp1,exp2;
    char *item;
    //unsigned char bPtr[10];
    unsigned char bPtr[10];
    int n;

    //printf("execExpression: %s\n",*str);

    // Tweak bPtr Bytes 0..9 and copy them to nPtr
    // We did not receive characters
    for (n = 0; n <= 9; n++) {
        //bPtr[n]=*bInPtr++ & 255;
        bPtr[n] = *bInPtr++;
    }

    switch (nextToken(str, &item, &n)) {
    case PLUS:
        f = 1;
        break;
    case MINUS:
        f = -1;
        break;
    default:
        pushBack(str, n);
        break;
    }

    term1 = execTerm(str, bPtr, floatV, err) * f;
    if (*err) {
        return 0;
    }
    //printf(" T1=%f\n",term1);

    int t;
    while ((t = nextToken(str, &item, &n)) != END) {
        f = 1;
        switch (t) {
        case PLUS:
            f = 1;
            break;
        case MINUS:
            f = -1;
            break;
        default:
            //printf(" Exp=%f\n",term1);
            pushBack(str, n);
            return term1;
        }

        term2 = execTerm(str, bPtr, floatV, err);
        if (*err) {
            return 0;
        }
        //printf(" T2=%f\n",term2);
        term1 += term2 * f;
    }

    //printf(" Exp=%f\n",term1);
    return term1;
}

float execTerm(char **str, unsigned char *bPtr, float floatV, char *err)
{
    float factor1, factor2;
    int op;
    char *item;
    int n;

    //printf("execTerm: %s\n",*str);

    factor1 = execFactor(str, bPtr, floatV, err);
    if (*err) {
        return 0;
    }

    //printf(" F1=%f\n",factor1);
    while (1) {
        switch (nextToken(str, &item, &n)) {
        case MAL:
            op = MAL;
            break;
        case GETEILT:
            op = GETEILT;
            break;
        default:
            pushBack(str, n);
            //printf("  ret(%f)\n",factor1);
            return factor1;
        }
        factor2 = execFactor(str, bPtr, floatV, err);
        //printf(" F2=%f\n",factor2);
        if (*err) {
            return 0;
        }
        if (op == MAL) {
            factor1 *= factor2;
        } else {
            factor1 /= factor2;
        }
    }
}

float execFactor(char **str, unsigned char *bPtr, float floatV, char *err)
{
    char nstring[100];
    float expression;
    float factor;
    char *nPtr;
    char *item;
    char token;
    int n;

    //printf("execFactor: %s\n",*str);

    switch (nextToken(str, &item, &n)) {
    case BYTE0:
        return bPtr[0];
    case BYTE1:
        return bPtr[1];
    case BYTE2:
        return bPtr[2];
    case BYTE3:
        return bPtr[3];
    case BYTE4:
        return bPtr[4];
    case BYTE5:
        return bPtr[5];
    case BYTE6:
        return bPtr[6];
    case BYTE7:
        return bPtr[7];
    case BYTE8:
        return bPtr[8];
    case BYTE9:
        return bPtr[9];
    case VALUE:
        return floatV;
    case HEX:
        nPtr = nstring;
        memset(nstring, 0, sizeof(nstring));
        strcpy(nstring, "0x");
        nPtr += 2;
        token = nextToken(str, &item, &n);
        while ((token == DIGIT) || (token == HEXDIGIT)) {
            *nPtr++ = *item;
            token = nextToken(str, &item, &n);
        }
        pushBack(str, n);
        sscanf(nstring, "%f", &factor);
        return factor;
    case DIGIT:
        nPtr = nstring;
        do {
            *nPtr++ = *item;
        } while ((token = nextToken(str, &item, &n)) == DIGIT);
        // If a . follows, we have a decimal number
        if (token == PUNKT) {
            do {
                *nPtr++ = *item;
            } while ((token = nextToken(str, &item, &n)) == DIGIT);
        }
        pushBack(str, n);
        *nPtr = '\0';
        factor = atof(nstring);
        //printf("  Zahl: %s (f:%f)\n",nstring,factor);
        return factor;
    case KAUF:
        expression = execExpression(str, bPtr, floatV, err);
        if (*err) {
            return 0;
        }
        if (nextToken(str, &item, &n) != KZU) {
            sprintf(err, "expected factor:) [%c]\n", *item);
            return 0;
        }
        return expression;
    default:
        sprintf(err, "expected factor: B0..B9 number ( ) [%c]\n", *item);
        return 0;
    }
}

int execIExpression(char **str, unsigned char *bInPtr, char bitpos, char *pPtr, char *err)
{
    int f = 1;
    int term1, term2;
    //int exp1, exp2;
    int op;
    char *item;
    unsigned char bPtr[10];
    int n;

    //printf("execExpression: %s\n", *str);

    // Tweak bPtr bytes 0..9 and copy them to nPtr
    // We have received characters
    for (n = 0; n <= 9; n++) {
        //bPtr[n]=*bInPtr++ & 255;
        bPtr[n] = *bInPtr++;
    }

    op = ERROR;
    switch (nextToken(str, &item, &n)) {
    case PLUS:
        op = PLUS;
        break;
    case MINUS:
        op = MINUS;
        break;
    case NICHT:
        op = NICHT;
        break;
    default:
        pushBack(str, n);
        break;
    }

    if (op == MINUS) {
        term1 = execITerm(str, bPtr, bitpos, pPtr, err) * -1;
    } else if (op == NICHT) {
        term1 = ~(execITerm(str, bPtr, bitpos, pPtr, err));
    } else {
        term1 = execITerm(str, bPtr, bitpos, pPtr, err);
    }

    if (*err) {
        return 0;
    }

    int t;
    op = ERROR;
    while ((t = nextToken(str, &item, &n)) != END) {
        f = 1;
        switch (t) {
        case PLUS:
            op = PLUS;
            break;
        case MINUS:
            op = MINUS;
            break;
        case NICHT:
            op = NICHT;
            break;
        default:
            pushBack(str, n);
            return term1;
        }

        if (op == MINUS) {
            term2 = execITerm(str, bPtr, bitpos, pPtr, err) * -1;
        } else if (op == NICHT) {
            term2 = ~(execITerm(str, bPtr, bitpos, pPtr, err));
        } else if (op == PLUS) {
            term2 = execITerm(str, bPtr, bitpos, pPtr, err);
        } if (*err) {
            return 0;
        }
        term1 += term2;
    }

    return term1;
}

int execITerm(char **str, unsigned char *bPtr, char bitpos, char *pPtr, char *err)
{
    int factor1, factor2;
    int op;
    char *item;
    int n;

    //printf("execTerm: %s\n",*str);

    factor1 = execIFactor(str, bPtr, bitpos, pPtr, err);
    if (*err) {
        return 0;
    }

    while (1) {
        switch (nextToken(str, &item, &n)) {
        case MAL:
            op = MAL;
            break;
        case GETEILT:
            op = GETEILT;
            break;
        case MODULO:
            op = MODULO;
            break;
        case UND:
            op = UND;
            break;
        case ODER:
            op = ODER;
            break;
        case XOR:
            op = XOR;
            break;
        case SHL:
            op = SHL;
            break;
        case SHR:
            op = SHR;
            break;
        default:
            pushBack(str, n);
            //printf("  ret(%f)\n",factor1);
            return factor1;
        }

        factor2 = execIFactor(str, bPtr, bitpos, pPtr, err);

        if (*err) {
            return 0;
        }

        if (op == MAL) {
            factor1 *= factor2;
        } else if (op == GETEILT) {
            factor1 /= factor2;
        } else if (op == MODULO) {
            factor1 %= factor2;
        } else if (op == UND) {
            factor1 &= factor2;
        } else if (op == ODER) {
            factor1 |= factor2;
        } else if (op == XOR) {
            factor1 ^= factor2;
        } else if (op == SHL) {
            factor1 <<= factor2;
        } else if (op == SHR) {
            factor1 >>= factor2;
        } else {
            sprintf(err, "Error exec ITerm: Unknown token %d", op);
            return 0;
        }
    }
}

int execIFactor(char **str, unsigned char *bPtr, char bitpos, char *pPtr, char *err)
{
    char nstring[100];
    int expression;
    int factor;
    char *nPtr;
    char *item;
    char token;
    int n;

    //printf("execFactor: %s\n",*str);

    switch (nextToken(str, &item, &n)) {
    case BYTE0:
        return ((int)bPtr[0]) & 0xff;
    case BYTE1:
        return ((int)bPtr[1]) & 0xff;
    case BYTE2:
        return ((int)bPtr[2]) & 0xff;
    case BYTE3:
        return ((int)bPtr[3]) & 0xff;
    case BYTE4:
        return ((int)bPtr[4]) & 0xff;
    case BYTE5:
        return ((int)bPtr[5]) & 0xff;
    case BYTE6:
        return ((int)bPtr[6]) & 0xff;
    case BYTE7:
        return ((int)bPtr[7]) & 0xff;
    case BYTE8:
        return ((int)bPtr[8]) & 0xff;
    case BYTE9:
        return ((int)bPtr[9]) & 0xff;
    case BITPOS:
        return ((int)bitpos) & 0xff;
    case PBYTE0:
        return ((int)pPtr[0]) & 0xff;
    case PBYTE1:
        return ((int)pPtr[1]) & 0xff;
    case PBYTE2:
        return ((int)pPtr[2]) & 0xff;
    case PBYTE3:
        return ((int)pPtr[3]) & 0xff;
    case PBYTE4:
        return ((int)pPtr[4]) & 0xff;
    case PBYTE5:
        return ((int)pPtr[5]) & 0xff;
    case PBYTE6:
        return ((int)pPtr[6]) & 0xff;
    case PBYTE7:
        return ((int)pPtr[7]) & 0xff;
    case PBYTE8:
        return ((int)pPtr[8]) & 0xff;
    case PBYTE9:
        return ((int)pPtr[9]) & 0xff;
    case HEX:
        nPtr = nstring;
        memset(nstring, 0, sizeof(nstring));
        strcpy(nstring, "0x");
        nPtr += 2;
        token = nextToken(str, &item, &n);
        while ((token == DIGIT) || (token == HEXDIGIT)) {
            *nPtr++ = *item;
            token = nextToken(str, &item, &n);
        }
        pushBack(str, n);
        sscanf(nstring, "%i", &factor);
        return factor;
    case DIGIT:
        nPtr = nstring;
        do {
            *nPtr++ = *item;
        } while ((token = nextToken(str, &item, &n)) == DIGIT);
        // If a . follows, we have a decimal number
        if (token == PUNKT) {
            do {
                *nPtr++ = *item;
            } while ((token = nextToken(str, &item, &n)) == DIGIT);
        }
        pushBack(str, n);
        *nPtr = '\0';
        factor = atof(nstring);
        return factor;
    case KAUF:
        expression = execIExpression(str, bPtr, bitpos, pPtr, err);
        if (*err) {
            return 0;
        }
        if (nextToken(str, &item, &n) != KZU) {
            sprintf(err, "expected factor:) [%c]\n", *item);
            return 0;
        }
        return expression;
    case NICHT:
        return ~execIFactor(str, bPtr, bitpos, pPtr, err);
    default:
        sprintf(err, "expected factor: B0..B9 P0..P9 BP number ( ) [%c]\n", *item);
        return 0;
    }
}

int nextToken(char **str, char **c, int *count)
{
    char item;

    //printf("\tInput String:%s\n",*str);

    item = **str;
    while (isblank(item)) {
        item = *(++*str);
    }

    *c = *str;
    (*str)++;
    //printf("\t  Token: %c   [ %s ] \n",**c,*str);
    *count = 1;

    switch (**c) {
    case '+':
        return PLUS;
    case '-':
        return MINUS;
    case '*':
        return MAL;
    case '/':
        return GETEILT;
    case '%':
        return MODULO;
    case '(':
        return KAUF;
    case ')':
        return KZU;
    case 'V':
        return VALUE;
    case '^':
        return XOR;
    case '&':
        return UND;
    case '|':
        return ODER;
    case '~':
        return NICHT;
    case '0':
        if (*(*str) == 'x') {
            (*str)++;
            *count = 2;
            return HEX;
        }
        return DIGIT;
    case '<':
        *count = 2;
        switch (*(*str)++) {
        case '<' :
            return SHL;
        }
    case '>':
        *count = 2;
        switch (*(*str)++) {
        case '>':
            return SHR;
        }
    case 'B':
        *count = 2;
        switch (*(*str)++) {
        case '0':
            return BYTE0;
        case '1':
            return BYTE1;
        case '2':
            return BYTE2;
        case '3':
            return BYTE3;
        case '4':
            return BYTE4;
        case '5':
            return BYTE5;
        case '6':
            return BYTE6;
        case '7':
            return BYTE7;
        case '8':
            return BYTE8;
        case '9':
            return BYTE9;
        case 'P':
            return BITPOS;
        }
    case 'P':
        *count = 2;
        switch (*(*str)++) {
        case '0':
            return PBYTE0;
        case '1':
            return PBYTE1;
        case '2':
            return PBYTE2;
        case '3':
            return PBYTE3;
        case '4':
            return PBYTE4;
        case '5':
            return PBYTE5;
        case '6':
            return PBYTE6;
        case '7':
            return PBYTE7;
        case '8':
            return PBYTE8;
        case '9':
            return PBYTE9;
        }
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
        return DIGIT;
    case 'a':
    case 'b':
    case 'c':
    case 'd':
    case 'e' :
    case 'f':
        return HEXDIGIT;
    case '.':
        return PUNKT;
    case '\0':
        return END;
    default:
        return ERROR;
    }
}

void  pushBack(char **str, int count)
{
    (*str) -= count;
    //printf("\t<<::%s\n",*str);
}
07070100000025000081A4000028240000003200000001607BF448000003D6000000000000000000000000000000000000003800000000vcontrold-v0.98.10+git20210418.977e6f5/src/arithmetic.h/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// Calculation of arithmetic expressions

#ifndef ARITHMETIC_H
#define ARITHMETIC_H

float execExpression(char **str, char *bPtr, float floatV, char *err);
int execIExpression(char **str, char *bPtr, char bitpos, char *pPtr, char *err);

#endif // ARITHMETIC_H
07070100000026000081A4000028240000003200000001607BF44800001B6C000000000000000000000000000000000000003400000000vcontrold-v0.98.10+git20210418.977e6f5/src/client.c/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// Client routines for vcontrold queries

#include <syslog.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>
#include <ctype.h>

#include "client.h"
#include "prompt.h"
#include "common.h"
#include "socket.h"

static void sig_alrm(int);
static jmp_buf  env_alrm;

int sendTrList(int sockfd, trPtr ptr);

trPtr newTrNode(trPtr ptr)
{
    trPtr nptr;

    if (ptr && ptr->next) {
        return newTrNode(ptr->next);
    }

    nptr = calloc(1, sizeof(*ptr));
    if (! nptr) {
        fprintf(stderr, "malloc failed\n");
        exit(1);
    }

    if (ptr) {
        ptr->next = nptr;
    }

    return nptr;
}

ssize_t recvSync(int fd, char *wait, char **recv)
{
    char *rptr;
    char *pptr;
    char c;
    ssize_t count;
    int rcount = 1;

    if (signal(SIGALRM, sig_alrm) == SIG_ERR) {
        logIT1(LOG_ERR, "SIGALRM error");
    }

    if (setjmp(env_alrm) != 0) {
        logIT(LOG_ERR, "timeout wait:%s", wait);
        return -1;
    }

    alarm(CL_TIMEOUT);

    if (! (*recv = calloc(ALLOCSIZE, sizeof(char)))) {
        logIT1(LOG_ERR, "calloc error");
        exit(1);
    }

    rptr = *recv;
    size_t i = 0;
    while ((count = readn(fd, &c, 1))) {
        alarm(0);
        if (count < 0) {
            continue;
        }

        *rptr++ = c;
        *(rptr + 1) = '\0';
        i++;
        if (! ((rptr - *recv + 1) % ALLOCSIZE)) {
            char *tmp = realloc(*recv, ALLOCSIZE * sizeof(char) *  ++rcount);
            if (tmp == NULL) {
                logIT1(LOG_ERR, "realloc error");
                exit(1);
            } else {
                *recv = tmp;
                rptr = *recv + i;
            }
        }

        if ((pptr = strstr(*recv, wait))) {
            *pptr = '\0';
            logIT(LOG_INFO, "recv:%s", *recv);
            break;
        }

        alarm(CL_TIMEOUT);

    }

    char *tmp = realloc(*recv, strlen(*recv) + 1);
    if (tmp == NULL) {
        logIT1(LOG_ERR, "realloc error");
        exit(1);
    } else {
        *recv = tmp;
    }

    if (count <= 0) {
        logIT(LOG_ERR, "exit with count=%ld", count);;
    }

    return count;
}

// port is never 0, which is a bad number for a tcp port
int connectServer(char *host, int port)
{
    int sockfd;

    if (host[0] != '/' ) {
        sockfd = openCliSocket(host, port, 0);
        if (sockfd) {
            logIT(LOG_INFO, "Setup connection to %s port %d", host, port);
        } else {
            logIT(LOG_INFO, "Setting up connection to %s port %d failed", host, port);
            return -1;
        }
    } else {
        logIT(LOG_ERR, "Host format: IP|Name:Port");
        return -1;
    }
    return sockfd;
}

void disconnectServer(int sockfd)
{
    char string[8];
    char *ptr;

    snprintf(string, sizeof(string), "quit\n");
    sendServer(sockfd, string, strlen(string));
    recvSync(sockfd, BYE, &ptr);
    free(ptr);
    close(sockfd);
}

size_t sendServer(int fd, char *s_buf, size_t len)
{
    char string[256];

    // Empty buffer
    // As tcflush does not work correctly, we use nonblocking read
    fcntl(fd, F_SETFL, O_NONBLOCK);
    while (readn(fd, string, sizeof(string)) > 0) { }
    fcntl(fd, F_SETFL, ! O_NONBLOCK);
    return Writen(fd, s_buf, len);
}

trPtr sendCmdFile(int sockfd, const char *filename)
{
    FILE *filePtr;
    char line[MAXBUF];
    trPtr ptr;
    trPtr startPtr = NULL;

    if (! (filePtr = fopen(filename, "r"))) {
        return NULL;
    } else {
        logIT(LOG_INFO, "Opened command file %s", filename);
    }

    memset(line, 0, sizeof(line));
    while (fgets(line, MAXBUF - 1, filePtr)) {
        ptr = newTrNode(startPtr);
        if (! startPtr) {
            startPtr = ptr;
        }
        ptr->cmd = calloc(strlen(line), sizeof(char));
        strncpy(ptr->cmd, line, strlen(line) - 1);
    }

    if (! sendTrList(sockfd, startPtr)) {
        // Something with the communication went wrong
        return NULL;
    }

    return startPtr;
}

trPtr sendCmds(int sockfd, char *commands)
{
    char *sptr;
    trPtr ptr;
    trPtr startPtr = NULL;

    sptr = strtok(commands, ",");
    do {
        ptr = newTrNode(startPtr);
        if (! startPtr) {
            startPtr = ptr;
        }
        ptr->cmd = calloc(strlen(sptr) + 1, sizeof(char));
        strncpy(ptr->cmd, sptr, strlen(sptr));
    } while ((sptr = strtok(NULL, ",")) != NULL);

    if (! sendTrList(sockfd, startPtr))
    {
        // Something with the communication went wrong
        return NULL;
    }

    return startPtr;
}

int sendTrList(int sockfd, trPtr ptr)
{
    char string[1000 + 1];
    char prompt[] = PROMPT;
    char errTXT[] = ERR;
    char *sptr;
    char *dumPtr;

    if (recvSync(sockfd, prompt, &sptr) <= 0) {
        free(sptr);
        return 0;
    }

    while (ptr) {
        //memset(string, 0,sizeof(string));
        snprintf(string, sizeof(string), "%s\n", ptr->cmd);

        if (sendServer(sockfd, string, strlen(string)) <= 0) {
            return 0;
        }

        //memset(string, 0,sizeof(string));
        logIT(LOG_INFO, "SEND:%s", ptr->cmd);
        if (recvSync(sockfd, prompt, &sptr) <= 0) {
            free(sptr);
            return 0;
        }

        ptr->raw = sptr;
        if (iscntrl(*(ptr->raw + strlen(ptr->raw) - 1))) {
            *(ptr->raw + strlen(ptr->raw) - 1) = '\0';
        }

        dumPtr = calloc(strlen(sptr) + 20, sizeof(char));
        snprintf(dumPtr, (strlen(sptr) + 20) * sizeof(char), "RECV:%s", sptr);
        logIT1(LOG_INFO, dumPtr);
        free(dumPtr);

        // We fill errors and result
        if (strstr(ptr->raw, errTXT) == ptr->raw) {
            ptr->err = ptr->raw;
            fprintf(stderr, "SRV %s\n", ptr->err);
        } else {
            // Here, we search the first word in raw and save it as result
            char *rptr;
            char len;
            rptr = strchr(ptr->raw, ' ');
            if (! rptr) {
                rptr = ptr->raw + strlen(ptr->raw);
            }

            len = rptr - ptr->raw;
            ptr->result = atof(ptr->raw);
            ptr->err = NULL;
        }

        ptr = ptr->next;
    }

    return 1;
}

static void sig_alrm(int signo)
{
    longjmp(env_alrm, 1);
}
07070100000027000081A4000028240000003200000001607BF4480000050D000000000000000000000000000000000000003400000000vcontrold-v0.98.10+git20210418.977e6f5/src/client.h/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef CLIENT_H
#define CLIENT_H

#define CL_TIMEOUT 25

#ifndef MAXBUF
#define MAXBUF 4096
#endif

#define ALLOCSIZE 256

typedef struct txRx *trPtr;

ssize_t recvSync(int fd, char *wait, char **recv);
int connectServer(char *host, int port);
void disconnectServer(int sockfd);
size_t sendServer(int fd, char *s_buf, size_t len);
trPtr sendCmdFile(int sockfd, const char *tmpfile);
trPtr sendCmds(int sockfd, char *commands);

struct txRx {
    char *cmd;
    float result;
    char *err;
    char *raw;
    time_t timestamp;
    trPtr next;
} TxRx;

#endif // CLIENT_H
07070100000028000081A4000028240000003200000001607BF44800001248000000000000000000000000000000000000003400000000vcontrold-v0.98.10+git20210418.977e6f5/src/common.c/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// Common functions for vcontrold like logging and converting

#define _GNU_SOURCE

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <syslog.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <stdarg.h>

#include "common.h"

int syslogger = 0;
int debug = 0;
FILE *logFD;
char errMsg[2000];
int errClass = 99;
int dbgFD = -1;

int initLog(int useSyslog, char *logfile, int debugSwitch)
{
    // If needed, opes the syslog or log file
    if (useSyslog) {
        syslogger = 1;
        openlog("vito", LOG_PID, LOG_LOCAL0);
        syslog(LOG_LOCAL0, "vito started");
    }

    if (logfile) {
        // Log file is not NULL and not empty
        if (strcmp(logfile, "") == 0) {
            return 0;
        }

        logFD = fopen(logfile, "a");
        if (! logFD) {
            printf("Could not open %s: %s", logfile, strerror (errno));
            return 0 ;
        }
    }

    debug = debugSwitch;
    memset(errMsg, 0, sizeof(errMsg));

    return 1;
}

void logIT (int class, char *string, ...)
{
    va_list arguments;
    time_t t;
    char *tPtr;
    char *cPtr;
    time(&t);
    tPtr = ctime(&t);
    char *print_buffer;
    int pid;
    long avail;

    va_start(arguments, string);
    vasprintf(&print_buffer, string, arguments);
    va_end(arguments);

    if (class <= LOG_ERR)  {
        avail = sizeof(errMsg) - strlen(errMsg) - 2;
        if ( avail > 0 ) {
            strncat(errMsg, print_buffer, avail);
            strcat(errMsg, "\n");
        } else {
            strcpy(&errMsg[sizeof(errMsg) - 12], "OVERFLOW\n");
            // Should solve the semop error
        }
    }

    errClass = class;
    // Remove control characters
    cPtr = tPtr;
    while (*cPtr) {
        if (iscntrl(*cPtr)) {
            *cPtr = ' ';
        }
        cPtr++;
    }

    if (dbgFD >= 0) {
        // The debug FD is set and we firstly send the info there
        dprintf(dbgFD, "DEBUG:%s: %s\n", tPtr, print_buffer);
    }

    if (! debug && (class  > LOG_NOTICE)) {
        free(print_buffer);
        return;
    }

    pid = getpid();

    if (syslogger) {
        syslog(class, "%s", print_buffer);
    }

    if (logFD) {
        fprintf(logFD, "[%d] %s: %s\n", pid, tPtr, print_buffer);
        fflush(logFD);
    }

    // Output only if 2 is open as STDERR
    if (isatty(2)) {
        fprintf(stderr, "[%d] %s: %s\n", pid, tPtr, print_buffer);
    }

    free(print_buffer);
}

void sendErrMsg(int fd)
{
    char string[256];

    if ((fd >= 0) && (errClass <= 3)) {
        snprintf(string, sizeof(string), "ERR: %s", errMsg);
        write(fd, string, strlen(string));
        errClass = 99; // Thus it's only displayed once
        memset(errMsg, 0, sizeof(errMsg));
    }

    *errMsg = '\0';
    // Back to start, no matter if we actually output
    // Can be commented out for debugging, then we get the errors in errMsg
}

void setDebugFD(int fd)
{
    dbgFD = fd;
}

char hex2chr(char *hex)
{
    char buffer[16];
    int hex_value = -1;

    snprintf(buffer, sizeof(buffer), "0x%s", hex);
    if (sscanf(hex, "%x", &hex_value) != 1) {
        logIT(LOG_WARNING, "Invalid hex char in %s", hex);
    }

    return hex_value;
}

int char2hex(char *outString, const char *charPtr, int len)
{
    int n;
    char string[MAXBUF];

    memset(string, 0, sizeof(string));
    for (n = 0; n < len; n++) {
        unsigned char byte = *charPtr++ & 255;
        snprintf(string, sizeof(string), "%02X ", byte);
        strcat(outString, string);
    }

    // Remove last space
    outString[strlen(outString) - 1] = '\0';

    return len;
}

short string2chr(char *line, char *buf, short bufsize)
{
    char *sptr;
    short count;

    count = 0;

    sptr = strtok(line, " ");
    do {
        if (*sptr == ' ') {
            continue;
        }
        buf[count++] = hex2chr(sptr);
    } while ((sptr = strtok(NULL, " ")) && (count < bufsize));

    return count;
}
07070100000029000081A4000028240000003200000001607BF448000004F8000000000000000000000000000000000000003400000000vcontrold-v0.98.10+git20210418.977e6f5/src/common.h/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef COMMON_H
#define COMMON_H

int initLog(int useSyslog, char *logfile, int debugSwitch);
void logIT (int class, char *string, ...);
char hex2chr(char *hex);
int char2hex(char *outString, const char *charPtr, int len);
short string2chr(char *line, char *buf, short bufsize);
void sendErrMsg(int fd);
void setDebugFD(int fd);
ssize_t readn(int fd, void *vptr, size_t n);

#ifndef MAXBUF
#define MAXBUF 4096
#endif

#ifndef DEFAULT_PORT
#define DEFAULT_PORT 3002
#endif

#define logIT1(class, string) logIT(class, "%s", string)

#endif // COMMON_H
0707010000002A000081A4000028240000003200000001607BF44800004A5E000000000000000000000000000000000000003400000000vcontrold-v0.98.10+git20210418.977e6f5/src/framer.c/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/*
 * Framer interface
 *
 * For P300 framing is supported here
 *
 * Control is by a controlling byte P300_LEADING = 0x41
 *
 * with open and close P300 Mode is switched on, there is new xml-tag <pid> with protocol
 * definition which controls the switching of vitotronic to P300 mode.
 * additional assuming PDUs start by P300_LEADIN, else transferred as send by client
 * TODO: when PID is set, there is no need for defining a controlling x41 in getaddr etc.
 *
 * semaphore handling in vcontrol.c is changed to cover all from open until close to avoid
 * disturbance by other client trying to do uncoordinated open/close
 *
 * 2013-01-31 vheat
 */

#include <stdlib.h>
#include <stdio.h>
#include <syslog.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

#include "common.h"
#include "io.h"
#include "framer.h"

typedef unsigned short int uint16;

// general marker of P300 protocol
#define P300_LEADIN 0x41
#define P300_RESET  0x04
#define P300_ENABLE { 0x16, 0x00, 0x00 }

// message type
#define P300_REQUEST      0x00
#define P300_RESPONSE     0x01
#define P300_ERROR_REPORT 0x03
#define P300X_LINK_MNT    0x0f

// function
#define P300_READ_DATA  0x01
#define P300_WRITE_DATA 0x02
#define P300_FUNCT_CALL 0x07

#define P300X_OPEN     0x01
#define P300X_CLOSE    0x00
#define P300X_ATTEMPTS 3

// response
#define P300_ERROR             0x15
#define P300_NOT_INIT          0x05
#define P300_INIT_OK           0x06

// message buffer structure
#define P300_LEADIN_OFFSET     0
#define P300_LEN_OFFSET        1
#define P300_TYPE_OFFSET       2
#define P300_FCT_OFFSET        3
#define P300_ADDR_OFFSET       4
#define P300_RESP_LEN_OFFSET   6
#define P300_BUFFER_OFFSET     7

#define P300_LEADIN_LEN        1
#define P300_LEN_LEN           1
#define P300_CRC_LEN           1
#define P300_EXTRA_BYTES       (P300_LEADIN_LEN + P300_LEN_LEN + P300_CRC_LEN)

#define FRAMER_READ_ERROR      (-1)
#define FRAMER_LINK_STATUS(st) (0xFE00 + st)
#define FRAMER_READ_TIMEOUT    0

#define FRAMER_NO_ADDR         ((uint16) (-1))

// current active command
static uint16 framer_current_addr = FRAMER_NO_ADDR; // stored value depends on Endianess

// current active protocol
static char framer_pid = 0;

// status handling of current command
static void framer_set_actaddr(void *pdu)
{
    char string[100];
    uint16 framer_old_addr;

    if (framer_current_addr != FRAMER_NO_ADDR) {
        snprintf(string, sizeof(string),
                 ">FRAMER: addr was still active %04X",
                 framer_current_addr);
        logIT(LOG_ERR, string);
    }
    framer_old_addr = framer_current_addr;
    framer_current_addr = *(uint16 *) (((char *) pdu) + P300_ADDR_OFFSET);
    snprintf(string, sizeof(string),
            ">FRAMER: framer_set_actaddr framer_current_addr = %04X (was %04X)",
            framer_current_addr, framer_old_addr);
    logIT(LOG_DEBUG, string);
}

static void framer_reset_actaddr(void)
{
    char string[100];
    snprintf(string, sizeof(string),
            ">FRAMER: framer_reset_actaddr framer_current_addr = FRAMER_NO_ADDR (was %04X)",
            framer_current_addr);
    logIT(LOG_DEBUG, string);
    framer_current_addr = FRAMER_NO_ADDR;
}

static int framer_check_actaddr(void *pdu)
{
    char string[100];

    if (framer_current_addr != *(uint16 *) (((char *) pdu) + P300_ADDR_OFFSET)) {
        snprintf(string, sizeof(string),
                 ">FRAMER: addr corrupted stored %04X, now %04X",
                 framer_current_addr,
                 *(uint16 *) (((char *) pdu) + P300_ADDR_OFFSET));
        logIT(LOG_ERR, string);
        return -1;
    }
    return 0;
}

// TODO: could cause trouble on addr containing 0xFE
static void framer_set_result(char result)
{
    char string[100];
    snprintf(string, sizeof(string),
            ">FRAMER: framer_reset_actaddr framer_current_addr = FRAMER_LINK_STATUS(%02X) (was %04X)",
            result, framer_current_addr);
    logIT(LOG_DEBUG, string);
    framer_current_addr = FRAMER_LINK_STATUS(result);
}

static int framer_preset_result(char *r_buf, int r_len, unsigned long *petime)
{
    char string[100];

    if ((framer_pid == P300_LEADIN) &&
        ((framer_current_addr & FRAMER_LINK_STATUS(0)) == FRAMER_LINK_STATUS(0))) {
        r_buf[0] = (char) (framer_current_addr ^ FRAMER_LINK_STATUS(0));
        snprintf(string, sizeof(string), ">FRAMER: preset result %02X", r_buf[0]);
        logIT(LOG_INFO, string);
        return FRAMER_SUCCESS;
    }

    snprintf(string, sizeof(string), ">FRAMER: no preset result");
    logIT(LOG_INFO, string);
    return FRAMER_ERROR;
}

// Synchronization for P300 + switch to P300, back to normal for close -> repeating P300X_ATTEMPTS
static int framer_close_p300(int fd)
{
    char string[100];
    int i;
    char wbuf = P300_RESET;
    char rbuf = 0;
    unsigned long etime;
    int rlen;

    for (i = 0; i < P300X_ATTEMPTS; i++) {
        if (! my_send(fd, &wbuf, 1)) {
            framer_set_result(P300_ERROR);
            snprintf(string, sizeof(string), ">FRAMER: reset not send");
            logIT(LOG_ERR, string);
            return FRAMER_ERROR;
        }
        etime = 0;
        rlen = receive_nb(fd, &rbuf, 1, &etime);
        if (rlen < 0) {
            framer_set_result(P300_ERROR);
            snprintf(string, sizeof(string), ">FRAMER: close read failure for ack");
            logIT(LOG_ERR, string);
            return FRAMER_ERROR;
        } else if (rlen == 0) {
            framer_set_result(P300_ERROR);
            snprintf(string, sizeof(string), ">FRAMER: close read timeout for ack");
            logIT(LOG_ERR, string);
            return FRAMER_ERROR;
        } else if ((rbuf == P300_INIT_OK) || (rbuf == P300_NOT_INIT)) {
            framer_set_result(P300_NOT_INIT);
            snprintf(string, sizeof(string), ">FRAMER: closed");
            logIT(LOG_INFO, string);
            return FRAMER_SUCCESS;
        } else {
            snprintf(string, sizeof(string), ">FRAMER: unexpected data 0x%02X", rbuf);
            logIT(LOG_ERR, string);
            // continue anyway
        }
    }

    framer_set_result(P300_ERROR);
    snprintf(string, sizeof(string),
             ">FRAMER: could not close (%d attempts)", P300X_ATTEMPTS);
    logIT(LOG_ERR, string);
    return FRAMER_ERROR;
}

static int framer_open_p300(int fd)
{
    char string[100];
    int i;
    char rbuf = 0;
    char enable[] = P300_ENABLE;
    unsigned long etime;
    int rlen;

    for (i = 0; i < P300X_ATTEMPTS; i++) {
        if (! framer_close_p300(fd)) {
            snprintf(string, sizeof(string), ">FRAMER: could not set start condition");
            logIT(LOG_ERR, string);
            return FRAMER_ERROR;
        }

        if (! my_send(fd, enable, sizeof(enable))) {
            framer_set_result(P300_ERROR);
            snprintf(string, sizeof(string), ">FRAMER: enable not send");
            logIT(LOG_ERR, string);
            return FRAMER_ERROR;
        }

        etime = 0;
        rlen = receive_nb(fd, &rbuf, 1, &etime);
        if (rlen < 0) {
            framer_set_result(P300_ERROR);
            snprintf(string, sizeof(string), ">FRAMER: enable read failure for ack");
            logIT(LOG_ERR, string);
            return FRAMER_ERROR;
        } else if (rlen == 0) {
            framer_set_result(P300_ERROR);
            snprintf(string, sizeof(string), ">FRAMER: enable read timeout for ack");
            logIT(LOG_ERR, string);
            return FRAMER_ERROR;
        } else if (rbuf == P300_INIT_OK) {
            // hmueller: Replaced framer_set_result(P300_INIT_OK)
            // by framer_reset_actaddr() to avoid error log
            // >FRAMER: addr was still active FE06
            framer_reset_actaddr();
            snprintf(string, sizeof(string), ">FRAMER: opened");
            logIT(LOG_INFO, string);
            return FRAMER_SUCCESS;
        }
    }

    framer_set_result(P300_ERROR);
    snprintf(string, sizeof(string),
             ">FRAMER: could not close (%d attempts)", P300X_ATTEMPTS);
    logIT(LOG_ERR, string);

    return FRAMER_ERROR;
}

// calculation check sum for P300, assuming buffer is frame and starts by P300_LEADIN
static char framer_chksum(char *buf, int len)
{
    char sum = 0;
    while (len) {
        sum += *buf;
        //printf("framer chksum %d %02X %02X\n", len, *buf, sum);
        buf++;
        len--;
    }
    //printf("framer chksum %02x\n", sum);
    return sum;
}

/*
 * Frame a message in case P300 protocol is indicated
 *
 * Return 0: Error, else length of written bytes
 * This routine reads the first response byte for success status
 *
 * Format
 * Downlink
 * to framer
 * | type | function | addr | exp len |
 * to Vitotronic
 * | LEADIN | payload len | type | function | addr | exp len | chk |
 */
int framer_send(int fd, char *s_buf, int len)
{
    char string[256];

    if ((len < 1) || (! s_buf)) {
        snprintf(string, sizeof(string), ">FRAMER: invalid buffer %d %p", len, s_buf);
        logIT(LOG_ERR, string);
        return FRAMER_ERROR;
    }

    if (framer_pid != P300_LEADIN) {
        return my_send(fd, s_buf, len);
    } else if (len < 3) {
        snprintf(string, sizeof(string), ">FRAMER: too few for P300");
        logIT(LOG_ERR, string);
        return FRAMER_ERROR;
    } else {
        int pos = 0;
        char l_buf[256];
        unsigned long etime;
        int rlen;

        // prepare a new message, fill buffer starting with leadin
        l_buf[P300_LEADIN_OFFSET] = P300_LEADIN;
        l_buf[P300_LEN_OFFSET] = len; // only payload but len may contain other bytes
        memcpy(&l_buf[P300_TYPE_OFFSET], s_buf, len);
        l_buf[P300_LEADIN_LEN + P300_LEN_LEN + len] =
                framer_chksum(l_buf + P300_LEADIN_LEN, len + P300_LEN_LEN);
        if (! my_send(fd, l_buf, len + P300_EXTRA_BYTES)) {
            snprintf(string, sizeof(string), ">FRAMER: write failure %d",
                     len + P300_EXTRA_BYTES);
            logIT(LOG_ERR, string);
            return FRAMER_ERROR;
        }

        etime = 0;
        rlen = receive_nb(fd, l_buf, 1, &etime);
        if (rlen < 0) {
            snprintf(string, sizeof(string), ">FRAMER: read failure %d", pos + 1);
            logIT(LOG_ERR, string);
            return FRAMER_ERROR;
        } else if (rlen == 0) {
            snprintf(string, sizeof(string), ">FRAMER: timeout for ack %d", pos + 1);
            logIT(LOG_ERR, string);
            return FRAMER_ERROR;
        } else if (*l_buf != P300_INIT_OK) {
            snprintf(string, sizeof(string),
                     ">FRAMER: Error 0x%02X != 0x%02X (P300_INIT_OK)",
                     *l_buf, P300_INIT_OK);
            logIT(LOG_ERR, string);
            return FRAMER_ERROR;
        }

        framer_set_actaddr(l_buf);
        snprintf(string, sizeof(string), ">FRAMER: Command send");
        logIT(LOG_INFO, string);

        return FRAMER_SUCCESS;
    }
}

/*
 * Read a framed message in case P300 protocol is indicated
 *
 * Return 0: Error, else length of written bytes
 * This routine reads the first response byte for success status
 *
 * Format
 * from Vitotronic
 * | LEADIN | payload len | type | function | addr | exp len | chk |
 * Uplink
 * from framer
 * | data |
 * This simulates KW return, respective checking of the frame is done in this function
 *
 * etime is forwarded
 * return is FRAMER_ERROR, FRAMER_TIMEOUT or read len
 *
 * WEAKNESS:
 * If any other protocol gets an answer beginning 0x41, then this will return erroneous
 * KW may have values returned starting 0x41
 */
int framer_receive(int fd, char *r_buf, int r_len, unsigned long *petime)
{
    char string[256];
    int rlen;
    int total;
    int rtmp;
    char l_buf[256];
    unsigned long etime;
    char chk;

    // to identify TimerWWMi bug
    l_buf[P300_ADDR_OFFSET] = 0;
    l_buf[P300_ADDR_OFFSET + 1] = 0;

    if ((r_len < 1) || (! r_buf)) {
        snprintf(string, sizeof(string),
                 ">FRAMER: invalid read buffer %d %p", r_len, r_buf);
        logIT(LOG_ERR, string);
        return FRAMER_ERROR;
    }

    if (framer_preset_result(r_buf, r_len, petime)) {
        framer_reset_actaddr();
        return FRAMER_SUCCESS;
    }

    *petime = 0;
    rtmp = receive_nb(fd, l_buf, r_len, petime);
    if (rtmp < 0) {
        framer_reset_actaddr();
        snprintf(string, sizeof(string), ">FRAMER: read failure");
        logIT(LOG_ERR, string);
        return FRAMER_READ_ERROR;
    } else if (rtmp == 0) {
        framer_reset_actaddr();
        snprintf(string, sizeof(string), ">FRAMER: read timeout");
        logIT(LOG_ERR, string);
        return FRAMER_READ_TIMEOUT;
    } else if (framer_pid != P300_LEADIN) {
        // no P300 frame, just forward
        memcpy(r_buf, l_buf, r_len);
        return rtmp;
    }

    // this is not GWG / KW we know now
    etime = 0;
    // read at least the length info
    if (rtmp < 2) {
        rlen = receive_nb(fd, l_buf + 1, 1, &etime);
        *petime += etime;
        if (rlen < 0) {
            framer_reset_actaddr();
            snprintf(string, sizeof(string), ">FRAMER: read failure");
            logIT(LOG_ERR, string);
            return FRAMER_READ_ERROR;
        } else if (rlen == 0) {
            framer_reset_actaddr();
            snprintf(string, sizeof(string), ">FRAMER: read timeout");
            logIT(LOG_ERR, string);
            return FRAMER_READ_TIMEOUT;
        }
        rtmp += rlen;
    }

    total = l_buf[P300_LEN_OFFSET] + P300_EXTRA_BYTES;
    rlen = total - rtmp;
    if (rlen <= 0) {
        framer_reset_actaddr();
        snprintf(string, sizeof(string), ">FRAMER: strange read %d", rlen);
        logIT(LOG_ERR, string);
        return rtmp; // strange should not happen here
    }

    // now read what is extra
    rtmp = receive_nb(fd, l_buf + rtmp, rlen, &etime);
    *petime += etime;

    // check for leadin
    if (l_buf[P300_LEADIN_OFFSET] != P300_LEADIN) {
        framer_reset_actaddr();
        snprintf(string, sizeof(string),
                 ">FRAMER: read leadin error received 0x%02X expected 0x%02X",
                 l_buf[P300_LEADIN_OFFSET], P300_LEADIN);
        logIT(LOG_ERR, string);
        return FRAMER_READ_ERROR;
    }

    // bug in Vitotronic getTimerWWMi, we got it, but complete
    if ((l_buf[P300_ADDR_OFFSET] == 0x21) &&
            (l_buf[P300_ADDR_OFFSET + 1] == 0x10) &&
            (rtmp == -1)) {
        snprintf(string, sizeof(string), ">FRAMER: bug of getTimerWWMi - omit checksum");
        logIT(LOG_ERR, string);
    } else {
        if (rtmp < 0) {
            framer_reset_actaddr();
            snprintf(string, sizeof(string), ">FRAMER: read final failure");
            logIT(LOG_ERR, string);
            return FRAMER_READ_ERROR;
        } else if (rtmp == 0) {
            framer_reset_actaddr();
            snprintf(string, sizeof(string), ">FRAMER: read final timeout");
            logIT(LOG_ERR, string);
            return FRAMER_READ_TIMEOUT;
        }

        chk = framer_chksum(l_buf + P300_LEADIN_LEN, total - 2);
        if (l_buf[total - 1] != chk) {
            framer_reset_actaddr();
            snprintf(string, sizeof(string),
                    ">FRAMER: read chksum error received 0x%02X calc 0x%02X",
                    (unsigned char)l_buf[total - 1], (unsigned char)chk);
            logIT(LOG_ERR, string);
            return FRAMER_READ_ERROR;
        }
    }

    if (l_buf[P300_TYPE_OFFSET] == P300_ERROR_REPORT) {
        framer_reset_actaddr();
        snprintf(string, sizeof(string), ">FRAMER: ERROR address %02X%02X code %d",
                 l_buf[P300_ADDR_OFFSET], l_buf[P300_ADDR_OFFSET + 1],
                 l_buf[P300_BUFFER_OFFSET]);
        logIT(LOG_ERR, string);
        return FRAMER_READ_ERROR;
    }

    // TODO: could add check for address receive matching address send before
    if (framer_check_actaddr(l_buf)) {
        framer_reset_actaddr();
        snprintf(string, sizeof(string), ">FRAMER: not matching response addr");
        logIT(LOG_ERR, string);
        return FRAMER_READ_ERROR;
    }

    if ((l_buf[P300_FCT_OFFSET] == P300_WRITE_DATA) && (r_len == 1)) {
        // TODO: could add check for length matching previously requested data length
        if (r_len != (l_buf[P300_LEN_OFFSET] - 4)) {
            snprintf(string, sizeof(string),
                    ">FRAMER: unexpected length r_len=0x%02X != 0x%02X=l_buf[P300_LEN_OFFSET]-4",
                    r_len, l_buf[P300_LEN_OFFSET] - 4);
            logIT(LOG_ERR, string);
            framer_reset_actaddr();
            return FRAMER_READ_ERROR;
        }
        // if we have a P300 setaddr we do not get data back ...
        if (l_buf[P300_TYPE_OFFSET] == P300_RESPONSE) {
            // OK
            r_buf[rtmp] = 0x00;
        } else {
            // NOT OK
            r_buf[rtmp] = 0x01;
        }
    } else {
        if (r_len != (l_buf[P300_RESP_LEN_OFFSET])) {
            snprintf(string, sizeof(string),
                    ">FRAMER: unexpected length r_len=0x%02X != 0x%02X=l_buf[P300_RESP_LEN_OFFSET]",
                    r_len, l_buf[P300_RESP_LEN_OFFSET]);
            logIT(LOG_ERR, string);
            framer_reset_actaddr();
            return FRAMER_READ_ERROR;
        }
        memcpy(r_buf, &l_buf[P300_BUFFER_OFFSET], r_len);
    }

    framer_reset_actaddr();
    return r_len;
}

int framer_waitfor(int fd, char *w_buf, int w_len)
{
    unsigned long etime;

    if (framer_preset_result(w_buf, w_len, &etime)) {
        framer_reset_actaddr();
        return FRAMER_SUCCESS;
    }

    return waitfor(fd, w_buf, w_len);
}

// Device handling, with open and close the mode is also switched to P300/back
int framer_openDevice(char *device, char pid)
{
    char string[100];
    int fd;

    snprintf(string, sizeof(string),
             ">FRAMER: open device %s ProtocolID %02X", device, pid);
    logIT(LOG_INFO, string);

    if ((fd = openDevice(device)) == -1) {
        return -1;
    }

    if (pid == P300_LEADIN) {
        if (! framer_open_p300(fd)) {
            closeDevice(fd);
            return -1;
        }
    }

    framer_pid = pid;
    return fd;
}

void framer_closeDevice(int fd)
{
    if (framer_pid == P300_LEADIN) {
        framer_close_p300(fd);
    }

    framer_pid = 0;
    closeDevice(fd);
}
0707010000002B000081A4000028240000003200000001607BF44800000518000000000000000000000000000000000000003400000000vcontrold-v0.98.10+git20210418.977e6f5/src/framer.h/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/*
 * Framer interface
 *
 * For P300 framing is supported here
 *
 * Control is by a controlling byte defining the action
 *
 * 2013-01-31 vheat
 */

/*
 * Main indentification of P300 Protocol is about leadin 0x41
 */

#ifndef FRAMER_H
#define FRAMER_H

#define FRAMER_ERROR    0
#define FRAMER_SUCCESS  1

int framer_send(int fd, char *s_buf, int len);
int framer_waitfor(int fd, char *w_buf, int w_len);
int framer_receive(int fd, char *r_buf, int r_len, unsigned long *petime);
int framer_openDevice(char *device, char pid);
void framer_closeDevice(int fd);

#endif // FRAMER_H
0707010000002C000081A4000028240000003200000001607BF4480000248A000000000000000000000000000000000000003000000000vcontrold-v0.98.10+git20210418.977e6f5/src/io.c/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// Communication with the vito controller

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <syslog.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <time.h>
#include <setjmp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/times.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/ioctl.h>

#include "io.h"
#include "socket.h"
#include "common.h"

#ifdef __CYGWIN__
// NCC is not defined under cygwin
#define NCC NCCS
#endif

static void sig_alrm(int);
static jmp_buf    env_alrm;

void closeDevice(int fd)
{
    close(fd);
}

int openDevice(char *device)
{
    // We differentiate TTY and Socket connections.
    // Socker: no / at the beginning and a :
    int fd;
    char *dptr;

    if (device[0] != '/' && (dptr = strchr(device, ':'))) {
        char host[MAXBUF];
        int port;
        port = atoi(dptr + 1);
        // The dptr device gives us the length of the host
        memset(host, 0, sizeof(host));
        strncpy(host, device, dptr - device);
        // Third parameter == 1 --> noTCPDelay set
        fd = openCliSocket(host, port, 1);
    } else {
        fd = opentty(device);
    }

    if (fd < 0) {
        // Here goes some error stuff
        return -1;
    }

    return fd;
}

int opentty(char *device)
{
    int fd;

    logIT(LOG_LOCAL0, "Configuring serial interface %s", device);
    if ((fd = open(device, O_RDWR)) < 0) {
        logIT(LOG_ERR, "cannot open %s:%m", device);
        exit(1);
    }

    int s;
    struct termios oldsb, newsb;
    s = tcgetattr(fd, &oldsb);
    if (s < 0) {
        logIT(LOG_ERR, "error tcgetattr %s:%m", device);
        exit(1);
    }

    newsb = oldsb;

#ifdef NCC
    // NCC is not always defined
    for (s = 0; s < NCC; s++) {
        newsb.c_cc[s] = 0;
    }
#else
    memset(&newsb, 0, sizeof(newsb));
#endif

    newsb.c_iflag     = IGNBRK | IGNPAR;
    newsb.c_oflag     = 0;
    newsb.c_lflag     = 0; // removed ISIG for susp=control-z problem;
    newsb.c_cflag     = (CLOCAL | B4800 | CS8 | CREAD | PARENB | CSTOPB);
    newsb.c_cc[VMIN]  = 1;
    newsb.c_cc[VTIME] = 0;

    tcsetattr(fd, TCSADRAIN, &newsb);

    // DTR High for voltage supply
    int modemctl = 0;
    ioctl(fd, TIOCMGET, &modemctl);
    modemctl |= TIOCM_DTR;
    s = ioctl(fd, TIOCMSET, &modemctl);
    if (s < 0) {
        logIT(LOG_ERR, "error ioctl TIOCMSET %s:%m", device);
        exit(1);
    }

    return fd;
}

int my_send(int fd, char *s_buf, int len)
{
    int i;
    int wr;
    // Empty buffer
    tcflush(fd, TCIOFLUSH);
    // We use the socket fixed variant from socket.c
    wr = writen(fd, s_buf, len);
    for (i = 0; i < len; i++) {
        unsigned char byte = s_buf[i] & 255;
        logIT(LOG_INFO, ">SENT: %02X", (int)byte);
    }

    if (wr == len) {
        return wr;
    } else {
        logIT(LOG_ERR, ">ERROR: sent %d of %d bytes", wr, len);
        return 0;
    }
}

int receive(int fd, char *r_buf, int r_len, unsigned long *etime)
{
    int i;
    struct tms tms_t;
    clock_t start, end, mid, mid1;
    unsigned long clktck;

    clktck = sysconf(_SC_CLK_TCK);
    start = times(&tms_t);
    mid1 = start;
    for (i = 0; i < r_len; i++) {
        if (signal(SIGALRM, sig_alrm) == SIG_ERR) {
            logIT1(LOG_ERR, "SIGALRM error");
        }
        if (setjmp(env_alrm) != 0) {
            logIT1(LOG_ERR, "read timeout");
            return -1;
        }
        alarm(TIMEOUT);
        // We use the socket fixed variant from socket.c
        if (readn(fd, &r_buf[i], 1) <= 0) {
            logIT1(LOG_ERR, "error read tty");;
            alarm(0);
            return -1;
        }

        alarm(0);
        unsigned char byte = r_buf[i] & 255;
        mid = times(&tms_t);
        logIT(LOG_INFO, "<RECV: %02X (%0.1f ms)", byte, ((float)(mid - mid1) / clktck) * 1000);
        mid1 = mid;
    }

    end = times(&tms_t);
    *etime = ((float)(end - start) / clktck) * 1000;

    return i;
}

static int setnonblock(int fd)
{
    int flags;

    // If they have O_NONBLOCK, use the Posix way to do it
#if defined(O_NONBLOCK)
    if (-1 == (flags = fcntl(fd, F_GETFL, 0))) {
        flags = 0;
    }
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
#else
    // Otherwise, use the old way of doing it
    flags = 1;
    return ioctl(fd, FIOBIO, &flags);
#endif
}

static int setblock(int fd)
{
    int flags;

    // If they have O_BLOCK, use the Posix way to do it
#if defined(O_NONBLOCK)
    if (-1 == (flags = fcntl(fd, F_GETFL, 0))) {
        flags = 0;
    }
    return fcntl(fd, F_SETFL, flags & (~O_NONBLOCK));
#else
    // Otherwise, use the old way of doing it
    flags = 0;
    return ioctl(fd, FIOBIO, &flags);
#endif
}

static char *dump( char *dest, char *title, char *buf, int len)
{
    int pos = 0;
    int i;

    pos = sprintf(dest, "%s", title);
    for ( i = 0; i < len; i++) {
        pos += sprintf(dest + pos, " %02X",  buf[i] & 0xff);
    }

    return dest;
}

int receive_nb(int fd, char *r_buf, int r_len, unsigned long *etime)
{
    int i;
    ssize_t len;
    char string[100];

    fd_set rfds;
    struct timeval tv;
    int retval;

    struct tms tms_t;
    clock_t start, end, mid, mid1;
    unsigned long clktck;
    clktck = sysconf(_SC_CLK_TCK);
    start = times(&tms_t);
    mid1 = start;

    i = 0;
    while ( i < r_len ) {
        setnonblock(fd);
        FD_ZERO(&rfds);
        FD_SET(fd, &rfds);
        tv.tv_sec = TIMEOUT;
        tv.tv_usec = 0;
        retval = select(fd + 1, &rfds, NULL, NULL, &tv);
        if (retval == 0) {
            logIT(LOG_ERR, "<RECV: read timeout");
            setblock(fd);
            logIT(LOG_INFO, dump(string, "<RECV: received", r_buf, i));
            return -1;
        } else if (retval < 0) {
            if (errno == EINTR) {
                logIT(LOG_INFO, "<RECV: select interrupted - redo");
                continue;
            } else {
                logIT(LOG_ERR, "<RECV: select error %d", retval);
                setblock(fd);
                logIT(LOG_INFO, dump(string, "<RECV: received", r_buf, i));
                return -1;
            }
        } else if ( FD_ISSET(fd, &rfds)) {
            len = read(fd, &r_buf[i], r_len - i);
            if (len == 0) {
                logIT(LOG_ERR, "<RECV: read eof");
                setblock(fd);
                logIT(LOG_INFO, dump(string, "<RECV: received", r_buf, i));
                return -1;
            } else if (len < 0) {
                if (errno == EINTR) {
                    logIT(LOG_INFO, "<RECV: read interrupted - redo");
                    continue;
                } else {
                    logIT(LOG_ERR, "<RECV: read error %d", errno);
                    setblock(fd);
                    logIT(LOG_INFO, dump(string, "<RECV: received", r_buf, i));
                    return -1;
                }
            } else {
                unsigned char byte = r_buf[i] & 255;
                mid = times(&tms_t);
                logIT(LOG_INFO, "<RECV: len=%zd %02X (%0.1f ms)", len, byte, ((float)(mid - mid1) / clktck) * 1000);
                mid1 = mid;
                i += len;
            }
        } else {
            continue;
        }
    }

    end = times(&tms_t);
    *etime = ((float)(end - start) / clktck) * 1000;
    setblock(fd);
    logIT(LOG_INFO, dump(string, "<RECV: received", r_buf, i));

    return i;
}

static void sig_alrm(int signo)
{
    longjmp(env_alrm, 1);
}

int waitfor(int fd, char *w_buf, int w_len)
{
    int i;
    time_t start;
    char r_buf[MAXBUF];
    char hexString[128] = "\0";
    char dummy[3];
    unsigned long etime;

    for (i = 0; i < w_len; i++) {
        sprintf(dummy, "%02X", w_buf[i]);
        strncat(hexString, dummy, strlen(dummy));
    }

    logIT(LOG_INFO, "Waiting for %s", hexString);
    start = time(NULL);

    // We wait for the first character, then everything has to apply
    do {
        etime = 0;
        if (receive(fd, r_buf, 1, &etime) < 0) {
            return 0;
        }
        if (time(NULL) - start > TIMEOUT) {
            logIT1(LOG_WARNING, "Timeout wait");
            return 0;
        }
    } while (r_buf[0] != w_buf[0]);

    for (i = 1; i < w_len; i++) {
        etime = 0;
        if (receive(fd, r_buf, 1, &etime) < 0) {
            return 0;
        }
        if (time(NULL) - start > TIMEOUT) {
            logIT1(LOG_WARNING, "Timeout wait");
            return 0;
        }
        if ( r_buf[0] != w_buf[i]) {
            logIT1(LOG_ERR, "Lost synchronization");
            exit(1);
        }
    }

    // logIT1(LOG_INFO,"Recognized string");
    return 1;
}
0707010000002D000081A4000028240000003200000001607BF448000004A4000000000000000000000000000000000000003000000000vcontrold-v0.98.10+git20210418.977e6f5/src/io.h/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef IO_H
#define IO_H

int my_send(int fd, char *s_buf, int len);
int receive(int fd, char *r_buf, int r_len, unsigned long *etime);
int receive_nb(int fd, char *r_buf, int r_len, unsigned long *etime);
int waitfor(int fd, char *w_buf, int w_len);
int opentty(char *device);
int openDevice(char *device);
void closeDevice(int fd);

// Interface parameters

#define TTY "/dev/usb/tts/0"

#ifndef MAXBUF
#define MAXBUF 4096
#endif
#define TIMEOUT 5

#endif // IO_H
0707010000002E000081A4000028240000003200000001607BF448000054AD000000000000000000000000000000000000003400000000vcontrold-v0.98.10+git20210418.977e6f5/src/parser.c/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// Routines for reading commands

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <syslog.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
#include <ctype.h>

#include "xmlconfig.h"
#include "parser.h"
#include "unit.h"
#include "common.h"
#include "io.h"
#include "framer.h"

extern FILE *iniFD; // For creation of the Sim. INI Files

void *getUnit(char *str)
{
    // We parse the input for a known unit and return a pointer to the struct
    return NULL;
}

int parseLine(char *line, char *hex, int *hexlen, char *uSPtr, ssize_t uSPtrLen)
{
    int token = 0;

    if (strstr(line, "WAIT") == line) {
        token = WAIT;
    } else if (strstr(line, "SEND BYTES") == line) {
        token = BYTES;
    } else if (strstr(line, "SEND") == line) {
        token = SEND;
    } else if (strstr(line, "RECV") == line) {
        token = RECV;
    } else if (strstr(line, "PAUSE") == line) {
        token = PAUSE;
    }

    if (token) {
        // We parse a hex string and return it processed
        // Search first blank
        char *ptr;
        ptr = strchr(line, ' ');
        if (! ptr) {
            logIT(LOG_ERR, "Parse error: Found no blanks: %s", line);
        } else {
            // We process the rst of the line
            char pString[MAXBUF];
            char *pPtr;
            while (*ptr != '\0') {
                int i;
                pPtr = pString;
                for (i = 0; i < MAXBUF; i++) {
                    pPtr[i] = '\0';
                }
                pPtr = pString;
                // Remove blanks
                int endFound = 0;
                while (*++ptr == ' ') {
                    if (*ptr == '\0') {
                        endFound = 1;
                        break;
                    }
                }
                if (endFound) {
                    break;
                }
                // Now, we read the characters to the next blank or end
                while ((*ptr != ' ') && (*ptr != '\0') && (*ptr != '\n') && (*ptr != '\r')) {
                    *pPtr++ = *ptr++;
                }

                if (! *pString) {
                    break;
                }

                *pPtr = '\0';
                ++*hexlen;
                // If it's Pause or Reveice, we tread it as an integer
                if (token == PAUSE || token == RECV) {
                    *hexlen = atoi(pString);
                    // With RECV, one can give the unit to be converted
                    if (token == RECV) {
                        pPtr = pString;
                        while (*ptr && (isdigit(*ptr) || isspace(*ptr))) {
                            ptr++;
                        }
                        strncpy(uSPtr, ptr, uSPtrLen);
                    }
                    return token;
                } else if (token == BYTES ) {
                    // Here follows the unit
                    pPtr = pString;
                    while (*ptr && (isdigit(*ptr) || isspace(*ptr))) {
                        ptr++;
                    }
                    strncpy(uSPtr, ptr, uSPtrLen);
                    return token;
                } else  {
                    *hex++ = hex2chr(pString);
                }
                *pString = '\0';
            }
        }
    }

    return token;
}

int execByteCode(compilePtr cmpPtr, int fd, char *recvBuf, short recvLen,
                 char *sendBuf, short sendLen, short supressUnit,
                 char bitpos, int retry,
                 char *pRecvPtr, unsigned short recvTimeout)
{
    char string[256];
    char result[MAXBUF];
    char simIn[500];
    char simOut[500];
    short len;
    compilePtr cPtr;
    unsigned long etime;
    char out_buff[1024];
    int out_len;

    memset(simIn, 0, sizeof(simIn));
    memset(simOut, 0, sizeof(simOut));

    // First copy or convert the bytes of sendBuf to the BYTES token of cmpPtr
    // to be sure not to abort right in the middle of it
    cPtr = cmpPtr; // do not change cmpPtr, use local cPtr
    while (cPtr) {
        if (cPtr->token != BYTES) {
            cPtr = cPtr->next;
            continue;
        }
        if (supressUnit) {
            // No unit conversion needed, just copy the bytes
            if (sendLen != cPtr->len) {
                // This should never happen!
                logIT(LOG_ERR,
                      "Error in length of the hex string (%d) != send length of the command (%d), terminating", sendLen, cPtr->len);
                return -1;
            }
            if (cPtr->send) {
                free(cPtr->send);
            }
            cPtr->send = calloc(cPtr->len, sizeof(char));
            sendLen = 0; // We don't send the converted sendBuf
            memcpy(cPtr->send, sendBuf, cPtr->len);
        } else if (cPtr->uPtr) {
            len = sendLen; // we need this in procSetUnit() to clear sendBuf
            if (procSetUnit(cPtr->uPtr, sendBuf, &len, bitpos, pRecvPtr) <= 0) {
                logIT(LOG_ERR, "Error in unit conversion: %s, terminating", sendBuf);
                return -1;
            }
            if (cPtr->send) {
                free(cPtr->send);
            }
            cPtr->send = calloc(len, sizeof(char));
            cPtr->len = len;
            sendLen = 0; // We don't send the converted sendBuf
            memcpy(cPtr->send, sendBuf, len);
        }
        cPtr = cPtr->next;
    }

    do {
        cPtr = cmpPtr; // We need the starting point for the next round
        while (cmpPtr) {
            switch (cmpPtr->token) {
            case WAIT:
                if (! framer_waitfor(fd, cmpPtr->send, cmpPtr->len)) {
                    logIT1(LOG_ERR, "Error in wait, terminating");
                    return -1;
                }
                memset(string, 0, sizeof(string));
                char2hex(string, cmpPtr->send, cmpPtr->len);
                strcat(simIn, string);
                strcat(simIn, " ");
                break;
            case SEND:
                out_len = 0;
                while (1) {
                    if (out_len + cmpPtr->len > sizeof(out_buff)) {
                        // Hopefully, we never end up here
                        logIT1(LOG_ERR, "Error out_buff buffer overflow, terminating");
                        return -1;
                    }

                    // Copy all SEND data and BYTES data to out_buff, that CRC calculation
                    // works in framer_send()
                    memcpy(out_buff + out_len, cmpPtr->send, cmpPtr->len);
                    out_len += cmpPtr->len;

                    if (! (cmpPtr->next && cmpPtr->next->token == BYTES)) {
                        break;
                    }
                    cmpPtr = cmpPtr->next;
                }

                if (! framer_send(fd, out_buff, out_len)) {
                    logIT1(LOG_ERR, "Error in send, terminating");
                    return -1;
                }

                if (iniFD && *simIn && *simOut) {
                    // We already sent and received something, so we output it
                    fprintf(iniFD, "%s= %s\n", simOut, simIn);
                    memset(simOut, 0, sizeof(simOut));
                    memset(simIn, 0, sizeof(simIn));
                }

                memset(string, 0, sizeof(string));
                char2hex(string, out_buff, out_len);
                strcat(simOut, string);
                strcat(simOut, " ");
                break;
            case RECV:
                if (cmpPtr->len > recvLen) {
                    // Hopefully, we don't end up here
                    logIT(LOG_ERR, "Recv buffer too small. Is: %d, should be %d",
                          recvLen, cmpPtr->len);
                    cmpPtr->len = recvLen;
                }
                etime = 0;
                memset(recvBuf, 0, recvLen);
                if (framer_receive(fd, recvBuf, cmpPtr->len, &etime) <= 0) {
                    logIT1(LOG_ERR, "Error in recv, terminating");
                    return -1;
                }
                // If receiving took longer than the timeout, we start the next round
                if (recvTimeout && (etime > recvTimeout)) {
                    logIT(LOG_NOTICE, "Recv Timeout: %ld ms > %d ms (Retry: %d)",
                          etime, recvTimeout, (int)(retry - 1));
                    if (retry <= 1) {
                        logIT1(LOG_ERR, "Recv timeout, terminating");
                        return -1;
                    }
                    goto RETRY;
                }

                // If some errStr is defined, we check if the result is correct
                if (cmpPtr->errStr && *cmpPtr->errStr) {
                    if (memcmp(recvBuf, cmpPtr->errStr, cmpPtr->len) == 0) {
                        // Wrong answer
                        logIT(LOG_NOTICE, "Errstr matched, wrong result (Retry: %d)", retry - 1);
                        if (retry <= 1) {
                            logIT1(LOG_ERR, "Wrong result, terminating");
                            return -1;
                        }
                        goto RETRY;
                    }
                }

                memset(string, 0, sizeof(string));
                char2hex(string, recvBuf, cmpPtr->len);
                strcat(simIn, string);
                strcat(simIn, " ");

                // If we have a Unit (== uPtr), we convert the received value and also
                // return the converted value to uPtr
                memset(result, 0, sizeof(result));
                if (! supressUnit && cmpPtr->uPtr) {
                    if (procGetUnit(cmpPtr->uPtr, recvBuf, cmpPtr->len, result, bitpos, pRecvPtr)
                        <= 0) {
                        logIT(LOG_ERR, "Error in unit conversion: %s, terminating", result);
                        return -1;
                    }
                    strncpy(recvBuf, result, recvLen);

                    if (iniFD && *simIn && *simOut) {
                        // We already sent and received, now we output it.
                        fprintf(iniFD, "%s= %s \n", simOut, simIn);
                    }

                    return 0; // 0 == converted to unit
                }

                if (iniFD && *simIn && *simOut) {
                    // We already sent and received, now we output it.
                    fprintf(iniFD, "%s= %s \n", simOut, simIn);
                }
                return cmpPtr->len;
                break;
            case PAUSE:
                logIT(LOG_INFO, "Waiting %i ms", cmpPtr->len);
		struct timespec sleepTime = {
		    .tv_sec = cmpPtr->len / 1000L,
		    .tv_nsec = (cmpPtr->len % 1000L) * 1000000L};
                nanosleep(&sleepTime, NULL);
                break;
            case BYTES:
                // We send the forwarded sendBuffer. No converting has been done.
                if (sendLen) {
                    if (!my_send(fd, sendBuf, sendLen)) {
                        logIT1(LOG_ERR, "Error in send, terminating");
                        return -1;
                    }
                    char2hex(string, sendBuf, sendLen);
                    strcat(simOut, string);
                    strcat(simOut, " ");
                } else if (cmpPtr->len) {
                    // A unit to use is already defined, and we already converted it
                    if (! my_send(fd, cmpPtr->send, cmpPtr->len)) {
                        logIT1(LOG_ERR, "Error in send unit bytes, terminating");
                        free(cmpPtr->send);
                        cmpPtr->send = NULL;
                        cmpPtr->len = 0;
                        return -1;
                    }
                    memset(string, 0, sizeof(string));
                    char2hex(string, cmpPtr->send, cmpPtr->len);
                    strcat(simOut, string);
                    strcat(simOut, " ");
                }
                break;
            default:
                logIT(LOG_ERR, "Unknown token: %d, terminating", cmpPtr->token);
                return -1;
            }
            cmpPtr = cmpPtr->next;
        }
RETRY:
        retry--;
        cmpPtr = cPtr; // One more time, please
    } while ((cmpPtr->errStr || recvTimeout) && (retry > 0));

    return 0;
}

int execCmd(char *cmd, int fd, char *recvBuf, int recvLen)
{
    char uString[100];
    // We parse the individual lines
    char hex[MAXBUF];
    int token;
    int hexlen = 0;
    int t;
    unsigned long etime;

    logIT(LOG_INFO, "Execute %s", cmd);

    token = parseLine(cmd, hex, &hexlen, uString, sizeof(uString));
    // If noOpen is set for debugging, we do nothing here.

    switch (token) {
    case WAIT:
        if (! waitfor(fd, hex, hexlen)) {
            logIT1(LOG_ERR, "Error wait, terminating");
            return -1;
        }
        break;
    case SEND:
        if (! my_send(fd, hex, hexlen)) {
            logIT1(LOG_ERR, "Error send, terminating");
            exit(1);
        }
        break;
    case RECV:
        if (hexlen > recvLen) {
            logIT(LOG_ERR, "Recv buffer too small. Is: %d should be %d", recvLen, hexlen);
            hexlen = recvLen;
        }
        etime = 0;
        if (receive(fd, recvBuf, hexlen, &etime) <= 0) {
            logIT1(LOG_ERR, "Error recv, terminating");
            exit(1);
        }
        logIT(LOG_INFO, "Recv: %ld ms", etime);
        // If we have a unit (== uPtr), we convert the received value and also return it to uPtr
        return hexlen;
        break;
    case PAUSE:
        t = (int) hexlen / 1000;
        logIT(LOG_INFO, "Waiting %i s", t);
        sleep(t);
        break;
    default:
        logIT(LOG_INFO, "Unknown command: %s", cmd);

    }

    return 0;
}

compilePtr newCompileNode(compilePtr ptr)
{
    compilePtr nptr;

    if (ptr && ptr->next) {
        return newCompileNode(ptr->next);
    }

    nptr = calloc(1, sizeof(Compile));
    if (! nptr) {
        fprintf(stderr, "malloc failed\n");
        exit(1);
    }

    if (ptr) {
        ptr->next = nptr;
    }

    nptr->token = 0;
    nptr->len = 0;
    nptr->uPtr = NULL;
    nptr->next = NULL;

    return nptr;
}

void removeCompileList(compilePtr ptr)
{
    if (ptr && ptr->next) {
        removeCompileList(ptr->next);
    }

    if (ptr) {
        free(ptr->send);
        ptr->send = NULL;
        free(ptr);
        ptr = NULL;
    }
}

int expand(commandPtr cPtr, protocolPtr pPtr)
{
    // Recursion
    if (! cPtr) {
        return 0;
    }
    if (cPtr->next) {
        expand(cPtr->next, pPtr);
    }

    // If no address has been set, we do nothing
    if (! cPtr->addr) {
        return 0;
    }

    char eString[2000];
    char *ePtr = eString;;
    char var[100];
    char name[100];
    char *ptr, *bptr;
    macroPtr mFPtr;
    char string[256];
    char *sendPtr, *sendStartPtr;
    char *tmpPtr;

    icmdPtr iPtr;

    // The command pointer's send has to be assembled

    // 1. Search command pcmd at the protocol's command
    if (! (iPtr = (icmdPtr) getIcmdNode( pPtr->icPtr, cPtr->pcmd))) {
        logIT(LOG_ERR, "Protocol command %s (at %s) not defined", cPtr->pcmd, cPtr->name);
        exit(3);
    }

    // 2. Parse the line and replace the variables with values from cPtr
    sendPtr = iPtr->send;
    sendStartPtr = iPtr->send;
    if (! sendPtr) {
        return 0;
    }

    logIT(LOG_INFO, "protocmd line: %s", sendPtr);
    memset(eString, 0, sizeof(eString));
    cPtr->retry = iPtr->retry; // We take the Retry value from the protocol command
    cPtr->recvTimeout = iPtr->recvTimeout; // Same for the receive timeout
    do {
        ptr = sendPtr;
        while (*ptr++) {
            if ((*ptr == '$') || (*ptr == '\0')) {
                break;
            }
        }
        bptr = ptr;
        while (*bptr++) {
            if ((*bptr == ' ') || (*bptr == ';') || (*bptr == '\0')) {
                break;
            }
        }
        // Don't copy the converted string
        strncpy(ePtr, sendPtr, ptr - sendPtr);
        ePtr += ptr - sendPtr;
        memset(var, 0, sizeof(var));
        strncpy(var, ptr + 1, bptr - ptr - 1);
        //snprintf(string, sizeof(string),"   Var: %s",var);
        //logIT(LOG_INFO,string);
        if (*var) {
            // Do we have variabls to expand at all?
            if (strstr(var, "addr") == var) {
                // Always two bytes
                int i;
                memset(string, 0, sizeof(string));
                for (i = 0; i < strlen(cPtr->addr) - 1; i += 2) {
                    strncpy(ePtr, cPtr->addr + i, 2);
                    ePtr += 2;
                    *ePtr++ = ' ';
                }
                ePtr--;
            } else if (strstr(var, "len") == var) {
                snprintf(string, sizeof(string), "%d", cPtr->len);
                strncpy(ePtr, string, strlen(string));
                ePtr += strlen(string);
            } else if (strstr(var, "hexlen") == var) {
                snprintf(string, sizeof(string), "%02X", cPtr->len);
                strncpy(ePtr, string, strlen(string));
                ePtr += strlen(string);
            } else if (strstr(var, "unit") == var) {
                if (cPtr->unit) {
                    strncpy(ePtr, cPtr->unit, strlen(cPtr->unit));
                    ePtr += strlen(cPtr->unit);
                }
            } else {
                logIT(LOG_ERR, "Variable %s Unknown", var);
                exit(3);
            }
        }
        sendPtr = bptr;
    } while (*sendPtr);

    logIT(LOG_INFO, "  After substitution: %s", eString);
    tmpPtr = calloc(strlen(eString) + 1, sizeof(char));
    strcpy(tmpPtr, eString);

    // 3. Expand the line as usual
    sendPtr = tmpPtr;
    sendStartPtr = sendPtr;

    // We search for words and look if we have them as macros
    memset(eString, 0, sizeof(eString));
    ePtr = eString;
    do {
        ptr = sendPtr;
        while (*ptr++) {
            if ((*ptr == ' ') || (*ptr == ';') || (*ptr == '\0')) {
                break;
            }
        }
        memset(name, 0, sizeof(name));
        strncpy(name, sendPtr, ptr - sendPtr);
        if ((mFPtr = getMacroNode(pPtr->mPtr, name))) {
            strncpy(ePtr, mFPtr->command, strlen(mFPtr->command));
            ePtr += strlen(mFPtr->command);
            *ePtr++ = *ptr;
        } else {
            strncpy(ePtr, sendPtr, ptr - sendPtr + 1);
            ePtr += ptr - sendPtr + 1;
        }
        sendPtr = ptr + 1;
    } while (*sendPtr);

    free(tmpPtr);
    logIT(LOG_INFO, "   after EXPAND:%s", eString);
    if (! (cPtr->send = calloc(strlen(eString) + 1, sizeof(char)))) {
        logIT1(LOG_ERR, "calloc failed");
        exit(1);
    }

    strcpy(cPtr->send, eString);
    return 1;
}

compilePtr buildByteCode(commandPtr cPtr, unitPtr uPtr)
{
    // Recursion
    if (!cPtr) {
        return 0;
    }
    if (cPtr->next) {
        buildByteCode(cPtr->next, uPtr);
    }

    // If no address has been given, we do nothing
    if (! cPtr->addr) {
        return 0;
    }

    char eString[2000];
    char cmd[200];
    char *ptr;

    char hex[MAXBUF];
    int hexlen;
    int token;
    char uString[100];
    char *uSPtr = uString;

    compilePtr cmpPtr, cmpStartPtr;
    cmpStartPtr = NULL;

    char *sendPtr, *sendStartPtr;
    sendPtr = cPtr->send;
    sendStartPtr = cPtr->send;

    if (! sendPtr) {
        // Nothing to do here
        return 0;
    }

    logIT(LOG_INFO, "BuildByteCode: %s", sendPtr);
    memset(eString, 0, sizeof(eString));
    do {
        ptr = sendPtr;
        while (*ptr++) {
            if ((*ptr == '\0') || (*ptr == ';')) {
                break;
            }
        }
        memset(cmd, 0, sizeof(cmd));
        strncpy(cmd, sendPtr, ptr - sendPtr);
        hexlen = 0;
        memset(uSPtr, 0, sizeof(uString));
        token = parseLine(cmd, hex, &hexlen, uString, sizeof(uString));
        logIT(LOG_INFO, "        Token: %d Hexlen: %d, Unit: %s", token, hexlen, uSPtr);
        cmpPtr = newCompileNode(cmpStartPtr);

        if (!cmpStartPtr) {
            cmpStartPtr = cmpPtr;
        }
        cmpPtr->token = token;
        cmpPtr->len = hexlen;
        cmpPtr->errStr = cPtr->errStr;
        cmpPtr->send = calloc(hexlen, sizeof(char));
        memcpy(cmpPtr->send, hex, hexlen);

        if (*uSPtr && !(cmpPtr->uPtr = getUnitNode(uPtr, uSPtr))) {
            logIT(LOG_ERR, "Unit %s not defined", uSPtr);
            exit(3);
        }

        sendPtr += strlen(cmd) + 1;
    } while (*sendPtr);

    cPtr->cmpPtr = cmpStartPtr;
    return cmpStartPtr;
}

void compileCommand(devicePtr dPtr, unitPtr uPtr)
{
    if (! dPtr) {
        return;
    }
    if (dPtr->next) {
        compileCommand(dPtr->next, uPtr);
    }

    logIT(LOG_INFO, "Expanding command for device %s", dPtr->id);
    expand(dPtr->cmdPtr, dPtr->protoPtr);
    buildByteCode(dPtr->cmdPtr, uPtr);
}
0707010000002F000081A4000028240000003200000001607BF4480000056F000000000000000000000000000000000000003400000000vcontrold-v0.98.10+git20210418.977e6f5/src/parser.h/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PARSER_H
#define PARSER_H

int parseLine(char *lineo, char *hex, int *hexlen, char *uSPtr, ssize_t uSPtrLen);
int execCmd(char *cmd, int fd, char *result, int resultLen);
void removeCompileList(compilePtr ptr);
int execByteCode(compilePtr cmpPtr, int fd, char *recvBuf, short recvLen, char *sendBuf,
                 short sendLen, short supressUnit, char bitpos, int retry, char *pRecvPtr,
                 unsigned short recvTimeout);
void compileCommand(devicePtr dPtr, unitPtr uPtr);

// Token Definition
#define WAIT    1
#define RECV    2
#define SEND    3
#define PAUSE   4
#define BYTES   5

#ifndef MAXBUF
#define MAXBUF 4096
#endif

#endif // PARSER_H
07070100000030000081A4000028240000003200000001607BF448000003C1000000000000000000000000000000000000003400000000vcontrold-v0.98.10+git20210418.977e6f5/src/prompt.h/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/* Prompt Definition fuer die gemeinsame Nutzung in client und Server */

#ifndef PROMPT_H
#define PROMPT_H

#define PROMPT "vctrld>"
#define BYE "good bye!\n"
#define UNKNOWN "ERR: command unknown\n"
#define ERR "ERR:"

#endif // PROMPT_H
07070100000031000081A4000028240000003200000001607BF448000010F4000000000000000000000000000000000000003700000000vcontrold-v0.98.10+git20210418.977e6f5/src/semaphore.c/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
#include <syslog.h>
#include <sys/param.h>

#include "common.h"
#include "semaphore.h"

#define MAX_RETRIES 10

#if !defined(__APPLE__)
// on my mac this is a redefinition
union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};
#endif

/*
 * initsem() -- more-than-inspired by W. Richard Stevens' UNIX Network
 * Programming 2nd edition, volume 2, lockvsem.c, page 295.
 */
int initsem(key_t key, int nsems)
{
    // key from ftok()
    int i;
    union semun arg;
    struct semid_ds buf;
    struct sembuf sb;
    int semid;

    semid = semget(key, nsems, IPC_CREAT | IPC_EXCL | 0666);

    if (semid >= 0) { // we got it first
        sb.sem_op = 1;
        sb.sem_flg = 0;
        arg.val = 1;

        for (sb.sem_num = 0; sb.sem_num < nsems; sb.sem_num++) {
            // do a semop() to "free" the semaphores.
            // this sets the sem_otime field, as needed below.
            if (semop(semid, &sb, 1) == -1) {
                int e = errno;
                semctl(semid, 0, IPC_RMID); // clean up
                errno = e;
                return -1; // error, check errno
            }
        }
    } else if (errno == EEXIST) {
        // someone else got it first
        int ready = 0;

        semid = semget(key, nsems, 0); // get the id
        if (semid < 0) {
            // error, check errno
            return semid;
        }

        // wait for other process to initialize the semaphore:
        arg.buf = &buf;
        for (i = 0; i < MAX_RETRIES && !ready; i++) {
            semctl(semid, nsems - 1, IPC_STAT, arg);
            if (arg.buf->sem_otime != 0) {
                ready = 1;
            } else {
                sleep(1);
            }
        }
        if (! ready) {
            errno = ETIME;
            return -1;
        }
    } else {
        return semid;
        // error, check errno
    }

    return semid;
}

int semid;
char tmpfilename[MAXPATHLEN + 1]; // account for the leading '\0'

int vcontrol_seminit()
{
    key_t key;
    struct sembuf sb;

    int tmpfile;

    strncpy(tmpfilename, TMPFILENAME, sizeof(TMPFILENAME));
    if ((tmpfile = mkstemp(tmpfilename)) < 0) {
        perror("mkstemp");
        exit(1);
    }
    close(tmpfile);

    sb.sem_num = 0;
    sb.sem_op = -1; // set to allocate resource
    sb.sem_flg = SEM_UNDO;

    if ((key = ftok(tmpfilename, 'V')) == -1) {
        perror("ftok");
        exit(1);
    }

    // grab the semaphore set created by seminit.c:
    if ((semid = initsem(key, 1)) == -1) {
        perror("initsem");
        exit(1);
    }
    return 1;
}

int vcontrol_semfree()
{
    // remove it:
    if (semctl(semid, 0, IPC_RMID) == -1) {
        perror("semctl");
        exit(1);
    }
    unlink(tmpfilename);
    return 1; // what should we return?
}

int vcontrol_semget()
{
    logIT(LOG_INFO, "Process %d tries to aquire lock", getpid());

    struct sembuf sb;

    sb.sem_num = 0;
    sb.sem_op = -1; // set to allocate resource
    sb.sem_flg = SEM_UNDO;
    if (semop(semid, &sb, 1) == -1) {
        perror("semop");
        exit(1);
    }
    logIT(LOG_INFO, "Process %d got lock", getpid());
    return 1;
}

int vcontrol_semrelease()
{
    struct sembuf sb;

    logIT(LOG_INFO, "Process %d released lock", getpid());

    sb.sem_num = 0;
    sb.sem_op = 1; // free resource
    sb.sem_flg = SEM_UNDO;
    if (semop(semid, &sb, 1) == -1) {
        perror("semop");
        exit(1);
    }

    return 1;
}
07070100000032000081A4000028240000003200000001607BF4480000044D000000000000000000000000000000000000003700000000vcontrold-v0.98.10+git20210418.977e6f5/src/semaphore.h/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef SEMAPHORE_H
#define SEMAPHORE_H

#include <sys/param.h>

#ifdef _CYGWIN__
#define TMPFILENAME "vcontrol.lockXXXXXX"
#else
#define TMPFILENAME "/tmp/vcontrol.lockXXXXXX"
#endif

extern char tmpfilename[MAXPATHLEN + 1]; // account for the leading '\0'

int vcontrol_seminit();
int vcontrol_semfree();
int vcontrol_semget();
int vcontrol_semrelease();

#endif // SEMAPHORE_H
07070100000033000081A4000028240000003200000001607BF448000024DF000000000000000000000000000000000000003400000000vcontrold-v0.98.10+git20210418.977e6f5/src/socket.c/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#define _GNU_SOURCE

#include <sys/time.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h> // TCP_NODELAY is defined there -fn-
#if defined (__FreeBSD__) || defined(__APPLE__)
#include <netinet/in.h>
#endif

#include "socket.h"
#include "common.h"
#include "vclient.h"

const int LISTEN_QUEUE = 128;

int openSocket(int tcpport)
{
    int listenfd;
    int n;
    char *port;
    struct addrinfo hints, *res, *ressave;

    memset(&hints, 0, sizeof(struct addrinfo));

    switch (inetversion) {
    case 6:
        hints.ai_family = PF_INET6;
        break;
    case 4:
        // this is for backward compatibility. We can explictly
        // activate IPv6 with the -6 switch. Later we can use
        // PF_UNSPEC as default and let the OS decide
    default:
        hints.ai_family = PF_INET;
    }

    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags    = AI_PASSIVE;

    asprintf(&port, "%d", tcpport);

    n = getaddrinfo(NULL, port, &hints, &res);

    free(port);

    if (n < 0) {
        logIT(LOG_ERR, "getaddrinfo error: [%s]\n", gai_strerror(n));
        return -1;
    }

    ressave = res;

    // Try open socket with each address getaddrinfo returned,
    // until getting a valid listening socket.

    listenfd = -1;
    while (res) {
        listenfd = socket(res->ai_family,
                          res->ai_socktype,
                          res->ai_protocol);
        int optval = 1;
        if (listenfd > 0 && setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,
                                       &optval, sizeof optval) < 0 ) {
            logIT1(LOG_ERR, "setsockopt failed!");
            exit(1);
        }
        if (! (listenfd < 0)) {
            if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0) {
                break;
            }
            close(listenfd);
            listenfd = -1;
        }
        res = res->ai_next;
    }

    if (listenfd < 0) {
        freeaddrinfo(ressave);
        fprintf(stderr, "socket error: could not open socket\n");
        exit(1);
    }

    listen(listenfd, LISTEN_QUEUE);
    logIT(LOG_NOTICE, "TCP socket %d opened", tcpport);

    freeaddrinfo(ressave);

    return listenfd;
}

int listenToSocket(int listenfd, int makeChild)
{
    int connfd;
    pid_t childpid;
    struct sockaddr_storage cliaddr;
    socklen_t cliaddrlen = sizeof(cliaddr);
    char clienthost   [NI_MAXHOST];
    char clientservice[NI_MAXSERV];

    signal(SIGCHLD, SIG_IGN);

    for (;;) {
        connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &cliaddrlen);
        getnameinfo((struct sockaddr *) &cliaddr, cliaddrlen, clienthost, sizeof(clienthost),
                    clientservice, sizeof(clientservice), NI_NUMERICHOST);

        if (connfd < 0) {
            logIT(LOG_NOTICE, "accept on host %s: port %s", clienthost, clientservice);
            close(connfd);
            continue;
        }

        logIT(LOG_NOTICE, "Client connected %s:%s (FD:%d)", clienthost, clientservice, connfd);
        if (! makeChild) {
            return connfd;
        } else if ( (childpid = fork()) == 0) {
            // unser Kind
            close(listenfd);
            return connfd;
        } else {
            logIT(LOG_INFO, "Child process started with pid %d", childpid);
        }

        close(connfd);
    }
}

void closeSocket(int sockfd)
{
    logIT(LOG_INFO, "Closed connection (fd:%d)", sockfd);
    close(sockfd);
}

int openCliSocket(char *host, int port, int noTCPdelay)
{
    struct addrinfo hints; // use hints for ipv46 address resolution
    struct addrinfo *res;
    struct addrinfo *ressave;
    int n;
    int sockfd;
    char port_string[16]; // the IPv6 world use a char* instead of an int in getaddrinfo

    memset(&hints, 0, sizeof(struct addrinfo));

    hints.ai_family   = PF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags    = AI_ALL | AI_V4MAPPED;

    snprintf(port_string, sizeof(port_string), "%d", port);
    n = getaddrinfo(host, port_string, &hints, &res);
    if (n < 0) {
        logIT(LOG_ERR, "Error in getaddrinfo: %s:%s", host, gai_strerror(n));
        exit(1);
    }

    ressave = res;

    sockfd = -1;
    while (res) {
        sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
        if (! (sockfd < 0)) {
            if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0) {
                break;
            }
            // we have a succesfull connection
            close(sockfd);
            sockfd = -1;
        }
        res = res->ai_next;
    }

    freeaddrinfo(ressave);

    if (sockfd < 0) {
        logIT(LOG_ERR, "TTY Net: No connection to %s:%d", host, port);
        return -1;
    }
    logIT(LOG_INFO, "ClI Net: connected %s:%d (FD:%d)", host, port, sockfd);
    int flag = 1;
    if (noTCPdelay && (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)))) {
        logIT(LOG_ERR, "Error in setsockopt TCP_NODELAY (%s)", strerror(errno));
    }

    return sockfd;
}

// Stuff aus Unix Network Programming Vol 1
// include writen

// Write "n" bytes to a descriptor.
ssize_t writen(int fd, const void *vptr, size_t n)
{
    size_t nleft;
    ssize_t nwritten;
    const char *ptr;

    ptr = vptr;
    nleft = n;
    while (nleft > 0) {
        if ((nwritten = write(fd, ptr, nleft)) <= 0) {
            if (errno == EINTR) {
                // and call write() again
                nwritten = 0;
            }  else {
                // error
                return -1;
            }
        }
        nleft -= nwritten;
        ptr   += nwritten;
    }
    return n;
}

// end writen

ssize_t Writen(int fd, void *ptr, size_t nbytes)
{
    if (writen(fd, ptr, nbytes) != nbytes) {
        logIT1(LOG_ERR, "Error writing to socket");
        return 0;
    }
    return nbytes;
}

// include readn

// Read "n" bytes from a descriptor.
ssize_t readn(int fd, void *vptr, size_t n)
{
    size_t nleft;
    ssize_t nread;
    char *ptr;

    ptr = vptr;
    nleft = n;
    while (nleft > 0) {
        if ((nread = read(fd, ptr, nleft)) < 0) {
            if (errno == EINTR) {
                // and call read() again
                nread = 0;
            }
            else {
                return -1;
            }
        } else if (nread == 0) {
            // EOF
            break;
        }

#ifdef __CYGWIN__
        // This is a workaround for Cygwin.
        // Here cygwins read(fd,buff,count) is reading more than count chars! this is bad!
        if (nread > nleft) {
            nleft = 0;
        } else {
            nleft -= nread;
        }
#else
        nleft -= nread;
#endif
        ptr   += nread;
    }

    return n - nleft; //return >= 0
}

// end readn

ssize_t Readn(int fd, void *ptr, size_t nbytes)
{
    ssize_t n;
    if ((n = readn(fd, ptr, nbytes)) < 0) {
        logIT1(LOG_ERR, "Error reading from socket");
        return 0;
    }
    return n;
}

// include readline

static ssize_t my_read(int fd, char *ptr)
{

    static ssize_t read_cnt = 0;
    static char *read_ptr;
    static char read_buf[MAXLINE];

    if (read_cnt <= 0) {
again:
        if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
            if (errno == EINTR) {
                goto again;
            }
            return -1;
        } else if (read_cnt == 0) {
            return 0;
        }
        read_ptr = read_buf;
    }

    read_cnt--;
    *ptr = *read_ptr++;
    return 1;
}

ssize_t readline(int fd, void *vptr, size_t maxlen)
{
    int n;
    ssize_t rc;
    char c;
    char *ptr;

    ptr = vptr;
    for (n = 1; n < maxlen; n++) {
        if ( (rc = my_read(fd, &c)) == 1) {
            *ptr++ = c;
            if (c == '\n') {
                // newline is stored, like fgets()
                break;
            }
        } else if (rc == 0) {
            if (n == 1) {
                // EOF, no data read
                return 0;
            }
            else {
                // EOF, some data was read
                break;
            }
        } else {
            // error, errno set by read()
            return -1;
        }
    }

    *ptr = 0; // null terminate like fgets()
    return n;
}

// end readline

ssize_t Readline(int fd, void *ptr, size_t maxlen)
{
    ssize_t n;

    if ((n = readline(fd, ptr, maxlen)) < 0) {
        logIT1(LOG_ERR, "Error reading from socket");
        return 0;
    }
    return n;
}
07070100000034000081A4000028240000003200000001607BF4480000053B000000000000000000000000000000000000003400000000vcontrold-v0.98.10+git20210418.977e6f5/src/socket.h/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef SOCKET_H
#define SOCKET_H

#include <arpa/inet.h>

int openSocket(int tcpport);
int listenToSocket(int listenfd, int makeChild);
int openCliSocket(char *host, int port, int noTCPdelay);
void closeSocket(int sockfd);

ssize_t writen(int fd, const void *vptr, size_t n);
ssize_t Writen(int fd, void *ptr, size_t nbytes);

ssize_t readn(int fd, void *vptr, size_t n);
ssize_t Readn(int fd, void *ptr, size_t nbytes);

ssize_t readline(int fd, void *vptr, size_t maxlen);
ssize_t Readline(int fd, void *ptr, size_t maxlen);

#define LISTENQ 1024
#define MAXLINE 1000

#define CONNECT_TIMEOUT 3

#endif // SOCKET_H
07070100000035000081A4000028240000003200000001607BF44800005363000000000000000000000000000000000000003200000000vcontrold-v0.98.10+git20210418.977e6f5/src/unit.c/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// Conversion of units

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <syslog.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
#include <ctype.h>
#include <arpa/inet.h>

#if defined __linux__ || defined __CYGWIN__
#include <asm/byteorder.h>
#endif

#ifdef __CYGWIN__
#define __le64_to_cpu(x) (x)
#define __le32_to_cpu(x) (x)
#define __le16_to_cpu(x) (x)
#define __cpu_to_le64(x) (x)
#define __cpu_to_le32(x) (x)
#define __cpu_to_le16(x) (x)
#endif

#include "xmlconfig.h"
#include "common.h"
#include "arithmetic.h"

// We need this at procSet ...
#define FLOAT 1
#define INT 2

#if defined (__APPLE__)
// this is needed to emulate the linux API calls

#include <libkern/OSByteOrder.h>

#define __cpu_to_be64(x) OSSwapHostToBigInt64(x)
#define __cpu_to_le64(x) OSSwapHostToLittleInt64(x)
#define __cpu_to_be32(x) OSSwapHostToBigInt32(x)
#define __cpu_to_le32(x) OSSwapHostToLittleInt32(x)
#define __cpu_to_be16(x) OSSwapHostToBigInt16(x)
#define __cpu_to_le16(x) OSSwapHostToLittleInt16(x)

#define __be64_to_cpu(x) OSSwapBigToHostInt64(x)
#define __le64_to_cpu(x) OSSwapLittleToHostInt64(x)
#define __be32_to_cpu(x) OSSwapBigToHostInt32(x)
#define __le32_to_cpu(x) OSSwapLittleToHostInt32(x)
#define __be16_to_cpu(x) OSSwapBigToHostInt16(x)
#define __le16_to_cpu(x) OSSwapLittleToHostInt16(x)

#endif

int getCycleTime(char *recv, int len, char *result);
int setCycleTime(char *string, char *sendBuf);
short bytes2Enum(enumPtr ptr, char *bytes, char **text, short len);
short text2Enum(enumPtr ptr, char *text, char **bytes, short *len);
int getErrState(enumPtr ePtr, char *recv, int len, char *result);
int getSysTime(char *recv, int len, char *result);
int setSysTime(char *input, char *sendBuf);

int getCycleTime(char *recv, int len, char *result)
{
    int i;
    char string[80];

    if (len % 2) {
        sprintf(result, "Byte count not even");
        return 0;
    }

    memset(string, 0, sizeof(string));

    for (i = 0; i < len; i += 2) {
        // TODO - vitoopen: Leave output in German.
        // CHANGING THIS WOULD BREAK existing applications.
        // => maybe we could enable english results later with a build option,
        // but not for the default.
        if (recv[i] == (char)0xff) {
            snprintf(string, sizeof(string), "%d:An:--     Aus:--\n", (i / 2) + 1);
        } else {
            snprintf(string, sizeof(string), "%d:An:%02d:%02d  Aus:%02d:%02d\n", (i / 2) + 1,
                     (recv[i] & 0xF8) >> 3, (recv[i] & 7) * 10,
                     (recv[i + 1] & 0xF8) >> 3, (recv[i + 1] & 7) * 10);
        }
        strcat(result, string);
    }
    result[strlen(result) - 1] = '\0'; // remove \n

    return 1;
}

int setCycleTime(char *input, char *sendBuf)
{
    char *sptr, *cptr;
    char *bptr = sendBuf;
    int hour, min;
    int count = 0;

    // We split at the blank
    sptr = strtok(input, " ");
    cptr = NULL;

    // First, we fill the sendBuf with 8 x ff
    for (count = 0; count < 8; sendBuf[count++] = 0xff);
    count = 0;

    do {
        if (sptr < cptr) {
            // We already have been here (by double blanks)
            continue;
        }

        while (isblank(*sptr)) {
            sptr++;
        }
        // Does the string only consist of one or two minus? --> This line remains empty
        if ((0 == strcmp(sptr, "-")) ||  (0 == strcmp(sptr, "--"))) {
            // We skip the next time designation, as it also must be a "-"
            bptr++;
            count++;
            sptr = strtok(NULL, " ");
            logIT(LOG_INFO, "Cycle Time: -- -- -> [%02X%02X]", 0xff, 0xff);
        } else {
            // Is the a : in the string?
            if (! strchr(sptr, ':')) {
                sprintf(sendBuf, "Wrong time format: %s", sptr);
                return 0;
            }
            sscanf(sptr, "%u:%u", &hour, &min);
            *bptr = ((hour << 3) + (min / 10)) & 0xff;
            logIT(LOG_INFO, "Cycle Time: %02d:%02d -> [%02X]", hour, min, (unsigned char) *bptr);
        }
        bptr++;
        cptr = sptr;
        count++;

    } while ((sptr = strtok(NULL, " ")) != NULL);

    if ((count / 2) * 2 != count) {
        logIT(LOG_WARNING, "Times count odd, ignoring %s", cptr);
        *(bptr - 1) = 0xff;
    }

    return 8;
}

int bcd2dec(int bcd) {
    int dec = bcd - ((int)(bcd / 16) * 6);
    return dec;
}

int getSysTime(char *recv, int len, char *result)
{
    struct tm *t;
    time_t tt;

    if (len != 8) {
        sprintf(result, "System time: Len <> 8 bytes");
        return 0;
    }

    // Use timezone information from the host system
    time(&tt);
    t = localtime(&tt);
    t->tm_year = bcd2dec(recv[0]) * 100 + bcd2dec(recv[1]) - 1900;
    t->tm_mon = bcd2dec(recv[2]) - 1;
    t->tm_mday = bcd2dec(recv[3]);
    t->tm_wday = bcd2dec(recv[4]) % 7;
    t->tm_hour = bcd2dec(recv[5]);
    t->tm_min = bcd2dec(recv[6]);
    t->tm_sec = bcd2dec(recv[7]);

    // This might break existing applications. But changing the string to some custom format
    // that is not recognized by strptime for parsing dates has a symmetry impact that the
    // format for getTime is different from the format required by setTime.
    // I would consider the command symmetry more important for the future.
    strftime(result, 48, "%FT%T%z", t);

    return 1;
}

int setSysTime(char *input, char *sendBuf)
{
    char systime[80];
    time_t tt;
    struct tm t_in = {0};
    struct tm *t;
    struct tm *th;

    memset(systime, 0, sizeof(systime));

    time(&tt);
    th = localtime(&tt);

    // No parameter, set the current system time
    if (!*input) {
        t = th;
    } else {
#ifdef _XOPEN_SOURCE
        char *parseEnd = strptime(input, "%FT%T%z", &t_in);
        // If the string is fully parsed, parseEnd should be the terminating '0' character of the input string.
        if (!parseEnd || *parseEnd) {
            logIT(LOG_ERR, "Can not parse time string '%s'. Use the same ISO 8601 time format for setting a time as you get when getting a time.", input);
            return 0;
        }
#else
        logIT1(LOG_ERR, "Setting an explicit time is not supported yet");
        return 0;
#endif

        // Recalculate the input time adjusted to the hosts timezone.
        // It is probably not the best idea to use internal variables, but
        // there doesn't seem to be an efficient way to transform times
        // between timezones.
#if defined(__CYGWIN__) || defined(__APPLE__)
        t_in.tm_sec += (th->tm_gmtoff - t_in.tm_gmtoff);
        t_in.tm_gmtoff = th->tm_gmtoff;
#else
        t_in.tm_sec += (th->__tm_gmtoff - t_in.__tm_gmtoff);
        t_in.__tm_gmtoff = th->__tm_gmtoff;
#endif
        t_in.tm_isdst = th->tm_isdst;
        mktime(&t_in);

        t = &t_in;
    }

    strftime(systime, 24, "%C %y %m %d %w %H %M %S", t);
    return string2chr(systime, sendBuf, 8);
}

int getErrState(enumPtr ePtr, char *recv, int len, char *result)
{
    int i;
    char *errtext;
    char systime[35];
    char string[300];
    char *ptr;

    if (len % 9) {
        sprintf(result, "Bytes count is not mod 9");
        return 0;
    }

    for (i = 0; i < len; i += 9) {
        ptr = recv + i;
        memset(string, 0, sizeof(string));
        memset(systime, 0, sizeof(systime));
        // Error code: Byte 0
        if (bytes2Enum(ePtr, ptr, &errtext, 1))
            // Rest SysTime
            if (getSysTime(ptr + 1, 8, systime)) {
                snprintf(string, sizeof(string),
                         "%s %s (%02X)\n", systime, errtext, (unsigned char) *ptr);
                strcat(result, string);
                continue;
            }
        // Format error
        sprintf(result, "%s %s", errtext, systime);
        return 0;
    }

    result[strlen(result) - 1] = '\0'; // Remove \n
    return 1;
}

short bytes2Enum(enumPtr ptr, char *bytes, char **text, short len)
{
    enumPtr ePtr = NULL;
    char string[200];

    if (! len) {
        return 0;
    }

    // Search for the appropriate enum and return the value
    if (! (ePtr = getEnumNode(ptr, bytes, len))) {
        // We search for the default
        ePtr = getEnumNode(ptr, bytes, -1);
    }
    if (ePtr) {
        *text = ePtr->text;
        memset(string, 0, sizeof(string));
        char2hex(string, bytes, len);
        strcat(string, " -> ");
        strcat(string, ePtr->text);
        logIT1(LOG_INFO, string);
        return 1;
    } else {
        return 0;
    }
}

short text2Enum(enumPtr ptr, char *text, char **bytes, short *len)
{
    enumPtr ePtr = NULL;
    char string[200];
    char string2[1000];

    // Search for the appropriate enum and return the value
    if (! (ePtr = getEnumNode(ptr, text, 0))) {
        return 0;
    }

    *bytes = ePtr->bytes;
    *len = ePtr->len;
    memset(string, 0, sizeof(string));
    strncpy(string, text, sizeof(string));
    strcat(string, " -> ");
    memset(string2, 0, sizeof(string2));
    char2hex(string2, ePtr->bytes, ePtr->len);
    strcat(string, string2);
    logIT1(LOG_INFO, string);

    return *len;
}

int procGetUnit(unitPtr uPtr, char *recvBuf, int recvLen, char *result, char bitpos, char *pRecvPtr)
{
    char string[256];
    char error[1000];
    char buffer[MAXBUF];
    char *errPtr = error;
    //short t;
    float erg;
    int ergI;
    char formatI[20];
    float floatV = 0;
    char *inPtr;
    char *tPtr;
    // Here the types for the conversion in <type> tags
    int8_t charV;
    uint8_t ucharV;
    int16_t shortV;
    int16_t tmpS;
    uint16_t ushortV;
    uint16_t tmpUS;
    int32_t intV;
    int32_t tmpI;
    uint32_t tmpUI;
    uint32_t uintV;

    memset(errPtr, 0, sizeof(error));

    // We tread the different <type> entries
    if (strstr(uPtr->type, "cycletime") == uPtr->type) {
        // Cycle time
        if (getCycleTime(recvBuf, recvLen, result)) {
            return 1;
        } else {
            return -1;
        }
    } else if (strstr(uPtr->type, "systime") == uPtr->type) {
        // System time
        if (getSysTime(recvBuf, recvLen, result)) {
            return 1;
        } else {
            return -1;
        }
    } else if (strstr(uPtr->type, "errstate") == uPtr->type) {
        // Error code + System time
        if (getErrState(uPtr->ePtr, recvBuf, recvLen, result)) {
            return 1;
        } else {
            return -1;
        }
    } else if (strstr(uPtr->type, "enum") == uPtr->type) {
        // enum
        if (bytes2Enum(uPtr->ePtr, recvBuf, &tPtr, recvLen)) {
            strcpy(result, tPtr);
            return 1;
        } else {
            sprintf(result, "Didn't find an appropriate enum");
            return -1;
        }
    }

    // Here are all the numeric types
    if (strstr(uPtr->type, "char") == uPtr->type) {
        // Conversion to Char 1Byte
        memcpy(&charV, recvBuf, 1);
        floatV = charV; // Implicit type conversion to float for our arithmetic
        sprintf(formatI, "%%02X %%s");
    } else if (strstr(uPtr->type, "uchar") == uPtr->type) {
        // Conversion to Unsigned Char 1Byte
        memcpy(&ucharV, recvBuf, 1);
        floatV = ucharV; // Implicit type conversion to float for our arithmetic
        sprintf(formatI, "%%02X %%s");
    } else if (strstr(uPtr->type, "short") == uPtr->type) {
        // Conversion to Short 2Byte
        memcpy(&tmpS, recvBuf, 2);
        // According to the CPU, the conversion is done here
        shortV = __le16_to_cpu(tmpS);
        floatV = shortV; // Implicit type conversion to float for our arithmetic
        sprintf(formatI, "%%04X %%s");
    } else if (strstr(uPtr->type, "ushort") == uPtr->type) {
        // Conversion to  Short 2Byte
        memcpy(&tmpUS, recvBuf, 2);
        // According to the CPU, the conversion is done here
        ushortV = __le16_to_cpu(tmpUS);
        floatV = ushortV; // Implicit type conversion to float for our arithmetic
        sprintf(formatI, "%%04X %%s");
    } else if (strstr(uPtr->type, "int") == uPtr->type) {
        // Conversion to Int 4Byte
        memcpy(&tmpI, recvBuf, 4);
        // According to the CPU, the conversion is done here
        intV = __le32_to_cpu(tmpI);
        floatV = intV; // Implicit type conversion to float for our arithmetic
        sprintf(formatI, "%%08X %%s");
    } else if (strstr(uPtr->type, "uint") == uPtr->type) {
        // Conversion to Unsigned Int 4Byte
        memcpy(&tmpUI, recvBuf, 4);
        // According to the CPU, the conversion is done here
        uintV = __le32_to_cpu(tmpUI);
        floatV = uintV; // Implicit type conversion to float for our arithmetic
        sprintf(formatI, "%%08X %%s");
    } else if (uPtr->type) {
        logIT(LOG_ERR, "Unknown type %s in unit %s", uPtr->type, uPtr->name);
        return -1;
    }

    // Some logging
    int n;
    char *ptr;
    char res;

    ptr = recvBuf;
    memset(buffer, 0, sizeof(buffer));
    for (n = 0; n <= 9; n++) {
        // The bytes 0..9 are of interest
        memset(string, 0, sizeof(string));
        unsigned char byte = *ptr++ & 255;
        snprintf(string, sizeof(string), "B%d:%02X ", n, byte);
        strcat(buffer, string);
        if (n >= MAXBUF - 3) {
            break;
        }
    }

    if (uPtr->gCalc && *uPtr->gCalc) {
        // calc in XML and get defined within
        logIT(LOG_INFO, "Typ: %s (in float: %f)", uPtr->type, floatV);
        inPtr = uPtr->gCalc;
        logIT(LOG_INFO, "(FLOAT) Exp: %s [%s]", inPtr, buffer);
        erg = execExpression(&inPtr, recvBuf, floatV, errPtr);
        if (*errPtr) {
            logIT(LOG_ERR, "Exec %s: %s", uPtr->gCalc, error);
            strcpy(result, string);
            return -1;
        }
        sprintf(result, "%f %s", erg, uPtr->entity);
    } else if (uPtr->gICalc && *uPtr->gICalc) {
        // icalc in XML and get defined within
        inPtr = uPtr->gICalc;
        logIT(LOG_INFO, "(INT) Exp: %s [BP:%d] [%s]", inPtr, bitpos, buffer);
        ergI = execIExpression(&inPtr, recvBuf, bitpos, pRecvPtr, errPtr);
        if (*errPtr) {
            logIT(LOG_ERR, "Exec %s: %s", uPtr->gCalc, error);
            strcpy(result, string);
            return -1;
        }
        logIT(LOG_INFO, "Res: (Hex max. 4 bytes) %08x", ergI);
        res = ergI;
        if ( uPtr->ePtr && bytes2Enum(uPtr->ePtr, &res, &tPtr, recvLen)) {
            strcpy(result, tPtr);
            return 1;
        } else {
            sprintf(result, formatI, ergI, uPtr->entity);
            return 1;
        }
        // Probably do the enum search here
    }
    return 1;
}

int procSetUnit(unitPtr uPtr, char *sendBuf, short *sendLen, char bitpos, char *pRecvPtr)
{
    char string[256];
    char error[1000];
    char buffer[MAXBUF];
    char input[MAXBUF];
    char *errPtr = error;
    // short t;
    float erg = 0.0;
    int ergI = 0;
    short count;
    char ergType;
    float floatV;
    char *inPtr;
    // Here the types for the <type> tag conversion
    int8_t charV;
    uint8_t ucharV;
    int16_t shortV;
    int16_t tmpS;
    uint16_t ushortV;
    uint16_t tmpUS;
    int32_t intV;
    int32_t tmpI;
    uint32_t tmpUI;
    uint32_t uintV;

    memset(errPtr, 0, sizeof(error));
    // Some logging
    int n = 0;
    char *ptr;
    char dumBuf[10];
    memset(dumBuf, 0, sizeof(dumBuf));
    memset(buffer, 0, sizeof(buffer));
    // We copy the sendBuf, as this one is also used for return
    strncpy(input, sendBuf, sizeof(input));
    memset(sendBuf, 0, *sendLen);

    if (strstr(uPtr->type, "cycletime") == uPtr->type) {
        // Cycle time
        if (! *input) {
            return -1;
        }
        if (! (*sendLen = setCycleTime(input, sendBuf))) {
            return -1;
        } else  {
            return 1;
        }
    }
    if (strstr(uPtr->type, "systime") == uPtr->type) {
        // System time
        if (! (*sendLen = setSysTime(input, sendBuf))) {
            return -1;
        } else  {
            return 1;
        }
    } else if (strstr(uPtr->type, "enum") == uPtr->type) {
        // enum
        if (! *input)
        { return -1; }
        if (!(count = text2Enum(uPtr->ePtr, input, &ptr, sendLen))) {
            sprintf(sendBuf, "Did not find an appropriate enum");
            return -1;
        } else {
            memcpy(sendBuf, ptr, count);
            return 1;
        }
    }

    if (! *input) {
        return -1;
    }

    // Here the forwarded value
    if (uPtr->sCalc && *uPtr->sCalc) {
        // calc in XML and get defined within
        floatV = atof(input);
        inPtr = uPtr->sCalc;
        logIT(LOG_INFO, "Send Exp: %s [V=%f]", inPtr, floatV);
        erg = execExpression(&inPtr, dumBuf, floatV, errPtr);
        if (*errPtr) {
            logIT(LOG_ERR, "Exec %s: %s", uPtr->sCalc, error);
            strcpy(sendBuf, string);
            return -1;
        }
        ergType = FLOAT;
    }

    if (uPtr->sICalc && *uPtr->sICalc) {
        // icalc in XML and get defined within
        inPtr = uPtr->sICalc;
        if (uPtr->ePtr) {
            // There are enums
            if (! *input) {
                sprintf(sendBuf, "Input missing");
                return -1;
            }
            if (! (count = text2Enum(uPtr->ePtr, input, &ptr, sendLen))) {
                sprintf(sendBuf, "Did not find an appropriate enum");
                return -1;
            } else {
                memset(dumBuf, 0, sizeof(dumBuf));
                memcpy(dumBuf, ptr, count);
                logIT(LOG_INFO, "(INT) Exp: %s [BP:%d]", inPtr, bitpos);
                ergI = execIExpression(&inPtr, dumBuf, bitpos, pRecvPtr, errPtr);
                if (*errPtr) {
                    logIT(LOG_ERR, "Exec %s: %s", uPtr->sICalc, error);
                    strcpy(sendBuf, string);
                    return -1;
                }
                ergType = INT;
                snprintf(string, sizeof(string), "Res: (Hex max. 4 bytes) %08x", ergI);
            }
        }
    }

    // The result is in erg and has to be converted according to the type
    if (uPtr->type) {
        if (strstr(uPtr->type, "char") == uPtr->type) {
            // Conversion to Short 2Byte
            // According to the CPU, the conversion is done here
            (ergType == FLOAT) ? (charV = erg) : (charV = ergI);
            memcpy(sendBuf, &charV, 1);
            *sendLen = 1;
        } else if (strstr(uPtr->type, "uchar") == uPtr->type) {
            // According to the CPU, the conversion is done here
            (ergType == FLOAT) ? (ucharV = erg) : (ucharV = ergI);
            memcpy(sendBuf, &ucharV, 1);
            *sendLen = 1;
        } else if (strstr(uPtr->type, "short") == uPtr->type) {
            // According to the CPU, the conversion is done here
            (ergType == FLOAT) ? (tmpS = erg) : (tmpS = ergI);
            shortV = __cpu_to_le16(tmpS);
            memcpy(sendBuf, &shortV, 2);
            *sendLen = 2;
        } else if (strstr(uPtr->type, "ushort") == uPtr->type) {
            // According to the CPU, the conversion is done here
            (ergType == FLOAT) ? (tmpUS = erg) : (tmpUS = ergI);
            ushortV = __cpu_to_le16(tmpUS);
            memcpy(sendBuf, &ushortV, 2);
            *sendLen = 2;
        } else if (strstr(uPtr->type, "int") == uPtr->type) {
            // According to the CPU, the conversion is done here
            (ergType == FLOAT) ? (tmpI = erg) : (tmpI = ergI);
            intV = __cpu_to_le32(tmpI);
            memcpy(sendBuf, &intV, 2);
            *sendLen = 4;
        } else if (strstr(uPtr->type, "uint") == uPtr->type) {
            // According to the CPU, the conversion is done here
            (ergType == FLOAT) ? (tmpUI = erg) : (tmpUI = ergI);
            uintV = __cpu_to_le32(tmpUI);
            memcpy(sendBuf, &uintV, 2);
        } else if (uPtr->type) {
            memset(string, 0, sizeof(string));
            logIT(LOG_ERR, "Unknown type %s in unit %s", uPtr->type, uPtr->name);
            return -1;
        }

        memset(buffer, 0, sizeof(buffer));
        ptr = sendBuf;
        while (*ptr) {
            memset(string, 0, sizeof(string));
            unsigned char byte = *ptr++ & 255;
            snprintf(string, sizeof(string), "%02X ", byte);
            strcat(buffer, string);
            if (n >= MAXBUF - 3) {
                // FN: Where is 'n' initialized?!
                break;
            }
        }

        logIT(LOG_INFO, "Type: %s (bytes: %s)  ", uPtr->type, buffer);

        return 1;
    }

    // We should never reach here. But we want to keep the compiler happy.
    return 0;
}
07070100000036000081A4000028240000003200000001607BF448000003BF000000000000000000000000000000000000003200000000vcontrold-v0.98.10+git20210418.977e6f5/src/unit.h/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef UNIT_H
#define UNIT_H

int procGetUnit(unitPtr uPtr, char *recvBuf, int len, char *result, char bitpos, char *pRecvPtr);
int procSetUnit(unitPtr uPtr, char *sendBuf, short *sendLen, char bitpos, char *pRecvPtr);

#endif // UNIT_H
07070100000037000081A4000028240000003200000001607BF448000058C5000000000000000000000000000000000000003500000000vcontrold-v0.98.10+git20210418.977e6f5/src/vclient.c/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// Vcontrold client

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <syslog.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <getopt.h>
#include <fcntl.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/ioctl.h>

#include "common.h"
#include "socket.h"
#include "io.h"
#include "client.h"
#include "vclient.h"
#include "version.h"
#include "prompt.h"

// global variables
int inetversion = 0;

void usage()
{
    //      1       10        20        30        40        50        60        70        80

    printf("usage:\n");
    printf("    vclient [-h <ip:port>] [-c <command1,command2,..>] [-f <commandfile>]\n");
    printf("            [-s <csv file>] [-t <template file>] [-o <output file>]\n");
    printf("            [-x <exec file>] [-k] [-m] [-v] [-j] [-J]\n\n");

    printf("or:\n");
    printf("    vclient [--host <ip>] [--port <port>] [--command <command1,command2,..>]\n");
    printf("            [--commandfile <command file>] [--csvfile <csv file>]\n");
    printf("            [--template <template file>] [--output <output file>]\n");
    printf("            [--execute <exec file>] [--cacti] [--munin] [--verbose]\n");
    printf("            [--json-short] [--json-long]\n");
    printf("            [command3 [command4] ...]\n\n");

    printf("    -h|--host         <IPv4>:<Port> or <IPv6> of vcontrold\n");
    printf("    -p|--port         <port> of vcontrold when using IPv6\n");
    printf("    -c|--command      List of commands to be executed, sparated by commas\n");
    printf("    -f|--commandfile  Optional command file, one command per line\n");
    printf("    -s|--csvfile      Format output in CSV for further processing\n");
    printf("    -t|--template     Template, variables are substituted with acquired values\n");
    printf("    -o|--output       Write to given file instead of STDOUT\n");
    printf("    -x|--execute      The converted template (cf. -t) is written to the given\n");
    printf("                      file and executed subsequently\n");
    printf("    -m|--munin        Output a Munin data logger compatible format (units and\n");
    printf("                      error details are discarded)\n");
    printf("    -k|--cacti        Output a Cacti data logger compatible format (units and\n");
    printf("                      error details are discarded)\n");
    printf("    -j|--json-short   Output json data (short form, just command and value)\n");
    printf("    -J|--json-long    Output json data (long form including raw and errors)\n");
    printf("    -v|--verbose      Be verbose (for testing purposes)\n");
    printf("    -V|--Version      Print version and exit\n");
    printf("    -4|--inet4        IPv4 is preferred\n");
    printf("    -6|--inet6        IPv6 is preferred\n");
    printf("                      (if none of the two above is set, the system default will\n");
    printf("                      be used)\n");
    printf("    --help            Display this help message\n\n");

    exit(1);
}

int main(int argc, char *argv[])
{
    // Get the command line options
    char *host = NULL;
    int port = 0;
    char commands[512] = "";
    const char *cmdfile = NULL;
    const char *csvfile = NULL;
    const char *tmplfile = NULL;
    const char *outfile = NULL;
    char string[1024] = "";
    char result[1024] = "";
    int sockfd;
    char dummylog[] = "\0";
    int opt;
    static int verbose = 0;
    static int munin = 0;
    static int cacti = 0;
    static int json_short = 0;
    static int json_long = 0;
    short execMe = 0;
    trPtr resPtr;
    FILE *filePtr;
    FILE *ofilePtr;

    while (1) {
        static struct option long_options[] = {
            {"host",        required_argument, 0,            'h'},
            {"port",        required_argument, 0,            'p'},
            {"command",     required_argument, 0,            'c'},
            {"commandfile", required_argument, 0,            'f'},
            {"csvfile",     required_argument, 0,            's'},
            {"template",    required_argument, 0,            't'},
            {"output",      required_argument, 0,            'o'},
            {"execute",     required_argument, 0,            'x'},
            {"verbose",     no_argument,       &verbose,     1  },
            {"Version",     no_argument,       0,            0  },
            {"munin",       no_argument,       &munin,       1  },
            {"cacti",       no_argument,       &cacti,       1  },
            {"json-short",  no_argument,       &json_short,  1  },
            {"json-long",   no_argument,       &json_long,   1  },
            {"inet4",       no_argument,       &inetversion, 4  },
            {"inet6",       no_argument,       &inetversion, 6  },
            {"help",        no_argument,       0,            0  },
            {0,             0,                 0,            0  }
        };
        // getopt_long stores the option index here.
        int option_index = 0;
        opt = getopt_long(argc, argv, "h:p:c:f:s:t:o:x:vVmkjJ46", long_options, &option_index);

        // Detect the end of the options.
        if (opt == -1) {
            break;
        }

        switch (opt) {
        case 0:
            // If this option sets a flag, we do nothing for now
            if (long_options[option_index].flag != 0) {
                break;
            }
            if (verbose) {
                printf("option %s", long_options[option_index].name);
                if (optarg) {
                    printf(" with arg %s", optarg);
                }
                printf("\n");
            }
            if (strcmp("help", long_options[option_index].name) == 0) {
                usage();
            }
            break;
        case 'v':
            puts("option -v\n");
            verbose = 1;
            break;
        case 'V':
            printf("vclient version %s\n", VERSION);
            exit(1);
            break;
        case 'm':
            if (verbose) {
                puts("option -m\n");
            }
            munin = 1;
            break;
        case 'k':
            if (verbose) {
                puts("option -k\n");
            }
            cacti = 1;
            break;
        case 'j':
            if (verbose) {
                puts("option -j\n");
            }
            json_short = 1;
            break;
        case 'J':
            if (verbose) {
                puts("option -J\n");
            }
            json_long = 1;
            break;
        case 'h':
            if (verbose) {
                printf("option -h with value `%s'\n", optarg);
            }
            host = optarg;
            break;
        case 'p':
            if (verbose) {
                printf("option -p with value `%s'\n", optarg);
            }
            port = atoi(optarg);
            if (port == 0) {
                fprintf(stderr, "Invalid value for option --port: %s\n", optarg);
                usage(); // and exit
            }
            break;
        case 'c':
            if (verbose) {
                printf("option -c with value `%s'\n", optarg);
                printf("sizeof optarg:%zu, strlen:%zu, sizeof commands:%zu, strlen:%zu,  [%s]\n",
                       sizeof(optarg), strlen(optarg), sizeof(commands), strlen(commands),
                       commands);
            }
            if (strlen(commands) == 0) {
                strncpy(commands, optarg, sizeof(commands));
            } else {
                if (strlen(optarg) + 2 > sizeof(commands) - strlen(commands)) {
                    fprintf(stderr, "too many commands\n");
                    break;
                }
                strcat(commands, ",");
                strncat(commands, optarg, sizeof(commands) - strlen(commands) - 2);
            }
            break;
        case 'f':
            if (verbose) {
                printf("option -f with value `%s'\n", optarg);
            }
            cmdfile = optarg;
            break;
        case 's':
            if (verbose) {
                printf("option -s with value `%s'\n", optarg);
            }
            csvfile = optarg;
            break;
        case 't':
            if (verbose) {
                printf("option -t with value `%s'\n", optarg);
            }
            tmplfile = optarg;
            break;

        case 'o':
        case 'x':
            if (verbose) {
                printf("option -%c with value `%s'\n", opt, optarg);
            }
            outfile = optarg;
            if (opt == 'x') {
                execMe = 1;
            }
            break;
        case '4':
            if (verbose) {
                printf("option -%c with value `%s'\n", opt, optarg);
            }
            inetversion = 4;
            break;
        case '6':
            if (verbose) {
                printf("option -%c with value `%s'\n", opt, optarg);
            }
            inetversion = 6;
            break;
        case '?':
            // getopt_long already printed an error message.
            usage();
            break;
        default:
            abort();
        }
    }

    if (host == NULL) {
      host = "localhost";
    }

    // Collect any remaining command line arguments (not options).
    // and use the as commands like for the -c option.
    if (optind < argc) {
        if (verbose) {
            printf("non-option ARGV-elements: ");
        }
        while (optind < argc) {
            if (verbose) {
                printf("%s ", argv[optind]);
            }
            if (strlen(commands) == 0) {
                strncpy(commands, argv[optind], sizeof(commands));
            } else {
                if (strlen(argv[optind]) + 2 > sizeof(commands) - strlen(commands)) {
                    fprintf(stderr, "Command list too long\n");
                    optind++;
                    break;
                }
                strcat(commands, ",");
                strncat(commands, argv[optind], sizeof(commands) - strlen(commands) - 2);
            }
            optind++;
        }
        if (verbose) {
            putchar('\n');
        }
    }

   /* Check for :<port> if port==0
       then separate the port number from the host name
       or the IP adsress.
       The IP address could be a plain old IPv4 or a IPv6 one,
       which could contain more than one ':', so that makes a bad
       separator in the IPv6 case and you better use --port
       -h 192.168.2.1:3002 vs --host 2003:abcd:ff::1 --port 3002
       or --host 2003:abcd:ff::1:3002, assume the last :3002 be the port
       This is just for backwards compatibility. */
    if (port == 0) {
        // check for last ':' in host
        char *last_colon = NULL;

        last_colon = strrchr(host, ':');
        if (last_colon != NULL) {
            port = atoi(last_colon + 1);
            *last_colon = '\0';
        }
    }
    if (port == 0) {
        port = DEFAULT_PORT;
    }

    if (verbose) {
      printf("Host: %s Port: %d\n",host,port);
    }

    initLog(0, dummylog, verbose);
    if (! *commands && !cmdfile) {
        usage();
    }

    sockfd = connectServer(host, port);
    if (sockfd < 0) {
        logIT(LOG_ERR, "No connection to host %s on port %d", host, port);
        exit(1);
    }

    // Give commands directly
    resPtr = NULL;
    if (*commands) {
        resPtr = sendCmds(sockfd, commands);
    } else if (cmdfile) {
        resPtr = sendCmdFile(sockfd, cmdfile);
    }
    if (! resPtr) {
        logIT(LOG_ERR, "Error communicating with the server");
        exit(1);
    }
    disconnectServer(sockfd);

    if (outfile) {
        if (! (ofilePtr = fopen(outfile, "w"))) {
            logIT(LOG_ERR, "Could not create file %s", outfile);
            exit(1);
        }
        memset(string, 0, sizeof(string));
        logIT(LOG_INFO, "Output file %s", outfile);
    } else {
        ofilePtr = fdopen(fileno(stdout), "w");
    }

    // The result is in the resPtr list, now we differentiate the output
    if (csvfile) {
        // Kompakt Format mit Semikolon getrennt
        if (! (filePtr = fopen(csvfile, "a"))) {
            logIT(LOG_ERR, "Could not create file %s", csvfile);
            exit(1);
        }
        memset(string, 0, sizeof(string));
        memset(result, 0, sizeof(result));
        while (resPtr) {
            if (resPtr->err) {
                //fprintf(stderr,"%s:%s\n",resPtr->cmd,resPtr->err);
                fprintf(stderr, "%s: server error\n", resPtr->cmd);
                strcat(result, ";");
                resPtr = resPtr->next;
                continue;
            }
            memset(string, 0, sizeof(string));
            sprintf(string, "%f;", resPtr->result);
            strncat(result, string, sizeof(result) - strlen(result) - 1);
            resPtr = resPtr->next;
        }
        // Remove the last semicolon and add \n
        if (*result) {
            *(result + strlen(result) - 1) = '\n';
            fputs(result, filePtr);
        }
        fclose(filePtr);
    } else if (tmplfile) {
        // Template given
        char line[1000];
        char *lptr;
        char *lSptr;
        char *lEptr;
        char varname[20];
        unsigned short count;
        unsigned short idx;
        unsigned short maxIdx;
        trPtr tPtr = resPtr;
        trPtr *idxPtr;
        short varReplaced;

        // In array idxPtr, the particular results are referenced via the index
        for (count = 0; tPtr; tPtr = tPtr->next) {
            count++;
        }

        // We reserve an array with a suitable size
        idxPtr = calloc(count, sizeof(tPtr));

        maxIdx = count; // Biggest index in the variables

        count = 0;
        tPtr = resPtr;
        while (tPtr) {
            idxPtr[count++] = tPtr;
            tPtr = tPtr->next;
        }

        if (! (filePtr = fopen(tmplfile, "r"))) {
            logIT(LOG_ERR, "Could not open template file %s", tmplfile);
            exit(1);
        }
        // The following variables are to replace:
        // $Rn: Result (trPtr->raw)
        // $n: Float (trPtr->result)
        while ((fgets(line, sizeof(line) - 1, filePtr))) {
            logIT(LOG_INFO, "Tmpl line: %s", line);
            lSptr = line;
            while ((lptr = strchr(lSptr, '$'))) {
                varReplaced = 0;
                if ((lptr > line) && (*(lptr - 1) == '\\')) { // $ is masked by a backslash
                    memset(string, 0, sizeof(string));
                    strncpy(string, lSptr, lptr - lSptr - 1);
                    fprintf(ofilePtr, "%s%c", string, *lptr);
                    lSptr = lptr + 1;
                    continue;
                }
                lEptr = lptr + 1;
                // Noew, we search the end of the variables.
                while (isalpha(*lEptr) || isdigit(*lEptr)) {
                    lEptr++;
                }
                memset(varname, 0, sizeof(varname));
                strncpy(varname, lptr + 1, lEptr - lptr - 1);
                logIT(LOG_INFO, "    Recognized variable: %s", varname);

                // We output everything up to this
                memset(string, 0, sizeof(string));
                strncpy(string, lSptr, lptr - lSptr);
                fprintf(ofilePtr, "%s", string);

                // We differentiate the different variables

                // $Rn
                if ((strlen(varname) > 1) && (*varname == 'R') && (idx = atoi(varname + 1))) {
                    // Variable R index then in idx
                    if ((idx - 1) < maxIdx) {
                        tPtr = idxPtr[idx - 1];
                        logIT(LOG_INFO, "%s:%s", tPtr->cmd, tPtr->raw);
                        if (tPtr->raw) {
                            // if tPtr->raw starts with "ERR:" output only if verbose
                            if (!((verbose == 0) && strncmp(tPtr->raw, ERR, strlen(ERR)) == 0)) {
                                fprintf(ofilePtr, "%s", tPtr->raw);
                            }
                        }
                    } else {
                        logIT(LOG_ERR, "Index of variable $%s > %d", varname, maxIdx - 1);
                    }
                }
                // $Cn
                else if ((strlen(varname) > 1) && (*varname == 'C') && (idx = atoi(varname + 1))) {
                    // Variable R index then in idx
                    if ((idx - 1) < maxIdx) {
                        tPtr = idxPtr[idx - 1];
                        logIT(LOG_INFO, "Command: %s", tPtr->cmd);
                        if (tPtr->cmd) {
                            fprintf(ofilePtr, "%s", tPtr->cmd);
                        }
                    } else {
                        logIT(LOG_ERR, "Index of Variable $%s > %d", varname, maxIdx - 1);
                    }
                }
                // $En
                else if ((strlen(varname) > 1) && (*varname == 'E') && (idx = atoi(varname + 1))) {
                    // Variable R index then in idx
                    if ((idx - 1) < maxIdx) {
                        tPtr = idxPtr[idx - 1];
                        logIT(LOG_INFO, "Error: %s: %s", tPtr->cmd, tPtr->err);
                        if (tPtr->err) {
                            fprintf(ofilePtr, "%s", tPtr->err);
                        } else {
                            fprintf(ofilePtr, "OK");
                        }
                    } else {
                        logIT(LOG_ERR, "Index of variable $%s > %d", varname, maxIdx - 1);
                    }
                }
                // $n
                else if (isdigit(*varname) && (idx = atoi(varname)))  {
                    if ((idx - 1) < maxIdx) {
                        tPtr = idxPtr[idx - 1];
                        logIT(LOG_INFO, "%s:%f", tPtr->cmd, tPtr->result);
                        //if (tPtr->result)
                        fprintf(ofilePtr, "%f", tPtr->result);
                    } else {
                        logIT(LOG_ERR, "Index of variable $%s > %d", varname, maxIdx - 1);
                    }
                } else {
                    memset(string, 0, sizeof(string));
                    strncpy(string, lptr, lEptr - lptr);
                    fprintf(ofilePtr, "%s", string);
                }
                lSptr = lEptr;
            }
            fprintf(ofilePtr, "%s", lSptr);
        }

        fclose(filePtr);

        if (outfile && *outfile && execMe) {
            // Make the file executable and start
            fclose(ofilePtr);
            memset(string, 0, sizeof(string));
            logIT(LOG_INFO, "Executing file %s", outfile);
            if (chmod(outfile, S_IXUSR | S_IRUSR | S_IWUSR) != 0) {
                logIT(LOG_ERR, "Error chmod +x %s", outfile);
                exit(1);
            }

            short ret;
            if ((ret = system(outfile)) == -1) {
                logIT(LOG_ERR, "Error system(%s)", outfile);
                exit(1);
            }

            logIT(LOG_INFO, "Ret Code: %d", ret);
            exit(ret);
        }

    } else if (munin) {
        // Output Munin format
        while (resPtr) {
            fprintf(ofilePtr, "%s.value ", resPtr->cmd);
            if (resPtr->err) {
                fprintf(ofilePtr, "U\n");
                resPtr = resPtr->next;
                continue;
            }
            if (resPtr->raw) {
                fprintf(ofilePtr, "%f\n", resPtr->result);
            } else {
                fprintf(ofilePtr, "U\n");
            }
            resPtr = resPtr->next;
        }

    } else if (cacti) {
        // Output Cacti format
        int index = 1;
        while (resPtr) {
            fprintf(ofilePtr, "v%d:", index);
            if (resPtr->err) {
                fprintf(ofilePtr, "U ");
                resPtr = resPtr->next;
                index++;
                continue;
            }
            if (resPtr->raw) {
                fprintf(ofilePtr, "%f ", resPtr->result);
            } else {
                fprintf(ofilePtr, "U ");
            }
            index++;
            resPtr = resPtr->next;
        }
        fprintf(ofilePtr, "\n");

    } else if (json_short) {
        // Output JSON format - object with cmd,value pairs
        int index = 1;
	fprintf(ofilePtr, "{");
        while (resPtr) {
            fprintf(ofilePtr, "\"%s\":", resPtr->cmd);
            fprintf(ofilePtr, "%f", resPtr->result);
            index++;
            resPtr = resPtr->next;
            if (resPtr) {
                fprintf(ofilePtr, ",");
            }
        }
        fprintf(ofilePtr, "}\n");

    } else if (json_long) {
        // Output JSON format list with with objects in it
        int index = 1;
        fprintf(ofilePtr,"[");
        while (resPtr) {
            fprintf(ofilePtr, "{", resPtr->cmd);
            fprintf(ofilePtr, "\"command\":\"%s\",", resPtr->cmd);
            fprintf(ofilePtr, "\"value\":%f,", resPtr->result);
            fprintf(ofilePtr, "\"raw\":\"%s\",", resPtr->raw);
            if (resPtr->err) {
                fprintf(ofilePtr, "\"error\":\"%s\"", resPtr->err);
            } else {
                fprintf(ofilePtr, "\"error\":\"\"");
            }
            fprintf(ofilePtr, "}");
            index++;
            resPtr = resPtr->next;
            if (resPtr) {
                fprintf(ofilePtr, ",");
            }
        }
        fprintf(ofilePtr, "]\n");

    } else {
        while (resPtr) {
            fprintf(ofilePtr, "%s:\n", resPtr->cmd);
            if (resPtr->err) {
                //fprintf(stderr,"%s",resPtr->err);
                fprintf(stderr, "server error\n");
                resPtr = resPtr->next;
                continue;
            }
            if (resPtr->raw) {
                fprintf(ofilePtr, "%s\n", resPtr->raw);
            }
            resPtr = resPtr->next;
        }
    }

    return 0;
}
07070100000038000081A4000028240000003200000001607BF4480000030D000000000000000000000000000000000000003500000000vcontrold-v0.98.10+git20210418.977e6f5/src/vclient.h/*  Copyright 2007-2017 Frank Nobis <...>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef VCLIENT_H
#define VCLIENT_H

extern int inetversion;

#endif // VCLIENT_H
07070100000039000081A4000028240000003200000001607BF44800008503000000000000000000000000000000000000003700000000vcontrold-v0.98.10+git20210418.977e6f5/src/vcontrold.c/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// Vito control daemon for vito queries

#define _GNU_SOURCE

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <syslog.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <time.h>
#include <getopt.h>
#include <grp.h>
#include <pwd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <sys/time.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>

#include "io.h"
#include "common.h"
#include "xmlconfig.h"
#include "parser.h"
#include "socket.h"
#include "prompt.h"
#include "semaphore.h"
#include "framer.h"

#ifdef __CYGWIN__
#define XMLFILE "vcontrold.xml"
#define INIOUTFILE "sim-%s.ini"
#else
#define XMLFILE "/etc/vcontrold/vcontrold.xml"
#define INIOUTFILE "/tmp/sim-%s.ini"
#endif

#include "version.h"

// Global variables
char *xmlfile = XMLFILE;
FILE *iniFD = NULL;
int makeDaemon = 1;
int inetversion = 0;

// Defined in xmlconfig.c
extern protocolPtr protoPtr;
extern unitPtr uPtr;
extern devicePtr devPtr;
extern configPtr cfgPtr;

// Declarations
int readCmdFile(char *filename, char *result, int *resultLen, char *device);
int interactive(int socketfd, char *device);
void printHelp(int socketfd);
int rawModus (int socketfd, char *device);
static void sigPipeHandler(int signo);
static void sigHupHandler(int signo);
int reloadConfig();

void usage()
{
    //      1       10        20        30        40        50        60        70        80

    printf("usage: vcontrold [-x|--xmlfile xml-file] [-d|--device <device>]\n");
    printf("                 [-l|--logfile <logfile>] [-p|--port port] [-s|--syslog]\n");
    printf("                 [-n|--nodaemon] [-v|--verbose] [-V|--Version]\n");
    printf("                 [-c|--commandfile <command-file>] [-P|--pidfile <pid-file>]");
    printf("                 [-U|--username <username>] [-G|--groupname <groupname>]\n");
    printf("                 [-?|--help] [-i|--vsim] [-g|--debug]\n");
    printf("                 [-4|--inet4] [-6|--inet6]\n\n");

    exit(1);
}

int reloadConfig()
{
    if (parseXMLFile(xmlfile)) {
        compileCommand(devPtr, uPtr);
        logIT(LOG_NOTICE, "XML file %s reloaded", xmlfile);
        return 1;
    } else {
        logIT(LOG_ERR, "Loading of XML file %s failed", xmlfile);
        return 0;
    }
}

int readCmdFile(char *filename, char *result, int *resultLen, char *device)
{
    FILE *cmdPtr;
    char line[MAXBUF];
    char string[256];
    char recvBuf[MAXBUF];
    char *resultPtr = result;
    int fd;
    int count = 0;
    *resultLen = 0; // nothing received yet :-)

    // Open the device only if we have something to do
    vcontrol_semget();
    if ((fd = framer_openDevice(device, cfgPtr->devPtr->protoPtr->id)) == -1) {
        vcontrol_semrelease();
        logIT(LOG_ERR, "Error opening %s", device);
        result = "\0";
        *resultLen = 0;
        return 0;
    }

    cmdPtr = fopen(filename, "r");
    if (! cmdPtr) {
        logIT(LOG_ERR, "Could not open cmd file %s", filename);
        result = "\0";
        *resultLen = 0;
        framer_closeDevice(fd);
        vcontrol_semrelease();
        return 0;
    }
    logIT(LOG_INFO, "Reading cmd file %s", filename);
    // Empty queue
    tcflush(fd, TCIOFLUSH);
    while (fgets(line, MAXBUF - 1, cmdPtr)) {
        // Remove \n
        line[strlen(line) - 1] = '\0';
        memset(recvBuf, 0, sizeof(recvBuf));
        count = execCmd(line, fd, recvBuf, sizeof(recvBuf));
        int n;
        char *ptr;
        ptr = recvBuf;
        char buffer[MAXBUF];
        memset(buffer, 0, sizeof(buffer));
        for (n = 0; n < count; n++) {
            // We received characters
            *resultPtr++ = *ptr;
            (*resultLen)++;
            memset(string, 0, sizeof(string));
            unsigned char byte = *ptr++ & 255;
            snprintf(string, sizeof(string), "%02X ", byte);
            strcat(buffer, string);
            if (n >= MAXBUF - 3) {
                break;
            }
        }
        if (count - 1) {
            // timeout
        }
        if (count) {
            logIT(LOG_INFO, "Received: %s", buffer);
        }

    }
    framer_closeDevice(fd);
    vcontrol_semrelease();
    fclose(cmdPtr);
    return 1;
}

void printHelp(int socketfd)
{
//      10        20        30        40        50        60        70        80
    char string[] = " \
close              Close connection to device\n \
commands           List all commands for the protocol listed in the XML file\n \
debug on|off       Toggle debug information\n \
detail <command>   Show detailed information about <command>\n \
device             The device set in the XML file\n \
protocol           Active protocol\n \
raw                Raw mode, commands WAIT,SEND,RECV,PAUSE terminated with END\n \
reload             Reload XML configuration\n \
unit on|off        Toggle conversion to given unit\n \
version            Show the version number\n \
quit               Close the session\n";
    Writen(socketfd, string, strlen(string));
}

int rawModus(int socketfd, char *device)
{
    // Here, we write all incoming commands in a temporary file, which is forwarded to readCmdFile
    char readBuf[MAXBUF];
    char string[256];

    // FIXME: I think we should be using some PID or such instead of "XXXXXX".
#ifdef __CYGWIN__
    char tmpfile[] = "vitotmp-XXXXXX";
#else
    char tmpfile[] = "/tmp/vitotmp-XXXXXX";
#endif

    FILE *filePtr;
    char result[MAXBUF];
    int resultLen;

    if (! mkstemp(tmpfile)) {
        // Another try
        if (! mkstemp(tmpfile)) {
            logIT1(LOG_ERR, "Error creating mkstemp");
            return 0;
        }
    }

    filePtr = fopen(tmpfile, "w+");
    if (! filePtr) {
        logIT(LOG_ERR, "Could not create temp file %s", tmpfile);
        return 0;
    }

    logIT(LOG_INFO, "Raw mode: Temp file: %s", tmpfile);

    while (Readline(socketfd, readBuf, sizeof(readBuf))) {
        // Here, we parse the particular commands
        if (strstr(readBuf, "END") == readBuf) {
            fclose(filePtr);
            resultLen = sizeof(result);
            readCmdFile(tmpfile, result, &resultLen, device);
            if (resultLen) {
                // Re received characters
                char buffer[MAXBUF];
                memset(buffer, 0, sizeof(buffer));
                char2hex(buffer, result, resultLen);
                snprintf(string, sizeof(string), "Result: %s\n", buffer);
                Writen(socketfd, string, strlen(string));
            }
            remove(tmpfile);
            return 1;
        }
        logIT(LOG_INFO, "Raw: Read: %s", readBuf);
        if (fputs(readBuf, filePtr) == EOF) {
            logIT1(LOG_ERR, "Error writing to temp file");
        } else {
            // debug stuff
        }
    }
    return 0; // is this correct?
}

int interactive(int socketfd, char *device)
{
    char readBuf[1000];
    char *readPtr;
    char bye[] = BYE;
    char string[256];
    commandPtr cPtr;
    commandPtr pcPtr;
    int fd = -1;
    short count = 0;
    short rcount = 0;
    short noUnit = 0;
    char recvBuf[MAXBUF];
    char pRecvBuf[MAXBUF];
    char sendBuf[MAXBUF];
    char cmd[MAXBUF];
    char para[MAXBUF];
    char *ptr;
    short sendLen;
    char buffer[MAXBUF];

    Writen(socketfd, PROMPT, strlen(PROMPT));
    memset(readBuf, 0, sizeof(readBuf));

    while ((rcount = Readline(socketfd, readBuf, sizeof(readBuf)))) {
        sendErrMsg(socketfd);
        // Remove control characters
        readPtr = readBuf + rcount;
        while (iscntrl(*readPtr)) {
            *readPtr-- = '\0';
        }
        logIT(LOG_INFO, "Command: %s", readBuf);

        // We separate the command and possible options at the first blank
        memset(cmd, 0, sizeof(cmd));
        memset(para, 0, sizeof(para));
        if ((ptr = strchr(readBuf, ' '))) {
            strncpy(cmd, readBuf, ptr - readBuf);
            strcpy(para, ptr + 1);
        } else {
            strcpy(cmd, readBuf);
        }

        // Here, the particular commands are parsed
        if (strstr(readBuf, "help") == readBuf) {
            printHelp(socketfd);
        } else if (strstr(readBuf, "quit") == readBuf) {
            Writen(socketfd, bye, strlen(bye));
            framer_closeDevice(fd);
            vcontrol_semrelease();
            return 1;
        } else if (strstr(readBuf, "debug on") == readBuf) {
            setDebugFD(socketfd);
        } else if (strstr(readBuf, "debug off") == readBuf) {
            setDebugFD(-1);
        } else if (strstr(readBuf, "unit off") == readBuf) {
            noUnit = 1;
        } else if (strstr(readBuf, "unit on") == readBuf) {
            noUnit = 0;
        } else if (strstr(readBuf, "reload") == readBuf) {
            if (reloadConfig()) {
                memset(string, 0, sizeof(string));
                snprintf(string, sizeof(string), "XML file %s reloaded\n", xmlfile);
                Writen(socketfd, string, strlen(string));
                // If we have a parent (daemon mode), it reveives a SIGHUP
                if (makeDaemon) {
                    kill(getppid(), SIGHUP);
                }
            } else {
                memset(string, 0, sizeof(string));
                snprintf(string, sizeof(string),
                         "Loading of XML file %s failed, using old configuration\n", xmlfile);
                Writen(socketfd, string, strlen(string));
            }
        } else if (strstr(readBuf, "raw") == readBuf) {
            rawModus(socketfd, device);
        } else if (strstr(readBuf, "close") == readBuf) {
            framer_closeDevice(fd);
            vcontrol_semrelease();
            snprintf(string, sizeof(string), "%s closed\n", device);
            Writen(socketfd, string, strlen(string));
            fd = -1;
        } else if (strstr(readBuf, "commands") == readBuf) {
            cPtr = cfgPtr->devPtr->cmdPtr;
            while (cPtr) {
                if (cPtr->addr) {
                    memset(string, 0, sizeof(string));
                    snprintf(string, sizeof(string), "%s: %s\n", cPtr->name, cPtr->description);
                    Writen(socketfd, string, strlen(string));
                }
                cPtr = cPtr->next;
            }
        } else if (strstr(readBuf, "protocol") == readBuf) {
            memset(string, 0, sizeof(string));
            snprintf(string, sizeof(string), "%s\n", cfgPtr->devPtr->protoPtr->name);
            Writen(socketfd, string, strlen(string));
        } else if (strstr(readBuf, "device") == readBuf) {
            memset(string, 0, sizeof(string));
            snprintf(string, sizeof(string), "%s (ID=%s) (Protocol=%s)\n", cfgPtr->devPtr->name,
                     cfgPtr->devPtr->id,
                     cfgPtr->devPtr->protoPtr->name);
            Writen(socketfd, string, strlen(string));
        } else if (strstr(readBuf, "version") == readBuf) {
            memset(string, 0, sizeof(string));
            snprintf(string, sizeof(string), "Version: %s\n", VERSION);
            Writen(socketfd, string, strlen(string));
        } else if ((cPtr = getCommandNode(cfgPtr->devPtr->cmdPtr, cmd)) && (cPtr->addr)) {
            // The command is defined in XML, so we take care of it ...
            memset(string, 0, sizeof(string));
            memset(recvBuf, 0, sizeof(recvBuf));
            memset(sendBuf, 0, sizeof(sendBuf));
            memset(pRecvBuf, 0, sizeof(pRecvBuf));

            // If unit off is set or no unit is defined, we pass the parameters in hex
            memset(sendBuf, 0, sizeof(sendBuf));
            if ((noUnit || !cPtr->unit) && *para) {
                if ((sendLen = string2chr(para, sendBuf, sizeof(sendBuf))) == -1) {
                    logIT(LOG_ERR, "No hex string: %s", para);
                    sendErrMsg(socketfd);
                    if (! Writen(socketfd, PROMPT, strlen(PROMPT))) {
                        sendErrMsg(socketfd);
                        framer_closeDevice(fd);
                        vcontrol_semrelease();
                        return 0;
                    }
                    continue;
                }
                // If sendLen > len of the command, we use len
                if (sendLen > cPtr->len) {
                    logIT(LOG_WARNING,
                          "Length of the hex string > send length of the command, sending only %d bytes", cPtr->len);
                    sendLen = cPtr->len;
                }
            } else if (*para) {
                // We copy the parameter, execByteCode itself takes care of it
                strcpy(sendBuf, para);
                sendLen = strlen(sendBuf);
            }
            if (iniFD) {
                fprintf(iniFD, ";%s\n", readBuf);
            }

            // We only open the device if we have something to do. But only if it's not open yet.
            if (fd < 0) {
                /* As one vclient call opens the link once, all is seen a transaction
                 * This may cause trouble for telnet sessions, as the whole session is
                 * one link activity, even more commands are given within.
                 * This is related to a accept/close on a server socket
                 */
                // everything on link is a transaction - all commands
                vcontrol_semget();

                if ((fd = framer_openDevice(device, cfgPtr->devPtr->protoPtr->id)) == -1) {
                    logIT(LOG_ERR, "Error opening %s", device);
                    sendErrMsg(socketfd);
                    framer_closeDevice(fd);
                    vcontrol_semrelease();
                    if (!Writen(socketfd, PROMPT, strlen(PROMPT))) {
                        sendErrMsg(socketfd);
                        return 0;
                    }
                    continue;
                }
            }

            // If there's a pre command, we execute this first
            if (cPtr->precmd && (pcPtr = getCommandNode(cfgPtr->devPtr->cmdPtr, cPtr->precmd))) {
                logIT(LOG_INFO, "Executing pre command %s", cPtr->precmd);

                if (execByteCode(pcPtr->cmpPtr, fd, pRecvBuf, sizeof(pRecvBuf), sendBuf, sendLen, 1, pcPtr->bit, pcPtr->retry, pRecvBuf, pcPtr->recvTimeout) == -1) {
                    logIT(LOG_ERR, "Error executing %s", readBuf);
                    sendErrMsg(socketfd);
                    break;
                } else {
                    memset(buffer, 0, sizeof(buffer));
                    char2hex(buffer, pRecvBuf, pcPtr->len);
                    logIT(LOG_INFO, "Result of pre command: %s", buffer);
                }
            }

            // We execute the bytecode:
            // -1: Error
            //  0: Preformatted string
            //  n: raw bytes
            count = execByteCode(cPtr->cmpPtr, fd, recvBuf, sizeof(recvBuf), sendBuf, sendLen, noUnit, cPtr->bit, cPtr->retry, pRecvBuf, cPtr->recvTimeout);

            if (count == -1) {
                logIT(LOG_ERR, "Error executing %s", readBuf);
                sendErrMsg(socketfd);
            } else if (*recvBuf && (count == 0)) {
                // Unit converted
                logIT1(LOG_INFO, recvBuf);
                snprintf(string, sizeof(string), "%s\n", recvBuf);
                Writen(socketfd, string, strlen(string));
            } else {
                int n;
                char *ptr;
                ptr = recvBuf;
                char buffer[MAXBUF];
                memset(buffer, 0, sizeof(buffer));
                for (n = 0; n < count; n++) {
                    // We received a character
                    memset(string, 0, sizeof(string));
                    unsigned char byte = *ptr++ & 255;
                    snprintf(string, sizeof(string), "%02X ", byte);
                    strcat(buffer, string);
                    if (n >= MAXBUF - 3) {
                        break;
                    }
                }
                if (count) {
                    snprintf(string, sizeof(string), "%s\n", buffer);
                    Writen(socketfd, string, strlen(string));
                    logIT(LOG_INFO, "Received: %s", buffer);
                }
            }
            if (iniFD) {
                fflush(iniFD);
            }
        } else if (strstr(readBuf, "detail") == readBuf) {
            readPtr = readBuf + strlen("detail");
            while (isspace(*readPtr)) {
                readPtr++;
            }
            // Is the command defined in the XML?
            if (readPtr && (cPtr = getCommandNode(cfgPtr->devPtr->cmdPtr, readPtr))) {
                memset(string, 0, sizeof(string));
                snprintf(string, sizeof(string), "%s: %s\n", cPtr->name, cPtr->send);
                Writen(socketfd, string, strlen(string));
                // Error String defined
                char buf[MAXBUF];
                memset(buf, 0, sizeof(buf));
                if (cPtr->errStr && char2hex(buf, cPtr->errStr, cPtr->len)) {
                    snprintf(string, sizeof(string), "\tError at (Hex): %s", buf);
                    Writen(socketfd, string, strlen(string));
                }
                // recvTimeout?
                if (cPtr->recvTimeout) {
                    snprintf(string, sizeof(string), "\tRECV Timeout: %d ms\n", cPtr->recvTimeout);
                    Writen(socketfd, string, strlen(string));
                }
                // Retry defined?
                if (cPtr->retry) {
                    snprintf(string, sizeof(string), "\tRetry: %d\n", cPtr->retry);
                    Writen(socketfd, string, strlen(string));
                }
                // Is Bit defined?
                if (cPtr->bit > 0) {
                    snprintf(string, sizeof(string), "\tBit (BP): %d\n", cPtr->bit);
                    Writen(socketfd, string, strlen(string));
                }
                // Pre command defined?
                if (cPtr->precmd) {
                    snprintf(string, sizeof(string), "\tPre command (P0-P9): %s\n", cPtr->precmd);
                    Writen(socketfd, string, strlen(string));
                }

                // If a unit has been given, we also output it
                compilePtr cmpPtr;
                cmpPtr = cPtr->cmpPtr;
                while (cmpPtr) {
                    if (cmpPtr && cmpPtr->uPtr) {
                        // Unit gefunden
                        char *gcalc;
                        char *scalc;
                        // We differentiate the calculation by get and setaddr
                        if (cmpPtr->uPtr->gCalc && *cmpPtr->uPtr->gCalc) {
                            gcalc = cmpPtr->uPtr->gCalc;
                        } else {
                            gcalc = cmpPtr->uPtr->gICalc;
                        }
                        if (cmpPtr->uPtr->sCalc && *cmpPtr->uPtr->sCalc) {
                            scalc = cmpPtr->uPtr->sCalc;
                        } else {
                            scalc = cmpPtr->uPtr->sICalc;
                        }

                        snprintf(string, sizeof(string),
                                 "\tUnit: %s (%s)\n\t  Type: %s\n\t  Get-Calc: %s\n\t  \
                                  Set-Calc: %s\n\t Einheit: %s\n",
                                 cmpPtr->uPtr->name, cmpPtr->uPtr->abbrev,
                                 cmpPtr->uPtr->type,
                                 gcalc,
                                 scalc,
                                 cmpPtr->uPtr->entity);
                        Writen(socketfd, string, strlen(string));
                        // If it's an enum, is the more?
                        if (cmpPtr->uPtr->ePtr) {
                            enumPtr ePtr;
                            ePtr = cmpPtr->uPtr->ePtr;
                            char dummy[20];
                            while (ePtr) {
                                memset(dummy, 0, sizeof(dummy));
                                if (!ePtr->bytes) {
                                    strcpy(dummy, "<default>");
                                } else {
                                    char2hex(dummy, ePtr->bytes, ePtr->len);
                                }
                                snprintf(string, sizeof(string), "\t  Enum Bytes: %s Text: %s\n",
                                         dummy, ePtr->text);
                                Writen(socketfd, string, strlen(string));
                                ePtr = ePtr->next;
                            }
                        }
                    }
                    cmpPtr = cmpPtr->next;
                }
            } else {
                memset(string, 0, sizeof(string));
                snprintf(string, sizeof(string), "ERR: command %s unknown\n", readPtr);
                Writen(socketfd, string, strlen(string));
            }
        } else if (*readBuf) {
            if (!Writen(socketfd, UNKNOWN, strlen(UNKNOWN))) {
                sendErrMsg(socketfd);
                framer_closeDevice(fd);
                vcontrol_semrelease();
                return 0;
            }
        }
        sendErrMsg(socketfd);
        if (!Writen(socketfd, PROMPT, strlen(PROMPT))) {
            sendErrMsg(socketfd);
            framer_closeDevice(fd);
            vcontrol_semrelease();
            return 0;
        }
        memset(readBuf, 0, sizeof(readBuf));
    }
    sendErrMsg(socketfd);
    framer_closeDevice(fd);
    vcontrol_semrelease();
    return 0;
}

static void sigPipeHandler(int signo)
{
    logIT1(LOG_ERR, "Received SIGPIPE");
    // FIXME: And we do nothing here? Why do we handle it then?
}

static void sigHupHandler(int signo)
{
    logIT1(LOG_NOTICE, "Received SIGHUP");
    reloadConfig();
}

char *pidFile = NULL;

static void sigTermHandler(int signo)
{
    if (signo == SIGTERM) {
        logIT1(LOG_NOTICE, "Received SIGTERM");
    } else if (signo == SIGINT) {
        logIT1(LOG_NOTICE, "Received SIGINT");
    } else if (signo == SIGQUIT) {
        logIT1(LOG_NOTICE, "Received SIGQUIT");
    } else {
        logIT(LOG_NOTICE, "Received signal %d", signo);
    }
    vcontrol_semfree();
    if (pidFile) {
        unlink(pidFile);
    }
    exit(1);
}

// Here we go
int main(int argc, char *argv[])
{
    // Parse the command line options
    char *device = NULL;
    char *cmdfile = NULL;
    char *logfile = NULL;
    char *username = NULL;
    char *groupname = NULL;
    static int useSyslog = 0;
    static int debug = 0;
    static int verbose = 0;
    int tcpport = 0;
    static int simuOut = 0;
    int opt;

    while (1) {
        static struct option long_options[] = {
            {"commandfile", required_argument, 0,            'c'},
            {"device",      required_argument, 0,            'd'},
            {"debug",       no_argument,       &debug,       1  },
            {"vsim",        no_argument,       &simuOut,     1  },
            {"logfile",     required_argument, 0,            'l'},
            {"pidfile",     required_argument, 0,            'P'},
            {"username",    required_argument, 0,            'U'},
            {"groupname",   required_argument, 0,            'G'},
            {"nodaemon",    no_argument,       &makeDaemon,  0  },
            {"port",        required_argument, 0,            'p'},
            {"syslog",      no_argument,       &useSyslog,   1  },
            {"xmlfile",     required_argument, 0,            'x'},
            {"verbose",     no_argument,       &verbose,     1  },
            {"Version",     no_argument,       0,            0  },
            {"inet4",       no_argument,       &inetversion, 4  },
            {"inet6",       no_argument,       &inetversion, 6  },
            {"help",        no_argument,       0,            0  },
            {0,             0,                 0,            0  }
        };

        // getopt_long stores the option index here.
        int option_index = 0;
        opt = getopt_long (argc, argv, "c:d:gil:P:U:G:np:sx:vV46",
                           long_options, &option_index);

        // Detect the end of the options.
        if (opt == -1) {
            break;
        }

        switch (opt) {
        case 0:
            // If this option sets a flag, we do nothing for now
            if (long_options[option_index].flag != 0) {
                break;
            }
            if (verbose) {
                printf("option %s", long_options[option_index].name);
                if (optarg) {
                    printf(" with arg %s", optarg);
                }
                printf("\n");
            }
            if (strcmp("help", long_options[option_index].name) == 0) {
                usage();
            }
            break;
        case '4':
            inetversion = 4;
            break;
        case '6':
            inetversion = 6;
            break;
        case 'c':
            cmdfile = optarg;
            break;
        case 'd':
            device = optarg;
            break;
        case 'g':
            debug = 1;
            break;
        case 'i':
            simuOut = 1;
            break;
        case 'l':
            logfile = optarg;
            break;
        case 'P':
            pidFile = optarg;
            break;
        case 'U':
            username = optarg;
            break;
        case 'G':
            groupname = optarg;
            break;
        case 'n':
            makeDaemon = 0;
            break;
        case 'p':
            tcpport = atoi(optarg);
            break;
        case 's':
            useSyslog = 1;
            break;
        case 'v':
            puts ("option -v\n");
            verbose = 1;
            break;
        case 'V':
            printf("vcontrold version %s\n", VERSION);
            exit(1);
            break;
        case 'x':
            xmlfile = optarg;
            break;
        case '?':
            // getopt_long already printed an error message.
            usage();
            break;
        default:
            abort();
        }
    }

    initLog(useSyslog, logfile, debug);

    logIT(LOG_NOTICE, "started vcontrold version %s", VERSION);

    if (!parseXMLFile(xmlfile)) {
        fprintf(stderr, "Error loading %s, terminating\n", xmlfile);
        exit(1);
    }

    // The global variables cfgPtr and protoPtr have been filled
    if (cfgPtr) {
        if (! tcpport) {
            tcpport = cfgPtr->port;
        }
        if (! device) {
            device = cfgPtr->tty;
        }
        if (! logfile) {
            logfile = cfgPtr->logfile;
        }
        if (! pidFile) {
            pidFile = cfgPtr->pidfile;
        }
        if (! username) {
            username = cfgPtr->username;
        }
        if (! groupname) {
            groupname = cfgPtr->groupname;
        }
        if (! useSyslog) {
            useSyslog = cfgPtr->syslog;
        }
        if (! debug) {
            debug = cfgPtr->debug;
        }
    }

    if (!initLog(useSyslog, logfile, debug)) {
        exit(1);
    }

    if (signal(SIGHUP, sigHupHandler) == SIG_ERR) {
        logIT1(LOG_ERR, "Error handling SIGHUP");
        exit(1);
    }

    if (signal(SIGQUIT, sigTermHandler) == SIG_ERR) {
        logIT(LOG_ERR, "Error handling SIGQUIT: %s", strerror(errno));
        exit(1);
    }
    if (signal(SIGINT, sigTermHandler) == SIG_ERR) {
        logIT(LOG_ERR, "Error handling SIGINT: %s", strerror(errno));
        exit(1);
    }
    if (signal(SIGTERM, sigTermHandler) == SIG_ERR) {
        logIT(LOG_ERR, "Error handling SIGTERM: %s", strerror(errno));
        exit(1);
    }

    // If -i has been given, we log the commands in Simulator INI format
    if (simuOut) {
        char file[100];
        snprintf(file, sizeof(file), INIOUTFILE, cfgPtr->devID);
        if (! (iniFD = fopen(file, "w"))) {
            logIT(LOG_ERR, "Could not create simulator INI file %s", file);
        }
        fprintf(iniFD, "[DATA]\n");
    }

    // The macros are replaced and the strings to send are converted to bytecode
    compileCommand(devPtr, uPtr);

    int fd = 0;
    char result[MAXBUF];
    int resultLen = sizeof(result);
    int sid;
    int pidFD = 0;
    char str[10];

    if (tcpport) {
        if (makeDaemon) {
            int pid;
            // Some siganl handling
            if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {
                logIT1(LOG_ERR, "Error handling SIGCHLD");
            }

            pid = fork();
            if (pid < 0) {
                logIT(LOG_ERR, "fork failed (%d)", pid);
                exit(1);
            }
            if (pid > 0) {
                exit(0); // Parent is terminated, child runs on
            }

            // From here, only the child is running

            // Close FD
            close(STDIN_FILENO);
            close(STDOUT_FILENO);
            close(STDERR_FILENO);
            umask(0);

            sid = setsid();
            if (sid < 0) {
                logIT1(LOG_ERR, "setsid failed");
                exit(1);
            }
            if (chdir("/") < 0) {
                logIT1(LOG_ERR, "chdir / failed");
                exit(1);
            }

            if (pidFile) {
                pidFD = open(pidFile, O_RDWR | O_CREAT, 0600);
                if (pidFD == -1) {
                    logIT(LOG_ERR, "Could not open PID lock file %s, exiting", pidFile);
                    exit(1);
                }
                if (lockf(pidFD, F_TLOCK, 0) == -1) {
                    logIT(LOG_ERR, "Could not lock PID lock file %s, exiting", pidFile);
                    exit(1);
                }

                sprintf(str, "%d\n", getpid());
                write(pidFD, str, strlen(str));
            }
        }

        int sockfd = -1;
        int listenfd = openSocket(tcpport);

        // Drop privileges after binding
        if (0 == getuid()) {
            struct passwd *pw;
            struct group *grp;
            struct stat stb;

            if (username == NULL) {
                username = strdup("nobody");
            }
            pw = getpwnam(username);
            if (pw == NULL) {
                int errsv = errno;
                logIT(LOG_ERR, "Error while dropping privileges: calling getpwnam(\"%s\") failed with errno %d", username, errsv);
                exit(1);
            }
            if (groupname == NULL) {
                groupname = strdup("dialout");
            }
            grp = getgrnam(groupname);
            if (grp == NULL) {
                int errsv = errno;
                logIT(LOG_ERR, "Error while dropping privileges: calling getgrnam(\"%s\") failed with errno %d", groupname, errsv);
                exit(1);
            }

            // Make logfile accessible to the anticipated user/group
            if (stat(logfile, &stb) == 0) {
                chmod(logfile, stb.st_mode | S_IRGRP | S_IWGRP);
                chown(logfile, pw->pw_uid, grp->gr_gid);
            }
            // Make lock file accessible to the anticipated user/group
            if (stat(tmpfilename, &stb) == 0) {
                chmod(tmpfilename, stb.st_mode | S_IRGRP | S_IWGRP);
                chown(tmpfilename, pw->pw_uid, grp->gr_gid);
            }

            if (setgroups(0, NULL) != 0) {
                int errsv = errno;
                logIT(LOG_ERR, "Error while dropping privileges: calling setgroups(0, NULL) failed with errno %d", errsv);
                exit(1);
            }

            if (setgid(grp->gr_gid) != 0) {
                int errsv = errno;
                logIT(LOG_ERR, "Error while dropping privileges: calling setgid(%d) failed with errno %d", grp->gr_gid, errsv);
                exit(1);
            }

            if (setuid(pw->pw_uid) != 0) {
                int errsv = errno;
                logIT(LOG_ERR, "Error while dropping privileges: calling setuid(%d) failed with errno %d", pw->pw_uid, errsv);
                exit(1);
            }

            logIT(LOG_INFO, "Dropped privileges, now running with effective user ID %d, group ID %d", geteuid(), getegid());
        } else {
            if (username || groupname) {
                logIT1(LOG_INFO, "Not started as root, username/groupname settings ignored");
            }
        }

        vcontrol_seminit();

        while (1) {
            sockfd = listenToSocket(listenfd, makeDaemon);
            if (signal(SIGPIPE, sigPipeHandler) == SIG_ERR) {
                logIT1(LOG_ERR, "Signal error");
                exit(1);
            }
            if (sockfd >= 0) {
                // Socket returned fd, the rest is done interactively
                interactive(sockfd, device);
                closeSocket(sockfd);
                setDebugFD(-1);
                if (makeDaemon) {
                    logIT(LOG_INFO, "Child process with PID %d terminated", getpid());
                    exit(0); // The child bids boodbye
                }
            } else {
                logIT1(LOG_ERR, "Error connecting");
            }
        }
    } else {
        vcontrol_seminit();
    }

    if (*cmdfile) {
        readCmdFile(cmdfile, result, &resultLen, device);
    }

    vcontrol_semfree();

    close(fd);
    if (pidFile) {
        close(pidFD);
        unlink(pidFile);
    };
    logIT1(LOG_LOCAL0, "vcontrold terminated");

    return 0;
}
0707010000003A000081A4000028240000003200000001607BF4480000031A000000000000000000000000000000000000003800000000vcontrold-v0.98.10+git20210418.977e6f5/src/version.h.in/* Copyright (c) 2017 Tobias Leupold <tobias.leupold@gmx.de>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef VERSION_H
#define VERSION_H

#define VERSION "@VERSION@"

#endif // VERSION_H
0707010000003B000081A4000028240000003200000001607BF4480000132E000000000000000000000000000000000000003200000000vcontrold-v0.98.10+git20210418.977e6f5/src/vsim.c/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#define _GNU_SOURCE

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <syslog.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <time.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <sys/time.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <stdarg.h>
#include "socket.h"
#include "vclient.h"

#define SERVERPORT 6578
int makeDaemon = 0;
int inetversion = 0;
int readCmdFile(char *filename, char *result, int *resultLen, char *device );
int interactive(int socketfd, char *device);
void printHelp(int socketfd);
int rawModus (int socketfd, char *device);
static void sigPipeHandler(int signo);

void logIT (int class, char *string, ...)
{
    va_list arguments;
    char *print_buffer;

    va_start(arguments, string);
    vasprintf(&print_buffer, string, arguments);
    va_end(arguments);
    printf("%s\n", print_buffer);
    free(print_buffer);
}

static void sigPipeHandler(int signo)
{
    logIT(LOG_ERR, "Received SIGPIPE");
}

static void dump(char *buf, int len, char *txt)
{
    int i = 0;
    printf("%s:\n", txt);
    for (i = 0; i < len; i++) {
        printf(" %02x", (unsigned char)buf[i]);
    }
    printf("\n");
}

typedef struct {
    int cmdlen;
    char cmd[20];
    int rsplen;
    char rsp[20];
} ctable;

int cmdc = 9;
ctable cmds[] = {
    { 1, { 0x04 }, 1, { 0x05 } },
    { 3, { 0x16, 0x00, 0x00 }, 1, { 0x06 } },
    { 5, { 0x01, 0xf7, 0xcb, 0x70, 01 }, 2, {0x30, 0x00 } },
    {
        8, { 0x41, 0x05, 0x00, 0x01, 0x55, 0x25, 0x02, 0x82 },
        11, { 0x06, 0x41, 0x07, 0x01, 0x01, 0x55, 0x25, 0x02, 0x07, 0x01, 0x8D }
    },
    {
        8, { 0x41, 0x05, 0x00, 0x01, 0x08, 0x00, 0x02, 0x10 },
        11, { 0x06, 0x41, 0x07, 0x01, 0x01, 0x08, 0x00, 0x02, 0x07, 0x01, 0x1B }
    },
    {
        8, { 0x41, 0x05, 0x00, 0x01, 0x21, 0x10, 0x08, 0x3F },
        16, { 0x06, 0x41, 0x0D, 0x01, 0x01, 0x21, 0x10, 0x08, 0x28, 0xB0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
    },
    {
        5, { 0x01, 0xf7, 0x08, 0x00, 0x02 },
        2, { 0x07, 0x01 }
    },
    {
        8, { 0x41, 0x05, 0x00, 0x01, 0x08, 0x04, 0x02, 0x14 },
        11, { 0x06, 0x41, 0x07, 0x01, 0x01, 0x08, 0x04, 0x02, 0x00, 0x00, 0x17 }
    },
};

char input[100];
char inpidx = 0;

static void handle(int fd)
{
    char buf[1] = "\0";
    ssize_t len;

    while ( 1 ) {
        len = readn(fd, buf, 1);
        if  (len < 0) {
            perror("read eror\n");
            exit(-1);
        } else if (len == 0) {
            printf("eof read\n");
            return;
        } else {
            int i = 0;
            int j = 0;
            while ( i < len ) {
                input[inpidx] = buf[i];
                dump(&input[inpidx], 1, "received char:");
                inpidx++;
                for ( j = 0 ; j < cmdc; j++) {
                    if ( cmds[j].cmd[0] == input[0] ) {
                        if (( cmds[j].cmdlen == inpidx) && (!memcmp(cmds[j].cmd, input, inpidx))) {
                            dump(input, inpidx, "received cmd:");
                            dump(cmds[j].rsp, cmds[j].rsplen, "answer:");
                            if (writen(fd, cmds[j].rsp, cmds[j].rsplen) != cmds[j].rsplen) {
                                printf("not completely written\n");
                            }
                            inpidx = 0;
                        }
                    }
                }
                i++;
            }
        }
    }
}

int main(int argc, char *argv[])
{
    int sockfd = -1;
    int listenfd = -1;

    listenfd = openSocket(SERVERPORT);
    while (1) {
        sockfd = listenToSocket(listenfd, makeDaemon);
        if (signal(SIGPIPE, sigPipeHandler) == SIG_ERR) {
            logIT(LOG_ERR, "Signal error");
            exit(1);
        }
        if (sockfd >= 0) {
            // Socket returned fd, the rest is done interactively
            logIT(LOG_INFO, "\nvcontrold connected");
            handle(sockfd);
            logIT(LOG_INFO, "\nvcontrold disconnected");
            closeSocket(sockfd);
        } else {
            logIT(LOG_ERR, "Error during connection setup");
        }
    }
}
0707010000003C000081A4000028240000003200000001607BF4480000BBD6000000000000000000000000000000000000003700000000vcontrold-v0.98.10+git20210418.977e6f5/src/xmlconfig.c/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// Routines for reading XML files

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <libxml/parser.h>
#include <libxml/xinclude.h>
#include <arpa/inet.h>

#include "xmlconfig.h"
#include "common.h"
#include "parser.h"

#if defined(__FreeBSD__)
#include <netinet/in.h>
#endif

// Declarations
protocolPtr newProtocolNode(protocolPtr ptr);
char *getPropertyNode(xmlAttrPtr cur, xmlChar *name);
enumPtr newEnumNode(enumPtr ptr);
void removeProtocolList(protocolPtr ptr);
void removeUnitList(unitPtr ptr);
void removeMacroList(macroPtr ptr);
void removeCommandList(commandPtr ptr);
void removeDeviceList(devicePtr ptr);
void removeIcmdList(icmdPtr ptr);
void removeEnumList(enumPtr ptr);
void freeAllLists();

// Globale variables
protocolPtr protoPtr = NULL;
unitPtr uPtr = NULL;
devicePtr devPtr = NULL;
configPtr cfgPtr = NULL;
commandPtr cmdPtr = NULL;

protocolPtr newProtocolNode(protocolPtr ptr)
{
    protocolPtr nptr;
    if (ptr && ptr->next) {
        return newProtocolNode(ptr->next);
    }

    nptr = calloc(1, sizeof(Protocol));
    if (! nptr) {
        fprintf(stderr, "malloc failed\n");
        exit(1);
    }

    if (ptr) {
        ptr->next = nptr;
    }
    nptr->next = NULL;
    nptr->icPtr = NULL;
    nptr->id = 0;

    return nptr;
}

protocolPtr getProtocolNode(protocolPtr ptr, const char *name)
{
    if (! ptr) {
        return NULL;
    }

    if (strcmp(ptr->name, name) != 0) {
        return getProtocolNode(ptr->next, name);
    }

    return ptr;
}

void removeProtocolList(protocolPtr ptr)
{
    if (ptr && ptr->next) {
        removeProtocolList(ptr->next);
    }
    if (ptr) {
        removeMacroList(ptr->mPtr);
        removeIcmdList(ptr->icPtr);
        free(ptr->name);
        free(ptr);
    }
}

unitPtr newUnitNode(unitPtr ptr)
{
    unitPtr nptr;
    if (ptr && ptr->next) {
        return newUnitNode(ptr->next);
    }

    nptr = calloc(1, sizeof(Unit));
    if (! nptr) {
        fprintf(stderr, "malloc failed\n");
        exit(1);
    }

    if (ptr) {
        ptr->next = nptr;
    }

    nptr->next = NULL;
    return nptr;
}

unitPtr getUnitNode(unitPtr ptr, const char *name)
{
    if (! ptr) {
        return NULL;
    }

    if (ptr->abbrev && (strcmp(ptr->abbrev, name) != 0)) {
        return getUnitNode(ptr->next, name);
    }

    return ptr;
}

void removeUnitList(unitPtr ptr)
{
    if (ptr && ptr->next) {
        removeUnitList(ptr->next);
    }

    if (ptr) {
        removeEnumList(ptr->ePtr);
        free(ptr->name);
        free(ptr->abbrev);
        free(ptr->gCalc);
        free(ptr->sCalc);
        free(ptr->gICalc);
        free(ptr->sICalc);
        free(ptr->entity);
        free(ptr->type);
        free(ptr);
    }
}

macroPtr newMacroNode(macroPtr ptr)
{
    macroPtr nptr;
    if (ptr && ptr->next) {
        return newMacroNode(ptr->next);
    }

    nptr = calloc(1, sizeof(Macro));
    if (! nptr) {
        fprintf(stderr, "malloc failed\n");
        exit(1);
    }

    if (ptr) {
        ptr->next = nptr;
    }

    nptr->next = NULL;
    return nptr;
}

macroPtr getMacroNode(macroPtr ptr, const char *name)
{
    if (! ptr) {
        return NULL;
    }

    if (ptr->name && strcmp(ptr->name, name) != 0) {
        //if (*name && !(strstr(ptr->name,name)==ptr->name))
        return getMacroNode(ptr->next, name);
    }

    return ptr;
}

void removeMacroList(macroPtr ptr)
{
    if (ptr && ptr->next) {
        removeMacroList(ptr->next);
    }

    if (ptr) {
        free(ptr->name);
        free(ptr->command);
        free(ptr);
    }
}

commandPtr newCommandNode(commandPtr ptr)
{
    commandPtr nptr;
    if (ptr && ptr->next) {
        return newCommandNode(ptr->next);
    }

    nptr = calloc(1, sizeof(Command));
    if (! nptr) {
        fprintf(stderr, "malloc failed\n");
        exit(1);
    }

    if (ptr) {
        ptr->next = nptr;
    }

    nptr->next = NULL;
    nptr->cmpPtr = NULL;
    nptr->bit = -1;

    return nptr;
}

void addCommandNode(commandPtr ptr, commandPtr nptr)
{
    if (! ptr) {
        return;
    } else if (ptr->next) {
        addCommandNode(ptr->next, nptr);
    } else {
        ptr->next = nptr;
    }
}

commandPtr getCommandNode(commandPtr ptr, const char *name)
{
    if (! ptr) {
        return NULL;
    }

    if (ptr->name && name && (strcmp(ptr->name, name) != 0)) {
        return getCommandNode(ptr->next, name);
    }

    return ptr;
}

void removeCommandList(commandPtr ptr)
{
    if (ptr && ptr->next) {
        removeCommandList(ptr->next);
    }

    if (ptr) {
        removeCompileList(ptr->cmpPtr);
        free(ptr->send);
        if (ptr->nodeType > 0) {
            // Has been read from the XML file
            if (ptr->addr) {
                free(ptr->addr);
            }
            if (ptr->precmd) {
                free(ptr->precmd);
            }
            if (ptr->unit) {
                free(ptr->unit);
            }
            if (ptr->pcmd) {
                free(ptr->pcmd);
            }
            if (ptr->errStr) {
                free(ptr->errStr);
            }
            if (ptr->nodeType == 1) {
                // Originalknoten
                if (ptr->name) {
                    free(ptr->name);
                }
                if (ptr->description) {
                    free(ptr->description);
                }
            }
        }
        free(ptr);
    }
}

devicePtr newDeviceNode(devicePtr ptr)
{
    devicePtr nptr;
    if (ptr && ptr->next) {
        return newDeviceNode(ptr->next);
    }

    nptr = calloc(1, sizeof(Device));
    if (! nptr) {
        fprintf(stderr, "malloc failed\n");
        exit(1);
    }

    if (ptr) {
        ptr->next = nptr;
    }

    nptr->next = NULL;
    return nptr;
}

devicePtr getDeviceNode(devicePtr ptr, char *id)
{
    if (! ptr) {
        return NULL;
    }

    if (strcmp(ptr->id, id) != 0) {
        return getDeviceNode(ptr->next, id);
    }

    return ptr;
}

void removeDeviceList(devicePtr ptr)
{
    if (ptr && ptr->next) {
        removeDeviceList(ptr->next);
    }
    if (ptr) {
        removeCommandList(ptr->cmdPtr);
        free(ptr->name);
        free(ptr->id);
        free(ptr);
    }
}

icmdPtr newIcmdNode(icmdPtr ptr)
{
    icmdPtr nptr;
    if (ptr && ptr->next) {
        return newIcmdNode(ptr->next);
    }

    nptr = calloc(1, sizeof(iCmd));
    if (! nptr) {
        fprintf(stderr, "malloc failed\n");
        exit(1);
    }

    if (ptr) {
        ptr->next = nptr;
    }

    nptr->next = NULL;
    return nptr;
}

icmdPtr getIcmdNode(icmdPtr ptr, const char *name)
{
    if (! ptr) {
        return NULL;
    }

    if (ptr->name && (strcmp(ptr->name, name) != 0)) {
        return getIcmdNode(ptr->next, name);
    }

    return ptr;
}

void removeIcmdList(icmdPtr ptr)
{
    if (ptr && ptr->next) {
        removeIcmdList(ptr->next);
    }
    if (ptr) {
        free(ptr->name);
        free(ptr->send);
        free(ptr);
    }
}

enumPtr newEnumNode(enumPtr ptr)
{
    enumPtr nptr;
    if (ptr && ptr->next) {
        return newEnumNode(ptr->next);
    }

    nptr = calloc(1, sizeof(Enumerate));
    if (! nptr) {
        fprintf(stderr, "malloc failed\n");
        exit(1);
    }

    if (ptr) {
        ptr->next = nptr;
    }

    return nptr;
}

enumPtr getEnumNode(enumPtr ptr, char *search, int len)
{
    if (! ptr) {
        return NULL;
    }

    if ((len > 0) && ptr->bytes && (memcmp(ptr->bytes, search, len) == 0)) {
        // len given, we search for bytes
        return ptr;
    } else if (!len && ptr->text && (strcmp(ptr->text, search) == 0)) {
        // len == 0 --> string comparison
        return ptr;
    } else if ((len < 0) && (ptr->bytes == NULL)) {
        // len == -1 --> default value
        return ptr;
    } else {
        return getEnumNode(ptr->next, search, len);
    }
}

void removeEnumList(enumPtr ptr)
{
    if (ptr && ptr->next) {
        removeEnumList(ptr->next);
    }

    if (ptr) {
        free(ptr->text);
        free(ptr->bytes);
        free(ptr);
    }
}

void printNode(xmlNodePtr ptr)
{
    static int blanks = 0;
    int n;

    if (! ptr) {
        return;
    }

    for (n = 0; n <= blanks; n++) {
        printf(" ");
    }

    if ((ptr->type == XML_ELEMENT_NODE) || (ptr->type == XML_TEXT_NODE)) {
        printf("(%d) Node::Name=%s Type:%d Content=%s\n",
               ptr->line, ptr->name, ptr->type, ptr->content);
    } else {
        printf("Node::Name=%s\n", ptr->name);
    }

    if ((ptr->type == XML_ELEMENT_NODE) && ptr->properties) {
        blanks++;
        printNode((xmlNodePtr) ptr->properties);
        blanks--;
    }

    if (ptr->children) {
        blanks++;
        printNode(ptr->children);
        blanks--;
    }

    if ( ptr->next) {
        printNode(ptr->next);
    }
}

char *getTextNode(xmlNodePtr cur)
{
    if ((cur->children) && (cur->children->type == XML_TEXT_NODE)) {
        return (char *)cur->children->content;
    } else {
        return NULL;
    }
}

char *getPropertyNode(xmlAttrPtr cur, xmlChar *name)
{
    if ((cur) && (cur->type == XML_ATTRIBUTE_NODE) && strstr((char *)cur->name, (char *)name)) {
        return (char *)getTextNode((xmlNodePtr) cur);
    } else if (cur && cur->next) {
        return (char *)getPropertyNode(cur->next, name);
    } else {
        return NULL;
    }
}

void nullIT(char **ptr)
{
    *ptr = calloc(1, sizeof(char));
    **ptr = '\0';
}

configPtr parseConfig(xmlNodePtr cur)
{
    int serialFound = 0;
    int netFound = 0;
    int logFound = 0;
    configPtr cfgPtr;
    char *chrPtr;
    xmlNodePtr prevPtr;
    //char string[256];
    char ip[16];

    cfgPtr = calloc(1, sizeof(Config));
    cfgPtr->port = 0;
    cfgPtr->syslog = 0;
    cfgPtr->debug = 0;

    while (cur) {
        logIT(LOG_INFO, "CONFIG:(%d) Node::Name=%s Type:%d Content=%s",
              cur->line, cur->name, cur->type, cur->content);
        if (strstr((char *)cur->name, "serial"))  {
            serialFound = 1;
            prevPtr = cur;
            cur = cur->children;
        } else if (strstr((char *)cur->name, "net")) {
            netFound = 1;
            prevPtr = cur;
            cur = cur->children;
        } else if (strstr((char *)cur->name, "logging"))  {
            logFound = 1;
            prevPtr = cur;
            cur = cur->children;
        } else if (strstr((char *)cur->name, "pidfile")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                cfgPtr->pidfile = calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(cfgPtr->pidfile, chrPtr);
            } else {
                nullIT(&cfgPtr->pidfile);
            }

            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else if (strstr((char *)cur->name, "username")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                cfgPtr->username= calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(cfgPtr->username, chrPtr);
            } else {
                nullIT(&cfgPtr->username);
            }

            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else if (strstr((char *)cur->name, "groupname")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                cfgPtr->groupname= calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(cfgPtr->groupname, chrPtr);
            } else {
                nullIT(&cfgPtr->groupname);
            }

            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else if (strstr((char *)cur->name, "device"))  {
            chrPtr = getPropertyNode(cur->properties, (xmlChar *)"ID");
            logIT(LOG_INFO, "     Device ID=%s", chrPtr);
            if (chrPtr) {
                cfgPtr->devID = calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(cfgPtr->devID, chrPtr);
            } else {
                nullIT(&cfgPtr->devID);
            }
            cur = cur->next;
        } else if (serialFound && strstr((char *)cur->name, "tty")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                cfgPtr->tty = calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(cfgPtr->tty, chrPtr);
            } else {
                nullIT(&cfgPtr->tty);
            }

            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else if (netFound && strstr((char *)cur->name, "port"))  {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                cfgPtr->port = atoi(chrPtr);
            }
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else if (logFound && strstr((char *)cur->name, "file")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                cfgPtr->logfile = calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(cfgPtr->logfile, chrPtr);
            } else {
                nullIT(&cfgPtr->logfile);
            }
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else if (logFound && strstr((char *)cur->name, "syslog")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            ((*chrPtr == 'y') || (*chrPtr == '1')) ? (cfgPtr->syslog = 1) : (cfgPtr->syslog = 0);
            (cur->next &&
            (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else if (logFound && strstr((char *)cur->name, "debug")) {
            chrPtr = getTextNode(cur);
            ((*chrPtr == 'y') || (*chrPtr == '1')) ? (cfgPtr->debug = 1) : (cfgPtr->debug = 0);
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else {
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        }
    }

    return cfgPtr;
}

unitPtr parseUnit(xmlNodePtr cur)
{
    unitPtr uPtr;
    unitPtr uStartPtr = NULL;
    char *unit;
    char *chrPtr;
    int unitFound = 0;
    xmlNodePtr prevPtr;
    char string[256];
    enumPtr ePtr;

    while (cur) {
        logIT(LOG_INFO, "UNIT: (%d) Node::Name=%s Type:%d Content=%s",
              cur->line, cur->name, cur->type, cur->content);

        if (cur->type == XML_TEXT_NODE) {
            cur = cur->next;
            continue;
        }

        if (strstr((char *)cur->name, "unit")) {
            unit = getPropertyNode(cur->properties, (xmlChar *)"name");
            if (unit) {
                // read new unit
                logIT(LOG_INFO, "New unit: %s", unit);
                uPtr = newUnitNode(uStartPtr);
                if (! uStartPtr) {
                    uStartPtr = uPtr;
                }
                uPtr->name = calloc(strlen(unit) + 1, sizeof(char));
                strcpy(uPtr->name, unit);
                unitFound = 1;
                prevPtr = cur;
                cur = cur->children;
                continue;
            }
        } else if (unitFound && strstr((char *)cur->name, "enum")) {
            chrPtr = getPropertyNode(cur->properties, (xmlChar *)"text");
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s (text)",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                ePtr = newEnumNode(uPtr->ePtr);
                if (! uPtr->ePtr) {
                    uPtr->ePtr = ePtr;
                }
                ePtr->text = calloc(strlen(chrPtr) + 1, sizeof(char));
                strncpy(ePtr->text, chrPtr, strlen(chrPtr));
                chrPtr = getPropertyNode(cur->properties, (xmlChar *)"bytes");
                if (chrPtr) {
                    logIT(LOG_INFO, "          (%d) Node::Name=%s Type:%d Content=%s (bytes)",
                          cur->line, cur->name, cur->type, chrPtr);
                    memset(string, 0, sizeof(string));
                    ePtr->len = string2chr(chrPtr, string, sizeof(string));
                    ePtr->bytes = calloc(ePtr->len, sizeof(char));
                    memcpy(ePtr->bytes, string, ePtr->len);
                }
            } else {
                logIT(LOG_ERR, "Property node without text=");
                return NULL;
            }

            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);

        } else if (unitFound && strstr((char *)cur->name, "abbrev")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                uPtr->abbrev = calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(uPtr->abbrev, chrPtr);
            } else {
                nullIT(&uPtr->abbrev);
            }
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else if (unitFound && (strcmp((char *)cur->name, "calc") == 0)) {
            chrPtr = getPropertyNode(cur->properties, (xmlChar *)"get");
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s (get)",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                uPtr->gCalc = calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(uPtr->gCalc, chrPtr);
            } else {
                nullIT(&uPtr->gCalc);
            }
            chrPtr = getPropertyNode(cur->properties, (xmlChar *)"set");
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s (set)",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                uPtr->sCalc = calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(uPtr->sCalc, chrPtr);
            } else {
                nullIT(&uPtr->sCalc);
            }
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else if (unitFound && (strcmp((char *)cur->name, "icalc") == 0)) {
            chrPtr = getPropertyNode(cur->properties, (xmlChar *)"get");
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s (get)",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                uPtr->gICalc = calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(uPtr->gICalc, chrPtr);
            } else {
                nullIT(&uPtr->gICalc);
            }
            chrPtr = getPropertyNode(cur->properties, (xmlChar *)"set");
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s (set)",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                uPtr->sICalc = calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(uPtr->sICalc, chrPtr);
            } else {
                nullIT(&uPtr->sICalc);
            }
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else if (unitFound && strstr((char *)cur->name, "type")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                uPtr->type = calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(uPtr->type, chrPtr);
            } else {
                nullIT(&uPtr->type);
            }
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else if (unitFound && strstr((char *)cur->name, "entity")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                uPtr->entity = calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(uPtr->entity, chrPtr);
            } else {
                nullIT(&uPtr->entity);
            }
            //(cur->next && cur->next->next) ? (cur=cur->next) : (cur=prevPtr->next);
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else {
            logIT(LOG_ERR, "Error parsing unit");
            return NULL;
        }
    }

    return uStartPtr;
}

macroPtr parseMacro(xmlNodePtr cur)
{
    macroPtr mPtr;
    macroPtr mStartPtr = NULL;
    char *macro;
    char *chrPtr;
    int macroFound = 0;
    xmlNodePtr prevPtr;

    while (cur) {
        logIT(LOG_INFO, "MACRO: (%d) Node::Name=%s Type:%d Content=%s",
              cur->line, cur->name, cur->type, cur->content);

        if (cur->type == XML_TEXT_NODE) {
            cur = cur->next;
            continue;
        }

        if (strstr((char *)cur->name, "macro")) {
            macro = getPropertyNode(cur->properties, (xmlChar *)"name");
            if (macro) {
                // Read new macro
                logIT(LOG_INFO, "New macro: %s", macro);
                mPtr = newMacroNode(mStartPtr);
                if (! mStartPtr) {
                    mStartPtr = mPtr;
                }
                mPtr->name = calloc(strlen(macro) + 1, sizeof(char));
                strcpy(mPtr->name, macro);
                macroFound = 1;
                prevPtr = cur;
                cur = cur->children;
                continue;
            }
        } else if (macroFound && strstr((char *)cur->name, "command")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                mPtr->command = calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(mPtr->command, chrPtr);
            } else {
                nullIT(&mPtr->command);
            }
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else {
            logIT(LOG_INFO, "Error parsing macro");
            return NULL;
        }
    }

    return mStartPtr;
}

commandPtr parseCommand(xmlNodePtr cur, commandPtr cPtr, devicePtr dePtr)
{
    commandPtr cStartPtr = NULL;
    devicePtr dPtr;
    char *command;
    char *protocmd;
    char *chrPtr;
    xmlNodePtr prevPtr;
    char string[256];    // TODO: get rid of that one
    char *id;
    int commandFound;
    commandPtr ncPtr;
    short count;

    // We look for a recursive call (then, cPtr is set)
    if (! cPtr) {
        commandFound = 0;
    } else {
        commandFound = 1;
        prevPtr = NULL;
    }

    while (cur) {
        logIT(LOG_INFO, "COMMAND: (%d) Node::Name=%s Type:%d Content=%s",
              cur->line, cur->name, cur->type, cur->content);

        if (xmlIsBlankNode(cur)) {
            cur = cur->next;
            continue;
        }

        if (strcmp((char *)cur->name, "command") == 0) {
            command = getPropertyNode(cur->properties, (xmlChar *)"name");
            protocmd = getPropertyNode(cur->properties, (xmlChar *)"protocmd");
            if (command) {
                // Read new command
                logIT(LOG_INFO, "New command: %s", command);
                cPtr = newCommandNode(cStartPtr);
                if (! cStartPtr) {
                    cStartPtr = cPtr;
                }
                cPtr->nodeType = 1; // No copy, we need this for deleting
                if (command) {
                    cPtr->name = calloc(strlen(command) + 1, sizeof(char));
                    strcpy(cPtr->name, command);
                } else {
                    nullIT(&cPtr->name);
                }
                if (protocmd) {
                    cPtr->pcmd = calloc(strlen(protocmd) + 1, sizeof(char));
                    strcpy(cPtr->pcmd, protocmd);
                } else {
                    nullIT(&cPtr->pcmd);
                }
                commandFound = 1;
                prevPtr = cur;
                cur = cur->children;
                continue;
            }
        } else if (commandFound && strstr((char *)cur->name, "device")) {
            id = getPropertyNode(cur->properties, (xmlChar *)"ID");
            protocmd = getPropertyNode(cur->properties, (xmlChar *)"protocmd");
            if (id) {
                // Read new device below command
                logIT(LOG_INFO, "    New device command: %s", id);
                // Search device from the list
                if (! (dPtr = getDeviceNode(dePtr, id))) {
                    logIT(LOG_ERR, "Device %s is not defined (%d)", id, cur->line);
                    return NULL;
                }
                // We take the description from the command entry
                strncpy(string, cPtr->description, sizeof(string));
                // Now again recursively parseCommand for all children
                ncPtr = newCommandNode(NULL);
                parseCommand(cur->children, ncPtr, dePtr);
                if (! dPtr->cmdPtr) {
                    dPtr->cmdPtr = ncPtr;
                } else {
                    addCommandNode(dPtr->cmdPtr, ncPtr);
                }
                // Refer to description in new accounts
                ncPtr->description = cPtr->description;
                ncPtr->name = cPtr->name;
                // If no unit has been given, we copy it
                if (! ncPtr->unit && cPtr->unit) {
                    ncPtr->unit = calloc(strlen(cPtr->unit) + 1, sizeof(char));
                    strcpy(ncPtr->unit, cPtr->unit);
                }
                // Same for the protocol command
                if (protocmd) {
                    ncPtr->pcmd = calloc(strlen(protocmd) + 1, sizeof(char));
                    strcpy(ncPtr->pcmd, protocmd);
                } else {
                    ncPtr->pcmd = calloc(strlen(cPtr->pcmd) + 1, sizeof(char));
                    strcpy(ncPtr->pcmd, cPtr->pcmd);
                }
                ncPtr->nodeType = 2; // 2 == decription, name has been copied
                if (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next)) {
                    cur = cur->next;
                } else if (prevPtr) {
                    cur = prevPtr->next;
                } else {
                    cur = NULL;
                }
            }
        } else if (commandFound && strstr((char *)cur->name, "addr")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                cPtr->addr = calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(cPtr->addr, chrPtr);
            } else {
                nullIT(&cPtr->addr);
            }
            if (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next)) {
                cur = cur->next;
            } else if (prevPtr) {
                cur = prevPtr->next;
            } else {
                cur = NULL;
            }
        } else if (commandFound && strstr((char *)cur->name, "error")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                memset(string, 0, sizeof(string));
                if ((count = string2chr(chrPtr, string, sizeof(string)))) {
                    cPtr->errStr = calloc(count, sizeof(char));
                    memcpy(cPtr->errStr, string, count);
                }
            } else {
                nullIT(&cPtr->errStr);
            }
            if (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next)) {
                cur = cur->next;
            } else if (prevPtr) {
                cur = prevPtr->next;
            } else {
                cur = NULL;
            }
        } else if (commandFound && strstr((char *)cur->name, "unit")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                cPtr->unit = calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(cPtr->unit, chrPtr);
            } else {
                nullIT(&cPtr->unit);
            }
            if (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next)) {
                cur = cur->next;
            } else if (prevPtr) {
                cur = prevPtr->next;
            } else {
                cur = NULL;
            }
        } else if (commandFound && (strcmp((char *)cur->name, "precommand") == 0)) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                cPtr->precmd = calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(cPtr->precmd, chrPtr);
            } else {
                nullIT(&cPtr->precmd);
            }
            if (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next)) {
                cur = cur->next;
            } else if (prevPtr) {
                cur = prevPtr->next;
            } else {
                cur = NULL;
            }
        } else if (commandFound && strstr((char *)cur->name, "description")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                cPtr->description = calloc(strlen(chrPtr) + 1, sizeof(char));
                strcpy(cPtr->description, chrPtr);
            } else {
                nullIT(&cPtr->description);
            }
            if (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next)) {
                cur = cur->next;
            } else if (prevPtr) {
                cur = prevPtr->next;
            } else {
                cur = NULL;
            }
        } else if (commandFound && strstr((char *)cur->name, "len")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                cPtr->len = atoi(chrPtr);
            }
            if (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next)) {
                cur = cur->next;
            } else if (prevPtr) {
                cur = prevPtr->next;
            } else {
                cur = NULL;
            }
        } else if (commandFound && strstr((char *)cur->name, "bit")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                cPtr->bit = atoi(chrPtr);
            }
            if (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next)) {
                cur = cur->next;
            } else if (prevPtr) {
                cur = prevPtr->next;
            } else {
                cur = NULL;
            }
        } else {
            logIT(LOG_ERR, "Error parsing command");
            return NULL;
        }
    }

    return cStartPtr;
}

icmdPtr parseICmd(xmlNodePtr cur)
{
    icmdPtr icPtr;
    icmdPtr icStartPtr = NULL;
    char *command;
    char *chrPtr;
    int commandFound = 0;
    xmlNodePtr prevPtr;

    while (cur) {
        logIT(LOG_INFO, "ICMD: (%d) Node::Name=%s Type:%d Content=%s",
              cur->line, cur->name, cur->type, cur->content);
        if (xmlIsBlankNode(cur))  {
            //if (cur->type == XML_TEXT_NODE) {
            cur = cur->next;
            continue;
        }
        if (strstr((char *)cur->name, "command")) {
            command = getPropertyNode(cur->properties, (xmlChar *)"name");
            if (command) {
                // Read new command
                logIT(LOG_INFO, "New iCommand: %s", command);
                icPtr = newIcmdNode(icStartPtr);
                if (! icStartPtr) {
                    icStartPtr = icPtr;
                }
                icPtr->name = calloc(strlen(command) + 1, sizeof(char));
                strcpy(icPtr->name, command);
                commandFound = 1;
                prevPtr = cur;
                cur = cur->children;
                continue;
            }
        } else if (commandFound && strstr((char *)cur->name, "send")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                icPtr->send = calloc(strlen(chrPtr) + 2, sizeof(char));
                strcpy(icPtr->send, chrPtr);
            } else {
                nullIT(&icPtr->send);
            }
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else if (commandFound && strstr((char *)cur->name, "retry")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                icPtr->retry = atoi(chrPtr);
            }
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else if (commandFound && strstr((char *)cur->name, "recvTimeout")) {
            chrPtr = getTextNode(cur);
            logIT(LOG_INFO, "   (%d) Node::Name=%s Type:%d Content=%s",
                  cur->line, cur->name, cur->type, chrPtr);
            if (chrPtr) {
                icPtr->recvTimeout = atoi(chrPtr);
            }
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else {
            logIT(LOG_ERR, "Error parsing command");
            return NULL;
        }
    }

    return icStartPtr;
}

devicePtr parseDevice(xmlNodePtr cur, protocolPtr pPtr)
{
    devicePtr dPtr;
    devicePtr dStartPtr = NULL;
    char *proto;
    char *name;
    char *id;
    xmlNodePtr prevPtr;

    while (cur) {
        logIT(LOG_INFO, "DEVICE: (%d) Node::Name=%s Type:%d Content=%s",
              cur->line, cur->name, cur->type, cur->content);

        if (cur->type == XML_TEXT_NODE) {
            cur = cur->next;
            continue;
        }

        if (strstr((char *)cur->name, "device")) {
            name = getPropertyNode(cur->properties, (xmlChar *)"name");
            id = getPropertyNode(cur->properties, (xmlChar *)"ID");
            proto = getPropertyNode(cur->properties, (xmlChar *)"protocol");
            if (proto) {
                // Read new protocol
                logIT(LOG_INFO, "    Neues Device: name=%s ID=%s proto=%s", name, id, proto);
                dPtr = newDeviceNode(dStartPtr);
                if (! dStartPtr) {
                    dStartPtr = dPtr;
                } // Remember anchor
                if (name) {
                    dPtr->name = calloc(strlen(name) + 1, sizeof(char));
                    strcpy(dPtr->name, name);
                } else {
                    nullIT(&dPtr->name);
                }

                if (id) {
                    dPtr->id = calloc(strlen(id) + 1, sizeof(char));
                    strcpy(dPtr->id, id);
                } else {
                    nullIT(&dPtr->id);
                }

                if (! (dPtr->protoPtr = getProtocolNode(pPtr, proto))) {
                    logIT(LOG_ERR, "Protocol %s not defined", proto);
                    return NULL;
                }
            } else {
                logIT(LOG_ERR, "Error parsing device");
                return NULL;
            }

            prevPtr = cur;
            cur = cur->next;

        } else {
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        }
    }

    return dStartPtr;
}

protocolPtr parseProtocol(xmlNodePtr cur)
{
    int protoFound = 0;
    protocolPtr protoPtr;
    protocolPtr protoStartPtr = NULL;
    macroPtr mPtr;
    icmdPtr icPtr;
    char *proto;
    char *chrPtr;
    xmlNodePtr prevPtr;

    while (cur) {
        logIT(LOG_INFO, "PROT: (%d) Node::Name=%s Type:%d Content=%s",
              cur->line, cur->name, cur->type, cur->content);

        if (cur->type == XML_TEXT_NODE) {
            cur = cur->next;
            continue;
        }

        if (strstr((char *)cur->name, "protocol")) {
            proto = getPropertyNode(cur->properties, (xmlChar *)"name");
            if (proto) {
                // Read new protocol
                logIT(LOG_INFO, "New protocol %s", proto);
                protoPtr = newProtocolNode(protoStartPtr);
                if (! protoStartPtr) {
                    protoStartPtr = protoPtr;
                } // Remember anchor
                if (proto) {
                    protoPtr->name = calloc(strlen(proto) + 1, sizeof(char));
                    strcpy(protoPtr->name, proto);
                } else {
                    nullIT(&protoPtr->name);
                }
            } else {
                logIT(LOG_ERR, "Error parsing protocol");
                return NULL;
            }
            protoFound = 1;
            prevPtr = cur;
            cur = cur->children;
        } else if (protoFound && strstr((char *)cur->name, "pid")) {
            chrPtr = getTextNode(cur);
            protoPtr->id = hex2chr(chrPtr);
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else if (protoFound && strstr((const char *)cur->name, "macros")) {
            mPtr = parseMacro(cur->children);
            if (mPtr) {
                protoPtr->mPtr = mPtr;
            }
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else if (protoFound && strstr((char *)cur->name, "commands")) {
            icPtr = parseICmd(cur->children);
            if (icPtr) {
                protoPtr->icPtr = icPtr;
            }
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        } else {
            (cur->next && (! (cur->next->type == XML_TEXT_NODE) || cur->next->next))
                ? (cur = cur->next) : (cur = prevPtr->next);
        }
    }

    return protoStartPtr;
}

void removeComments(xmlNodePtr node)
{
    while (node) {
        //printf("type:%d name=%s\n",node->type, node->name);
        if (node->children) {
            // if the node has children, process the children
            removeComments(node->children);
        }
        if (node->type == XML_COMMENT_NODE) {
            // if the node is a comment?
            //printf("found comment\n");
            if (node->next) {
                removeComments(node->next);
            }
            xmlUnlinkNode(node); // unlink
            xmlFreeNode(node);   // and free the node
        }
        node = node->next;
    }
}

int parseXMLFile(char *filename)
{
    xmlDocPtr doc;
    xmlNodePtr cur, curStart;
    xmlNodePtr prevPtr;
    xmlNsPtr ns;
    //char string[256];
    devicePtr dPtr;
    commandPtr cPtr, ncPtr;
    protocolPtr TprotoPtr = NULL;
    unitPtr TuPtr = NULL;
    devicePtr TdevPtr = NULL;
    commandPtr TcmdPtr = NULL;
    configPtr TcfgPtr = NULL;

    xmlKeepBlanksDefault(0);
    doc = xmlParseFile(filename);
    if (doc == NULL) {
        return 0;
    }
    curStart = xmlDocGetRootElement(doc);
    cur = curStart;
    if (cur == NULL) {
        logIT(LOG_ERR, "empty document\n");
        xmlFreeDoc(doc);
        return 0;
    }
    ns = xmlSearchNsByHref(doc, cur, (const xmlChar *) "http://www.openv.de/vcontrol");
    if (ns == NULL) {
        logIT(LOG_ERR, "document of the wrong type, vcontrol Namespace not found");
        xmlFreeDoc(doc);
        return 0;
    }
    if (xmlStrcmp(cur->name, (const xmlChar *) "V-Control")) {
        logIT(LOG_ERR, "document of the wrong type, root node != V-Control");
        xmlFreeDoc(doc);
        return 0;
    }
    // Run XInclude
    short xc = 0;
    if ((xc = xmlXIncludeProcessFlags(doc, XML_PARSE_XINCLUDE | XML_PARSE_NOXINCNODE)) == 0) {
        logIT(LOG_WARNING, "Didn't perform XInclude");
    } else if (xc < 0) {
        logIT(LOG_ERR, "Error during XInclude");
        return 0;
    } else {
        logIT(LOG_INFO, "%d XInclude performed", xc);
    }

    removeComments(cur); // now the xml tree is complete --> remove all comments

    int unixFound = 0;
    int protocolsFound = 0;
    cur = cur->children;

    while (cur) {
        logIT(LOG_INFO, "XML: (%d) Node::Name=%s Type:%d Content=%s",
              cur->line, cur->name, cur->type, cur->content);

        if (xmlIsBlankNode(cur)) {
            cur = cur->next;
            continue;
        }

        if (strstr((char *)cur->name, "unix"))  {
            if (unixFound) {
                // We must not reach here, second pass
                logIT(LOG_ERR, "Error in XML config");
                return 0;
            }
            prevPtr = cur;
            unixFound = 1;
            cur = cur->children; // On Unix, config follows. Thus children.
            continue;
        }

        if (strstr((char *)cur->name, "extern")) {
            cur = cur->children;
            if (cur && strstr((char *)cur->name, "vito"))  {
                prevPtr = cur;
                cur = cur->children; // The xinclude stuff can be found at <extern><vito>
                continue;
            } else {
                cur = prevPtr->next;
            }
        } else if (strstr((char *)cur->name, "protocols")) {
            if (protocolsFound) {
                // We must not reach here, second pass
                logIT(LOG_ERR, "Error in XML config");
                return 0;
            }
            protocolsFound = 1;
            if (! (TprotoPtr = parseProtocol(cur->children))) {
                return 0;
            }
            cur = cur->next; // Same level as Unix, proceed
            continue;
        } else if (strstr((char *)cur->name, "units")) {
            if (! (TuPtr = parseUnit(cur->children))) {
                return 0;
            }
            cur = cur->next; // Same level as Unix, proceed
            continue;
        } else if (strstr((char *)cur->name, "commands")) {
            if (! (TcmdPtr = parseCommand(cur->children, NULL, TdevPtr))) {
                return 0;
            }
            //(cur->next && cur->next->next) ? (cur=cur->next) : (cur=prevPtr->next);
            (cur->next) ? (cur = cur->next) : (cur = prevPtr->next);
            continue;
        } else if (strstr((char *)cur->name, "devices")) {
            if (! (TdevPtr = parseDevice(cur->children, TprotoPtr))) {
                return 0;
            }
            (cur->next) ? (cur = cur->next) : (cur = prevPtr->next);
            continue;
        } else if (strstr((char *)cur->name, "config")) {
            if (! (TcfgPtr = parseConfig(cur->children))) {
                return 0;
            }
            (cur->next && cur->next->next) ? (cur = cur->next) : (cur = prevPtr->next);
        } else {
            cur = cur->next;
        }
    }

    // For all commands that have default definitions,
    // we roam all devices and add the particular commands.
    cPtr = TcmdPtr;
    while (cPtr) {
        dPtr = TdevPtr;
        while (dPtr) {
            if (! getCommandNode(dPtr->cmdPtr, cPtr->name)) {
                // We don't know this one and copy the commands
                logIT(LOG_INFO, "Copying command %s to device %s", cPtr->name, dPtr->id);
                ncPtr = newCommandNode(dPtr->cmdPtr);
                if (! dPtr->cmdPtr) {
                    dPtr->cmdPtr = ncPtr;
                }
                ncPtr->name = cPtr->name;
                ncPtr->pcmd = cPtr->pcmd;
                ncPtr->addr = cPtr->addr;
                ncPtr->unit = cPtr->unit;
                ncPtr->bit = cPtr->bit;
                ncPtr->errStr = cPtr->errStr;
                ncPtr->precmd = cPtr->precmd;
                ncPtr->description = cPtr->description;
                ncPtr->len = cPtr->len;
            }
            dPtr = dPtr->next;
        }
        cPtr = cPtr->next;
    }

    // We search the default device
    if (! (TcfgPtr->devPtr = getDeviceNode(TdevPtr, TcfgPtr->devID))) {
        logIT(LOG_ERR, "Device %s is not defined\n", TcfgPtr->devID);
        return 0;
    }

    // If we reach here, the loading has been successful.
    // Now we free the old lists and allocate the new ones.

    // If we're called repetitive (SIGHUP), everything should be freed.
    freeAllLists();

    protoPtr = TprotoPtr;
    uPtr = TuPtr;
    devPtr = TdevPtr;
    cmdPtr = TcmdPtr;
    cfgPtr = TcfgPtr;

    xmlFreeDoc(doc);
    return 1;
}

void freeAllLists()
{
    removeProtocolList(protoPtr);
    removeUnitList(uPtr);
    removeDeviceList(devPtr);
    removeCommandList(cmdPtr);
    protoPtr = NULL;
    uPtr = NULL;
    devPtr = NULL;
    cmdPtr = NULL;
    if (cfgPtr) {
        free(cfgPtr->tty);
        free(cfgPtr->logfile);
        free(cfgPtr->devID);
        free(cfgPtr);
        cfgPtr = NULL;
    }
}
0707010000003D000081A4000028240000003200000001607BF44800000B85000000000000000000000000000000000000003700000000vcontrold-v0.98.10+git20210418.977e6f5/src/xmlconfig.h/*  Copyright 2007-2017 the original vcontrold development team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef XMLCONFIG_H
#define XMLCONFIG_H

#include <arpa/inet.h>

typedef struct config *configPtr;
typedef struct protocol *protocolPtr;
typedef struct unit *unitPtr;
typedef struct macro *macroPtr;
typedef struct command *commandPtr;
typedef struct compile *compilePtr;
typedef struct device *devicePtr;
typedef struct icmd *icmdPtr;
typedef struct allow *allowPtr;
typedef struct enumerate *enumPtr;

int parseXMLFile(char *filename);
macroPtr getMacroNode(macroPtr ptr, const char *name);
unitPtr getUnitNode(unitPtr ptr, const char *name);
commandPtr getCommandNode(commandPtr ptr, const char *name);
enumPtr getEnumNode(enumPtr prt, char *search, int len);
icmdPtr getIcmdNode(icmdPtr ptr, const char *name);

struct compile {
    int token;
    char *send;
    int len;
    unitPtr uPtr;
    char *errStr;
    compilePtr next;
} Compile;

struct config {
    char *tty;
    int port;
    char *logfile;
    char *pidfile;
    char *username;
    char *groupname;
    char *devID;
    devicePtr devPtr;
    int syslog;
    int debug;
} Config;

struct protocol {
    char *name;
    char id;
    macroPtr mPtr;
    icmdPtr icPtr;
    protocolPtr next;
} Protocol;

struct device {
    char *name;
    char *id;
    commandPtr cmdPtr;
    protocolPtr protoPtr;
    devicePtr next;
} Device;

struct unit {
    char *name;
    char *abbrev;
    char *gCalc;
    char *sCalc;
    char *gICalc;
    char *sICalc;
    char *entity;
    char *type;
    enumPtr ePtr;
    unitPtr next;
} Unit;

struct macro {
    char *name;
    char *command;
    macroPtr next;
} Macro;

struct command {
    char *name;
    char *pcmd;
    char *send;
    char *addr;
    char *unit;
    char *errStr;
    char *precmd;
    unsigned char len;
    int retry;
    unsigned short recvTimeout;
    char bit;
    char nodeType;
    // 0: everything copied
    // 1: everything orig
    // 2: only address, unit len orig
    compilePtr cmpPtr;
    char *description;
    commandPtr next;
} Command;

struct icmd {
    char *name;
    char *send;
    unsigned char retry;
    unsigned short recvTimeout;
    icmdPtr next;
} iCmd;

struct enumerate {
    char *bytes;
    int len;
    char *text;
    enumPtr next;
} Enumerate;

#endif // XMLCONFIG_H
0707010000003E000041ED000028240000003200000004607BF44800000000000000000000000000000000000000000000002B00000000vcontrold-v0.98.10+git20210418.977e6f5/xml0707010000003F000041ED000028240000003200000002607BF44800000000000000000000000000000000000000000000002F00000000vcontrold-v0.98.10+git20210418.977e6f5/xml/30007070100000040000081A4000028240000003200000001607BF44800004263000000000000000000000000000000000000003D00000000vcontrold-v0.98.10+git20210418.977e6f5/xml/300/vcontrold.xml<?xml version="1.0"?>
<V-Control xmlns:vcontrol="http://www.openv.de/vcontrol">
  <unix>
    <config>
      <!-- These are the default settings vcontrold drops privileges to when
           started as root. Be sure to have the respective user and group. If
           you use a serial device, be sure to have read and write permissions
           on the corresponding device file for the user/group -->
      <username>nobody</username>
      <groupname>dialout</groupname>
      <serial>
        <tty>/dev/ttySAC1</tty>
      </serial>
      <net>
        <port>3002</port>
      </net>
      <logging>
        <file>vcontrold.log</file>
        <syslog>n</syslog>
        <debug>n</debug>
      </logging>
      <device ID="20CB"/>
    </config>
  </unix>
  <units>
    <unit name="Temperatur">
      <abbrev>UT</abbrev>
      <calc get="V/10" set="V*10"/>
      <type>short</type>
      <entity>Grad Celsius</entity>
    </unit>
    <unit name="Temperatur100">
      <abbrev>UTH</abbrev>
      <calc get="V/100" set="V*100"/>
      <type>short</type>
      <entity>Grad Celsius</entity>
    </unit>
    <unit name="Neigung">
      <abbrev>UN</abbrev>
      <calc get="V/10" set="V*10"/>
      <type>short</type>
      <entity/>
    </unit>
    <unit name="Temperatur 1Byte">
      <abbrev>UT1</abbrev>
      <calc get="V/2" set="V*2"/>
      <type>char</type>
      <entity>Grad Celsius</entity>
    </unit>
    <unit name="Temperatur 1Byte unsigned">
      <abbrev>UT1U</abbrev>
      <calc get="V/2" set="V*2"/>
      <type>uchar</type>
      <entity>Grad Celsius</entity>
    </unit>
    <unit name="Temperatur 1Byte ganzzahlig">
      <abbrev>UTI</abbrev>
      <calc get="V" set="V"/>
      <type>uchar</type>
      <entity>Grad Celsius</entity>
    </unit>
    <unit name="Status">
      <abbrev>ST</abbrev>
      <calc get="V" set="V"/>
      <type>char</type>
      <entity/>
    </unit>
    <unit name="Counter">
      <abbrev>CO</abbrev>
      <calc get="V" set="V"/>
      <type>int</type>
      <entity/>
    </unit>
    <unit name="Volumenstrom">
      <abbrev>VS</abbrev>
      <calc get="V" set="V"/>
      <type>ushort</type>
      <entity>l/h</entity>
    </unit>
    <unit name="Counter liter">
      <abbrev>COL</abbrev>
      <calc get="V/1000" set="V*1000"/>
      <type>int</type>
      <entity/>
    </unit>
    <unit name="Prozent">
      <abbrev>PR</abbrev>
      <calc get="V/2" set="V*2"/>
      <type>short</type>
      <entity>%</entity>
    </unit>
    <unit name="Prozent 1 Byte ganzzahlig">
      <abbrev>PR1</abbrev>
      <calc get="V" set="V"/>
      <type>uchar</type>
      <entity>%</entity>
    </unit>
    <unit name="Prozent zweites Byte ganzzahlig (Pumpe)">
      <abbrev>PR2</abbrev>
      <calc get="B1" set="B1"/>
      <type>uchar</type>
      <entity>%</entity>
    </unit>
    <unit name="Prozent erstes Byte">
      <abbrev>PR3</abbrev>
      <calc get="V/2" set="V*2"/>
      <type>uchar</type>
      <entity>%</entity>
    </unit>
    <unit name="CounterS">
      <abbrev>CS</abbrev>
      <calc get="V/3600" set="V*3600"/>
      <type>uint</type>
      <entity>Stunden</entity>
    </unit>
    <unit name="CycleTime">
      <abbrev>CT</abbrev>
      <type>cycletime</type>
    </unit>
    <unit name="ReturnStatus">
      <abbrev>RT</abbrev>
      <type>enum</type>
      <enum bytes="00" text="0"/>
      <enum bytes="01" text="1"/>
      <!-- At least for device 20CB the heating circuit pump returns
           status 03 when it's on and the heating runs in in night mode -->
      <enum bytes='03' text='2'/>
      <enum text="NOT OK"/>
    </unit>
    <unit name="BetriebsArt">
      <abbrev>BA</abbrev>
      <type>enum</type>
      <enum bytes="00" text="WW"/>
      <enum bytes="01" text="RED"/>
      <enum bytes="02" text="NORM"/>
      <enum bytes="04" text="H+WW FS"/>
      <enum bytes="03" text="H+WW"/>
      <enum bytes="05" text="ABSCHALT"/>
      <enum text="UNKNOWN"/>
    </unit>
    <unit name="SetReturnStatus">
      <abbrev>SR</abbrev>
      <type>enum</type>
      <enum bytes="00" text="OK"/>
      <enum bytes="05" text="SYNC (NOT OK)"/>
      <enum text="NOT OK"/>
    </unit>
    <unit name="SystemTime">
      <abbrev>TI</abbrev>
      <type>systime</type>
    </unit>
    <unit name="ErrorState">
      <abbrev>ES</abbrev>
      <type>errstate</type>
      <enum bytes="00" text="Regelbetrieb (kein Fehler)"/>
      <enum bytes="0F" text="Wartung (fuer Reset Codieradresse 24 auf 0 stellen)"/>
      <enum bytes="10" text="Kurzschluss Aussentemperatursensor"/>
      <enum bytes="18" text="Unterbrechung Aussentemperatursensor"/>
      <enum bytes="20" text="Kurzschluss Vorlauftemperatursensor"/>
      <enum bytes="21" text="Kurzschluss Ruecklauftemperatursensor"/>
      <enum bytes="28" text="Unterbrechung Aussentemperatursensor"/>
      <enum bytes="29" text="Unterbrechung Ruecklauftemperatursensor"/>
      <enum bytes="30" text="Kurzschluss Kesseltemperatursensor"/>
      <enum bytes="38" text="Unterbrechung Kesseltemperatursensor"/>
      <enum bytes="40" text="Kurzschluss Vorlauftemperatursensor M2"/>
      <enum bytes="42" text="Unterbrechung Vorlauftemperatursensor M2"/>
      <enum bytes="50" text="Kurzschluss Speichertemperatursensor"/>
      <enum bytes="58" text="Unterbrechung Speichertemperatursensor"/>
      <enum bytes="92" text="Solar: Kurzschluss Kollektortemperatursensor"/>
      <enum bytes="93" text="Solar: Kurzschluss Sensor S3"/>
      <enum bytes="94" text="Solar: Kurzschluss Speichertemperatursensor"/>
      <enum bytes="9A" text="Solar: Unterbrechung Kollektortemperatursensor"/>
      <enum bytes="9B" text="Solar: Unterbrechung Sensor S3"/>
      <enum bytes="9C" text="Solar: Unterbrechung Speichertemperatursensor"/>
      <enum bytes='9E' text='Solar: Zu geringer/kein Volumenstrom oder Temperaturwächter ausgelöst'/>
      <enum bytes="9F" text="Solar: Fehlermeldung Solarteil (siehe Solarregler)"/>
      <enum bytes="A7" text="Bedienteil defekt"/>
      <enum bytes="B0" text="Kurzschluss Abgastemperatursensor"/>
      <enum bytes="B1" text="Kommunikationsfehler Bedieneinheit"/>
      <enum bytes="B4" text="Interner Fehler (Elektronik)"/>
      <enum bytes="B5" text="Interner Fehler (Elektronik)"/>
      <enum bytes="B6" text="Ungueltige Hardwarekennung (Elektronik)"/>
      <enum bytes="B7" text="Interner Fehler (Kesselkodierstecker)"/>
      <enum bytes="B8" text="Unterbrechung Abgastemperatursensor"/>
      <enum bytes="B9" text="Interner Fehler (Dateneingabe wiederholen)"/>
      <enum bytes="BA" text="Kommunikationsfehler Erweiterungssatz fuer Mischerkreis M2"/>
      <enum bytes="BC" text="Kommunikationsfehler Fernbedienung Vitorol, Heizkreis M1"/>
      <enum bytes="BD" text="Kommunikationsfehler Fernbedienung Vitorol, Heizkreis M2"/>
      <enum bytes="BE" text="Falsche Codierung Fernbedienung Vitorol"/>
      <enum bytes="C1" text="Externe Sicherheitseinrichtung (Kessel kuehlt aus)"/>
      <enum bytes="C2" text="Kommunikationsfehler Solarregelung"/>
      <enum bytes="C5" text="Kommunikationsfehler drehzahlgeregelte Heizkreispumpe, Heizkreis M1"/>
      <enum bytes="C6" text="Kommunikationsfehler drehzahlgeregelte Heizkreispumpe, Heizkreis M2"/>
      <enum bytes="C7" text="Falsche Codierung der Heizkreispumpe"/>
      <enum bytes="C9" text="Stoermeldeeingang am Schaltmodul-V aktiv"/>
      <enum bytes="CD" text="Kommunikationsfehler Vitocom 100 (KM-BUS)"/>
      <enum bytes="CE" text="Kommunikationsfehler Schaltmodul-V"/>
      <enum bytes="CF" text="Kommunikationsfehler LON Modul"/>
      <enum bytes="D1" text="Brennerstoerung"/>
      <enum bytes="D4" text="Sicherheitstemperaturbegrenzer hat ausgeloest oder Stoermeldemodul nicht richtig gesteckt"/>
      <enum bytes="DA" text="Kurzschluss Raumtemperatursensor, Heizkreis M1"/>
      <enum bytes="DB" text="Kurzschluss Raumtemperatursensor, Heizkreis M2"/>
      <enum bytes="DD" text="Unterbrechung Raumtemperatursensor, Heizkreis M1"/>
      <enum bytes="DE" text="Unterbrechung Raumtemperatursensor, Heizkreis M2"/>
      <enum bytes="E4" text="Fehler Versorgungsspannung"/>
      <enum bytes="E5" text="Interner Fehler (Ionisationselektrode)"/>
      <enum bytes="E6" text="Abgas- / Zuluftsystem verstopft"/>
      <enum bytes="F0" text="Interner Fehler (Regelung tauschen)"/>
      <enum bytes="F1" text="Abgastemperaturbegrenzer ausgeloest"/>
      <enum bytes="F2" text="Temperaturbegrenzer ausgeloest"/>
      <enum bytes="F3" text="Flammensigal beim Brennerstart bereits vorhanden"/>
      <enum bytes="F4" text="Flammensigal nicht vorhanden"/>
      <enum bytes="F7" text="Differenzdrucksensor defekt"/>
      <enum bytes="F8" text="Brennstoffventil schliesst zu spaet"/>
      <enum bytes="F9" text="Geblaesedrehzahl beim Brennerstart zu niedrig"/>
      <enum bytes="FA" text="Geblaesestillstand nicht erreicht"/>
      <enum bytes="FD" text="Fehler Gasfeuerungsautomat"/>
      <enum bytes="FE" text="Starkes Stoerfeld (EMV) in der Naehe oder Elektronik defekt"/>
      <enum bytes="FF" text="Starkes Stoerfeld (EMV) in der Naehe oder interner Fehler"/>
      <enum text="UNKNOWN"/>
    </unit>
    <unit name="DeviceType">
      <abbrev>DT</abbrev>
      <type>enum</type>
      <enum bytes="20 53 01 2B" text="V200WB2 ID=2053 Protokoll:GWG_VBEM"/>
      <enum bytes="20 98" text="V200KW2 ID=2098 Protokoll:KW"/>
      <enum bytes="20 CB 03 4A 00 00 01 0A" text="VScotHO1 ID=20CB Protokoll:KW,300"/>
      <enum bytes="20 94" text="V200KW1 ID=2094 Protokoll:KW"/>
      <enum text="UNKNOWN"/>
    </unit>
    <unit name="Sachnummer">
      <abbrev>SN</abbrev>
      <type>uint</type>
      <icalc get="((((((((((((B0-48)*10)+(B1-48))*10)+(B2-48))*10)+(B3-48))*10)+(B4-48))*10)+(B5-48))*10)+B6-48"/>
    </unit>
    <unit name="Brenner Stunden">
      <abbrev>BH</abbrev>
      <type>uchar</type>
      <calc get="(B1 * 100)+B0"/>
      <entity>h</entity>
    </unit>
    <unit name="ErrorStateGWG">
      <abbrev>ESG</abbrev>
      <type>enum</type>
      <enum bytes="00" text="Keine Stoerung"/>
      <enum bytes="02" text="Fehler Sicherheitskette"/>
      <enum bytes="04" text="Brennerstoerung 04"/>
      <enum bytes="05" text="Brennerstoerung 05"/>
      <enum bytes="07" text="Brennerstoerung 07"/>
      <enum bytes="08" text="Brennerstoerung 09"/>
      <enum bytes="08" text="Brennerstoerung 09"/>
      <enum bytes="0A" text="Brennerstoerung 10"/>
      <enum text="UNKNOWN"/>
    </unit>
    <unit name="Bitstatus">
      <abbrev>BST</abbrev>
      <type>uchar</type>
      <icalc get="(B0 &amp; (0x01&lt;&lt;BP))&gt;&gt; BP"/>
      <enum bytes="00" text="0"/>
      <enum bytes="01" text="1"/>
      <enum text="UNKNOWN"/>
    </unit>
    <unit name="HKP Pumpentyp">
      <abbrev>HKT</abbrev>
      <type>uchar</type>
      <icalc get="(B0 &amp; (0x01&lt;&lt;BP))&gt;&gt; BP"/>
      <enum bytes="00" text="stufig"/>
      <enum bytes="01" text="drehzahlgeregelt"/>
      <enum text="UNKNOWN"/>
    </unit>
    <unit name="Umschaltventil Stellung">
      <abbrev>USV</abbrev>
      <type>enum</type>
      <enum bytes="00" text="UNDEV"/>
      <enum bytes="01" text="Heizen"/>
      <enum bytes="02" text="Mittelstellung"/>
      <enum bytes="03" text="Warmwasser"/>
      <enum text="UNKNOWN"/>
    </unit>
  </units>
  <protocols>
    <protocol name="P300">
      <pid>41</pid>
      <macros>
        <macro name="GETADDR">
          <command>SEND 00 01</command>
        </macro>
        <macro name="SETADDR">
          <command>SEND 00 02</command>
        </macro>
      </macros>
      <commands>
        <command name="getaddr">
          <send>GETADDR $addr $hexlen;RECV $len $unit</send>
        </command>
        <command name="setaddr">
          <send>SETADDR $addr $hexlen;SEND BYTES $unit;RECV 1 SR</send>
        </command>
        <command name="gettestaddr">
          <send/>
        </command>
      </commands>
    </protocol>
    <protocol name="KW2">
      <macros>
        <macro name="SYNC">
          <command>SEND 04;WAIT 05</command>
        </macro>
        <macro name="GETADDR">
          <command>SEND 01 F7</command>
        </macro>
        <macro name="SETADDR">
          <command>SEND 01 F4</command>
        </macro>
      </macros>
      <commands>
        <command name="getaddr">
          <retry>3</retry>
          <recvTimeout>4000</recvTimeout>
          <send>SYNC;GETADDR $addr $hexlen;RECV $len $unit</send>
        </command>
        <command name="setaddr">
          <send>SYNC;SETADDR $addr $hexlen;SEND BYTES $unit;RECV 1 SR</send>
        </command>
        <command name="gettestaddr">
          <retry>3</retry>
          <recvTimeout>1500</recvTimeout>
          <send>SYNC;GETADDR;SEND BYTES;SEND 02;RECV 02</send>
        </command>
      </commands>
    </protocol>
    <protocol name="GWG">
      <macros>
        <macro name="SYNC">
          <command>SEND 04;WAIT 05</command>
        </macro>
        <macro name="GETADDR">
          <command>SEND 01 CB</command>
        </macro>
        <macro name="GETBADDR">
          <command>SEND 01 9E</command>
        </macro>
        <macro name="GETVADDR">
          <command>SEND 01 C7</command>
        </macro>
        <macro name="GETPADDR">
          <command>SEND 01 6E</command>
        </macro>
        <macro name="GETEADDR">
          <command>SEND 01 AE</command>
        </macro>
        <macro name="GETXADDR">
          <command>SEND 01 C5</command>
        </macro>
        <macro name="GETKMADDR">
          <command>SEND 01 43</command>
        </macro>
      </macros>
      <commands>
        <command name="enable">
          <send/>
        </command>
        <command name="disable">
          <send/>
        </command>
        <command name="getaddr">
          <retry>3</retry>
          <recvTimeout>4000</recvTimeout>
          <send>SYNC;GETADDR $addr $hexlen 04;RECV $len $unit</send>
        </command>
        <command name="gettestaddr">
          <retry>3</retry>
          <recvTimeout>4000</recvTimeout>
          <send>SYNC;GETADDR;SEND BYTES;SEND 01 04;RECV 1</send>
        </command>
        <command name="getbaddr">
          <retry>3</retry>
          <recvTimeout>4000</recvTimeout>
          <send>SYNC;GETBADDR $addr $hexlen 04;RECV $len $unit</send>
        </command>
        <command name="getbtestaddr">
          <retry>3</retry>
          <recvTimeout>4000</recvTimeout>
          <send>SYNC;GETBADDR;SEND BYTES;SEND 01 04;RECV 1</send>
        </command>
        <command name="getpaddr">
          <retry>3</retry>
          <recvTimeout>4000</recvTimeout>
          <send>SYNC;GETPADDR $addr $hexlen 04;RECV $len $unit</send>
        </command>
        <command name="getptestaddr">
          <retry>3</retry>
          <recvTimeout>4000</recvTimeout>
          <send>SYNC;GETPADDR;SEND BYTES;SEND 01 04;RECV 1</send>
        </command>
        <command name="getvaddr">
          <retry>3</retry>
          <recvTimeout>4000</recvTimeout>
          <send>SYNC;GETVADDR $addr $hexlen 04;RECV $len $unit</send>
        </command>
        <command name="getvtestaddr">
          <retry>3</retry>
          <recvTimeout>4000</recvTimeout>
          <send>SYNC;GETVADDR;SEND BYTES;SEND 01 04;RECV 1</send>
        </command>
        <command name="geteaddr">
          <retry>3</retry>
          <recvTimeout>4000</recvTimeout>
          <send>SYNC;GETEADDR $addr $hexlen 04;RECV $len $unit</send>
        </command>
        <command name="getetestaddr">
          <retry>3</retry>
          <recvTimeout>150</recvTimeout>
          <send>SYNC;GETEADDR;SEND BYTES;SEND 01 04;RECV 1</send>
        </command>
        <command name="getxaddr">
          <retry>3</retry>
          <recvTimeout>150</recvTimeout>
          <send>SYNC;GETXADDR $addr $hexlen 04;RECV $len $unit</send>
        </command>
        <command name="getxtestaddr">
          <retry>3</retry>
          <recvTimeout>4000</recvTimeout>
          <send>SYNC;GETXADDR;SEND BYTES;SEND 01 04;RECV 1</send>
        </command>
        <command name="getkmaddr">
          <send>SYNC;GETKMDDR $addr $hexlen 04;RECV $len $unit</send>
          <retry>3</retry>
          <recvTimeout>4000</recvTimeout>
        </command>
        <command name="getkmtestaddr">
          <retry>3</retry>
          <recvTimeout>4000</recvTimeout>
          <send>SYNC;GETKMADDR;SEND BYTES;SEND 01 04;RECV 1</send>
        </command>
        <command name="init">
          <send>SEND 16 00 00;PAUSE 100;SEND 16 00 00;PAUSE 100;SEND 16 00 00;PAUSE 100;SEND 16 00 00;PAUSE 100;SEND 16 00 00;PAUSE 100;SEND 16 00 00;PAUSE 100;SEND 16 00 00;PAUSE 100;SEND 16 00 00;PAUSE 100</send>
          <retry>3</retry>
        </command>
        <command name="setaddr">
          <send>SYNC;SETADDR $addr $hexlen;SEND BYTES $unit;RECV 1 SR</send>
        </command>
      </commands>
    </protocol>
  </protocols>
  <extern xmlns:xi="http://www.w3.org/2003/XInclude">
    <xi:include href="vito.xml" parse="xml"/>
  </extern>
</V-Control>
07070100000041000081A4000028240000003200000001607BF4480000DB13000000000000000000000000000000000000003800000000vcontrold-v0.98.10+git20210418.977e6f5/xml/300/vito.xml<?xml version="1.0"?>
<vito>
  <devices>
    <device ID="2098" name="V200KW2" protocol="KW2"/>
    <device ID="2053" name="GWG_VBEM" protocol="GWG"/>
    <device ID="20CB" name="VScotHO1" protocol="P300"/>
    <device ID="2094" name="V200KW1" protocol="KW2"/>
  </devices>
  <commands>
    <!-- Gerätedaten -->
    <!-- AUSSENTEMPERATUR -->
    <command name="getTempA" protocmd="getaddr">
      <addr>0800</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Aussentemperatur in Grad C</description>
      <device ID="2053">
        <addr>6F</addr>
        <unit>UT1</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getTempAtp" protocmd="getaddr">
      <addr>5525</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Aussentemperatur in Grad C (Tiefpass)</description>
      <device ID="2053"/>
    </command>
    <command name="getTempAged" protocmd="getaddr">
      <addr>5527</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Aussentemperatur in Grad C (Gedaempft)</description>
      <device ID="2053"/>
    </command>
    <!-- WARMWASSER -->
    <command name="getTempWWist" protocmd="getaddr">
      <addr>0804</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Warmwassertemperatur in Grad C</description>
    </command>
    <command name="getTempWWsoll" protocmd="getaddr">
      <addr>6300</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Ermittle die Warmwassersolltemperatur in Grad C</description>
    </command>
    <command name="setTempWWsoll" protocmd="setaddr">
      <addr>6300</addr>
      <len>2</len>
      <unit>UTI</unit>
      <description>Setze die Warmwassersolltemperatur in Grad C</description>
    </command>
    <command name="getTempStp2" protocmd="getaddr">
      <addr>0814</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die WW Auslauftemperatur in Grad C (Tiefpass)</description>
      <device ID="2053"/>
      <device ID="2098"/>
    </command>
    <command name="getWWUWPNachlauf" protocmd="getaddr">
      <addr>6762</addr>
      <len>2</len>
      <unit>CO</unit>
      <description>Ermittle Warmwasserpumpennachlauf </description>
    </command>
    <command name="setWWUWPNachlauf" protocmd="setaddr">
      <addr>6762</addr>
      <len>2</len>
      <unit>CO</unit>
      <description>Setze Warmwasserpumpennachlauf</description>
    </command>
    <command name="getSpeichervorrang" protocmd="getaddr">
      <addr>27A2</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Ermittle den Status Speichervorrang Heizkreis ohne Mischer A1 bzw. mit Mischer M1</description>
    </command>
    <command name="setSpeichervorrang" protocmd="setaddr">
      <addr>27A2</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Setze den Status Speichervorrang Heizkreis ohne Mischer A1 bzw. mit Mischer M1</description>
    </command>
    <command name="getSpeichervorrangM2" protocmd="getaddr">
      <addr>37A2</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Ermittle den Status Speichervorrang Heizkreis mit Mischer M2</description>
    </command>
    <command name="setSpeichervorrangM2" protocmd="setaddr">
      <addr>37A2</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Setze den Status Speichervorrang Heizkreis mit Mischer M2</description>
    </command>
    <command name="getSpeichervorrangM3" protocmd="getaddr">
      <addr>47A2</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Ermittle den Status Speichervorrang Heizkreis mit Mischer M3</description>
    </command>
    <command name="setSpeichervorrangM3" protocmd="setaddr">
      <addr>47A2</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Setze den Status Speichervorrang Heizkreis mit Mischer M3</description>
    </command>
    <!-- KESSEL -->
    <!-- Differenztemperatur Kesselkreis zu HKM(2) -->
    <command name="getTempKOffset" protocmd="getaddr">
      <addr>6760</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Ermittle Kesseloffset KT ueber WWsoll in Grad C</description>
    </command>
    <!-- Differenztemperatur Kesselkreis zu HKM(2) -->
    <command name="setTempKOffset" protocmd="setaddr">
      <addr>6760</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Setze Kesseloffset KT ueber WWsoll in Grad C</description>
    </command>
    <command name="getTempKist" protocmd="getaddr">
      <addr>0802</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Kesseltemperatur in Grad C</description>
      <device ID="2053">
        <addr>70</addr>
        <unit>UT1</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getTempAbgas" protocmd="getaddr">
      <addr>0808</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Abgastemperatur in Grad C</description>
      <device ID="20CB">
        <addr>0808</addr>
        <unit>UT</unit>
        <len>2</len>
      </device>
    </command>
    <command name="getTempKtp" protocmd="getaddr">
      <addr>0810</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Kesseltemperatur in Grad C (Tiefpass)</description>
      <device ID="2053"/>
    </command>
    <command name="getTempKsoll" protocmd="getaddr">
      <addr>5502</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Kesselsolltemperatur in Grad C</description>
      <device ID="2053">
        <addr>71</addr>
        <unit>UT1</unit>
        <len>1</len>
      </device>
      <device ID="20CB">
        <addr>555A</addr>
        <unit>UT</unit>
        <len>2</len>
      </device>
    </command>
    <command name="getVolStrom" protocmd="getaddr">
      <addr>0C24</addr>
      <len>2</len>
      <unit>VS</unit>
      <description>Ermittle den Volumenstrom l/h des Heizkreises</description>
    </command>
    <command name="getBetriebszeitStandby" protocmd="getaddr">
      <addr>08B8</addr>
      <len>4</len>
      <unit>CS</unit>
      <description>Ermittle die NRF_BetriebszeitStandby</description>
      <device ID="2053" protocmd="geteaddr">
        <addr>17</addr>
        <len>2</len>
        <unit>BH</unit>
      </device>
    </command>
    <!-- BRENNER -->
    <command name="getBrennerStatus" protocmd="getaddr">
      <addr>55D3</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Brennerstatus</description>
      <device ID="2053">
        <addr>22</addr>
        <len>1</len>
        <bit>1</bit>
        <unit>BST</unit>
      </device>
      <device ID="2094">
        <addr>0842</addr>
        <len>1</len>
        <unit>ST</unit>
      </device>
      <device ID="20CB">
        <addr>55D3</addr>
        <len>1</len>
        <unit>PR1</unit>
      </device>
    </command>
    <command name="getBrennerStatus2" protocmd="getaddr">
      <addr>0849</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Ermittle den Brennerstatus Stufe 2</description>
      <device ID="2094">
        <addr>0849</addr>
        <len>1</len>
        <unit>ST</unit>
      </device>
    </command>
    <command name="getBrennerStufe" protocmd="getaddr">
      <addr>551E</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Brennerstatus aktuelle Stufe</description>
      <device ID="2053">
        <addr>22</addr>
        <len>1</len>
        <bit>1</bit>
        <unit>BST</unit>
      </device>
    </command>
    <command name="getBrennerStarts" protocmd="getaddr">
      <addr>088A</addr>
      <len>4</len>
      <unit>CO</unit>
      <description>Ermittle die Brennerstarts</description>
      <device ID="2053"/>
    </command>
    <command name="setBrennerStarts" protocmd="setaddr">
      <addr>088A</addr>
      <len>2</len>
      <unit>CO</unit>
      <description>Setze die Brennerstarts</description>
      <device ID="2053"/>
    </command>
    <command name="getBrennerStunden1" protocmd="getaddr">
      <addr>08A7</addr>
      <len>4</len>
      <unit>CS</unit>
      <description>Ermittle die Brennerstunden Stufe 1</description>
      <device ID="2053" protocmd="geteaddr">
        <addr>17</addr>
        <len>2</len>
        <unit>BH</unit>
      </device>
    </command>
    <command name="setBrennerStunden1" protocmd="setaddr">
      <addr>08A7</addr>
      <len>4</len>
      <unit>CO</unit>
      <description>Setze die Brennerstunden Stufe 1</description>
      <device ID="2053" protocmd="setaddr">
        <addr>17</addr>
        <len>2</len>
        <unit>BH</unit>
      </device>
    </command>
    <command name="getBrennerStunden2" protocmd="getaddr">
      <addr>08AB</addr>
      <len>4</len>
      <unit>CS</unit>
      <description>Ermittle die Brennerstunden Stufe 2</description>
      <device ID="2053"/>
    </command>
    <command name="getLeistungIst" protocmd="getaddr">
      <addr>A38F</addr>
      <len>2</len>
      <unit>PR3</unit>
      <description>Ermittle Anlagen Ist-Leistung</description>
      <device ID="2053"/>
    </command>
    <command name="getOeldurchsatz" protocmd="getaddr">
      <addr>5726</addr>
      <len>4</len>
      <unit>CO</unit>
      <description>Eingestellter Oeldurchsatz Brenner in Dezi-Liter pro Stunde</description>
    </command>
    <command name="setOeldurchsatz" protocmd="setaddr">
      <addr>5726</addr>
      <len>4</len>
      <unit>CO</unit>
      <description>Oeldurchsatz Brenner in Dezi-Liter pro Stunde einstellen</description>
    </command>
    <command name="getOelverbrauch" protocmd="getaddr">
      <addr>7574</addr>
      <len>4</len>
      <unit>COL</unit>
      <description>Ermittle den Oelverbrauch kumuliert</description>
      <device ID="2053"/>
    </command>
    <!-- HEIZKURVEN -->
    <command name="getNeigungM1" protocmd="getaddr">
      <addr>2305</addr>
      <len>1</len>
      <unit>UN</unit>
      <description>Ermittle Neigung Heizkennlinie M1</description>
      <device ID="2053">
        <addr>65</addr>
        <unit>UN</unit>
        <len>1</len>
      </device>
      <device ID="20CB">
        <addr>27D3</addr>
        <unit>UN</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getNeigungM2" protocmd="getaddr">
      <addr>3305</addr>
      <len>1</len>
      <unit>UN</unit>
      <description>Ermittle Neigung Heizkennlinie M2</description>
      <device ID="2053">
        <addr>C4</addr>
        <unit>UN</unit>
        <len>1</len>
      </device>
      <device ID="20CB">
        <addr>37D3</addr>
        <unit>UN</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getNiveauM1" protocmd="getaddr">
      <addr>2304</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Ermittle Niveau Heizkennlinie M1</description>
      <device ID="2053">
        <addr>64</addr>
        <unit>ST</unit>
        <len>1</len>
      </device>
      <device ID="20CB">
        <addr>27D4</addr>
        <unit>ST</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getNiveauM2" protocmd="getaddr">
      <addr>3304</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Ermittle Niveau Heizkennlinie M2</description>
      <device ID="2053">
        <addr>C5</addr>
        <unit>ST</unit>
        <len>1</len>
      </device>
      <device ID="20CB">
        <addr>37D4</addr>
        <unit>ST</unit>
        <len>1</len>
      </device>
    </command>
    <command name="setNeigungM1" protocmd="setaddr">
      <!-- min zul.0.2 0.1 und 0.0 nicht erlaubt -->
      <addr>2305</addr>
      <len>1</len>
      <unit>UN</unit>
      <description>Setze Neigung Heizkennlinie M1</description>
      <device ID="2053"/>
      <device ID="20CB">
        <addr>27D3</addr>
        <unit>UN</unit>
        <len>1</len>
      </device>
    </command>
    <command name="setNeigungM2" protocmd="setaddr">
      <!-- min zul.0.2 0.1 und 0.0 nicht erlaubt -->
      <addr>3305</addr>
      <len>1</len>
      <unit>UN</unit>
      <description>Setze Neigung Heizkennlinie M2</description>
      <device ID="2053"/>
      <device ID="20CB">
        <addr>37D3</addr>
        <unit>UN</unit>
        <len>1</len>
      </device>
    </command>
    <command name="setNiveauM1" protocmd="setaddr">
      <addr>2304</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Setze Niveau Heizkennlinie M1</description>
      <device ID="2053"/>
      <device ID="20CB">
        <addr>27D4</addr>
        <unit>ST</unit>
        <len>1</len>
      </device>
    </command>
    <command name="setNiveauM2" protocmd="setaddr">
      <addr>3304</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Setze Niveau Heizkennlinie M2</description>
      <device ID="2053"/>
      <device ID="20CB">
        <addr>37D4</addr>
        <unit>ST</unit>
        <len>1</len>
      </device>
    </command>
    <!-- HEIZKREISE -->
    <command name="getTempVListM1" protocmd="getaddr">
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Vorlauftemperatur M1 in Grad C</description>
      <device ID="2053"/>
      <device ID="20CB">
        <addr>2900</addr>
        <unit>UT</unit>
        <len>2</len>
      </device>
    </command>
    <command name="getTempVListM2" protocmd="getaddr">
      <addr>080C</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Vorlauftemperatur M2 in Grad C</description>
      <device ID="2053">
        <addr>41</addr>
        <unit>UT1</unit>
        <len>1</len>
      </device>
      <device ID="20CB">
        <addr>3900</addr>
        <unit>UT</unit>
        <len>2</len>
      </device>
    </command>
    <command name="getTempVLsollM1" protocmd="getaddr">
      <addr>2544</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Vorlaufsolltemperatur M1 in Grad C</description>
      <device ID="2053">
        <addr>69</addr>
        <unit>UT1</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getTempVLsollM2" protocmd="getaddr">
      <addr>3544</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Vorlaufsolltemperatur M2 in Grad C</description>
      <device ID="2053">
        <addr>63</addr>
        <unit>UT1</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getTempVLsollM3" protocmd="getaddr">
      <addr>4544</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Vorlaufsolltemperatur M3 in Grad C</description>
      <device ID="2053"/>
    </command>
    <command name="getTempRL17A" protocmd="getaddr">
      <addr>080A</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Ruecklauftemperatur 17A in Grad C</description>
      <device ID="2053"/>
      <device ID="20CB">
        <addr>0808</addr>
        <len>2</len>
        <unit>UT</unit>
      </device>
    </command>
    <!-- SOLAR -->
    <command name="getTempKol" protocmd="getaddr">
      <addr>6564</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Kollektortemperatur in Grad C</description>
      <device ID="2053"/>
    </command>
    <command name="getSolarStatusWW" protocmd="getaddr">
      <addr>6551</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Status der Nachladeunterdrueckung</description>
      <device ID="2053"/>
    </command>
    <command name="getSolarStunden" protocmd="getaddr">
      <addr>6568</addr>
      <len>2</len>
      <unit>CO</unit>
      <description>Solar Betriebsstunden</description>
      <device ID="2053"/>
    </command>
    <command name="getSolarLeistung" protocmd="getaddr">
      <addr>6560</addr>
      <len>4</len>
      <unit>CO</unit>
      <description>Solar Leistung Gesamt</description>
      <device ID="2053"/>
    </command>
    <command name="getTempS4" protocmd="getaddr">
      <addr>0124</addr>
      <len>1</len>
      <unit>UT1</unit>
      <description>Ermittle die Temperatur S4 in Grad C</description>
      <device ID="2053"/>
    </command>
    <!-- SPEICHER1 -->
    <command name="getTempSpu" protocmd="getaddr">
      <addr>6566</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Speichertemperatur unten in Grad C</description>
      <device ID="2053"/>
    </command>
    <command name="getTempStp" protocmd="getaddr">
      <addr>0812</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Speichertemperatur in Grad C (Tiefpass)</description>
      <device ID="2053"/>
      <device ID="2098"/>
    </command>
    <!-- SOLLTEMPERATUREN -->
    <command name="getTempRaumNorSollM1" protocmd="getaddr">
      <addr>2306</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Ermittle die Raumsolltemperatur normal M1 in Grad C</description>
      <device ID="2053">
        <addr>53</addr>
        <len>1</len>
        <unit>UTI</unit>
      </device>
    </command>
    <!-- Aufruf in vclient ganzzahlig 'setTempRaumNorSollM1 21' -->
    <command name="setTempRaumNorSollM1" protocmd="setaddr">
      <addr>2306</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Setze die Raumsolltemperatur normal M1 in Grad C</description>
      <device ID="2053">
        <addr>53</addr>
        <len>1</len>
        <unit>UTI</unit>
      </device>
    </command>
    <command name="getTempRaumNorSollM2" protocmd="getaddr">
      <addr>3306</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Ermittle die Raumsolltemperatur normal M2 in Grad C</description>
      <device ID="2053"/>
    </command>
    <!-- Aufruf in vclient ganzzahlig 'setTempRaumNorSollM2 21' -->
    <command name="setTempRaumNorSollM2" protocmd="setaddr">
      <addr>3306</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Setze die Raumsolltemperatur normal M2 in Grad C</description>
      <device ID="2053"/>
    </command>
    <command name="getTempRaumRedSollM1" protocmd="getaddr">
      <addr>2307</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Ermittle die Raumsolltemperatur reduziert M1 in Grad C</description>
      <device ID="2053">
        <addr>54</addr>
        <len>1</len>
        <unit>UTI</unit>
      </device>
    </command>
    <command name="setTempRaumRedSollM1" protocmd="setaddr">
      <addr>2307</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Setze die Raumsolltemperatur reduziert M1 in Grad C</description>
      <device ID="2053">
        <addr>54</addr>
        <len>1</len>
        <unit>UTI</unit>
      </device>
    </command>
    <command name="getTempRaumRedSollM2" protocmd="getaddr">
      <addr>3307</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Ermittle die Raumsolltemperatur reduziert M2 in Grad C</description>
      <device ID="2053"/>
    </command>
    <command name="setTempRaumRedSollM2" protocmd="setaddr">
      <addr>3307</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Setze die Raumsolltemperatur reduziert M2 in Grad C</description>
      <device ID="2053"/>
    </command>
    <!-- PUMPEN -->
    <command name="getPumpeStatusM1" protocmd="getaddr">
      <addr>2906</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Status der Pumpe M1</description>
      <device ID="2053" protocmd="getpaddr">
        <addr>01</addr>
        <len>1</len>
        <bit>0</bit>
        <unit>BST</unit>
      </device>
      <device ID="20CB">
        <addr>7663</addr>
        <len>2</len>
        <unit>PR2</unit>
      </device>
    </command>
    <command name="getPumpeStatusM2" protocmd="getaddr">
      <addr>3906</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Status der Pumpe M2</description>
      <device ID="2053"/>
      <device ID="20CB">
        <addr>7665</addr>
        <len>1</len>
        <unit>RT</unit>
      </device>
    </command>
    <command name="getPumpeDrehzahlM2" protocmd="getaddr">
      <addr>3906</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle die Drehzahl der Pumpe M2</description>
      <device ID="2053"/>
      <device ID="20CB">
        <addr>7665</addr>
        <len>2</len>
        <unit>PR2</unit>
      </device>
    </command>
    <command name="getPumpeStatusIntern" protocmd="getaddr">
      <description>Ermittle den Status der Internen Pumpe</description>
      <device ID="20CB">
        <addr>7660</addr>
        <len>1</len>
        <unit>RT</unit>
      </device>
    </command>
    <command name="getPumpeDrehzahlIntern" protocmd="getaddr">
      <addr>3906</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle die Drehzahl der Internen Pumpe</description>
      <device ID="2053"/>
      <device ID="20CB">
        <addr>7660</addr>
        <len>2</len>
        <unit>PR2</unit>
      </device>
    </command>
    <command name="getPumpeStatusSp" protocmd="getaddr">
      <addr>0845</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Status der Speicherladepumpe</description>
      <device ID="2053"/>
      <device ID="20CB">
        <addr>6513</addr>
        <len>1</len>
        <unit>RT</unit>
      </device>
    </command>
    <command name="getPumpeStatusZirku" protocmd="getaddr">
      <addr>0846</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Status der Zirkulationspumpe</description>
      <device ID="2053" protocmd="getpaddr">
        <addr>01</addr>
        <len>1</len>
        <bit>2</bit>
        <unit>BST</unit>
      </device>
      <device ID="20CB">
        <addr>6515</addr>
        <len>1</len>
        <unit>RT</unit>
      </device>
    </command>
    <command name="getPumpeStatusSolar" protocmd="getaddr">
      <addr>6552</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Status der Umwaelzpumpe Solar</description>
      <device ID="2053"/>
    </command>
    <command name="getPumpeDrehzahlSolar" protocmd="getaddr">
      <addr>6552</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle die Drehzahl der Internen Pumpe</description>
      <device ID="2053"/>
      <device ID="20CB">
        <addr>6552</addr>
        <len>2</len>
        <unit>PR2</unit>
      </device>
    </command>
    <command name="getPumpeStatusSolar2" protocmd="getaddr">
      <addr>6553</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Status der Pumpe 2 Solar </description>
      <device ID="2053"/>
    </command>
    <command name="getPumpeStatusSolar3" protocmd="getaddr">
      <addr>6554</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Status der Pumpe 3 Solar </description>
      <device ID="2053"/>
    </command>
    <command name="getPumpeStatusSolar4" protocmd="getaddr">
      <addr>6555</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Status der Pumpe 4 Solar </description>
      <device ID="2053"/>
    </command>
    <command name="getTempSTSSOL" protocmd="getaddr">
      <addr>081A</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Speichertemperatur in Grad C (NRF_TiefpassTemperaturwert_STSSOL)</description>
      <device ID="2053"/>
      <device ID="2098"/>
    </command>
    <!-- MISCHER -->
    <command name="getMischerM1" protocmd="getaddr">
      <addr>254C</addr>
      <len>1</len>
      <unit>PR</unit>
      <description>Ermittle Mischerposition M1</description>
      <device ID="2053"/>
    </command>
    <command name="getMischerM2" protocmd="getaddr">
      <addr>354C</addr>
      <len>1</len>
      <unit>PR</unit>
      <description>Ermittle Mischerposition M2</description>
      <device ID="2053"/>
      <device ID="20CB">
        <addr>354C</addr>
        <len>1</len>
        <unit>PR</unit>
      </device>
    </command>
    <command name="getMischerM3" protocmd="getaddr">
      <addr>454C</addr>
      <len>1</len>
      <unit>PR</unit>
      <description>Ermittle Mischerposition M3</description>
      <device ID="2053"/>
    </command>
    <!-- HEIZZEITEN -->
    <command name="getTimerM1Mo" protocmd="getaddr">
      <addr>2000</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Montag M1</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM1Di" protocmd="getaddr">
      <addr>2008</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Dienstag M1</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM1Mi" protocmd="getaddr">
      <addr>2010</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit MIttwoch M1</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM1Do" protocmd="getaddr">
      <addr>2018</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Donnerstag M1</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM1Fr" protocmd="getaddr">
      <addr>2020</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Freitag M1</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM1Sa" protocmd="getaddr">
      <addr>2028</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Samstag M1</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM1So" protocmd="getaddr">
      <addr>2030</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Sonntag M1</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM2Mo" protocmd="getaddr">
      <addr>3000</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Montag M2</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM2Di" protocmd="getaddr">
      <addr>3008</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Dienstag M2</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM2Mi" protocmd="getaddr">
      <addr>3010</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Mittwoch M2</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM2Do" protocmd="getaddr">
      <addr>3018</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Donnerstag M2</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM2Fr" protocmd="getaddr">
      <addr>3020</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Freitag M2</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM2Sa" protocmd="getaddr">
      <addr>3028</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Samstag M2</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM2So" protocmd="getaddr">
      <addr>3030</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Sonntag M2</description>
      <device ID="2053"/>
    </command>
    <!-- WARMWASSERZEITEN -->
    <command name="getTimerWWMo" protocmd="getaddr">
      <addr>2100</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Montag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerWWDi" protocmd="getaddr">
      <addr>2108</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Dienstag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerWWMi" protocmd="getaddr">
      <addr>2110</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Mittwoch Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerWWDo" protocmd="getaddr">
      <addr>2118</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Donnerstag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerWWFr" protocmd="getaddr">
      <addr>2120</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Freitag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerWWSa" protocmd="getaddr">
      <addr>2128</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Samstag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerWWSo" protocmd="getaddr">
      <addr>2130</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Sonntag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <!-- ZIRKULATIONSZEITEN -->
    <command name="getTimerZirkuMo" protocmd="getaddr">
      <addr>2200</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Montag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerZirkuDi" protocmd="getaddr">
      <addr>2208</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Dienstag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerZirkuMi" protocmd="getaddr">
      <addr>2210</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Mittwoch Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerZirkuDo" protocmd="getaddr">
      <addr>2218</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Donnerstag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerZirkuFr" protocmd="getaddr">
      <addr>2220</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Freitag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerZirkuSa" protocmd="getaddr">
      <addr>2228</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Samstag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerZirkuSo" protocmd="getaddr">
      <addr>2230</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Sonntag Zirku</description>
      <device ID="2053"/>
    </command>
    <!-- BETRIEBSARTEN -->
    <command name="getBetriebArtM1" protocmd="getaddr">
      <addr>2301</addr>
      <len>1</len>
      <unit>BA</unit>
      <description>Betriebsart M1</description>
      <device ID="2053">
        <addr>51</addr>
        <len>1</len>
        <unit>BA</unit>
      </device>
    </command>
    <command name="getBetriebArt" protocmd="getaddr">
      <addr>2301</addr>
      <len>1</len>
      <unit>BA</unit>
      <description>Betriebsart</description>
      <device ID="2053"/>
    </command>
    <command name="getBetriebArtM2" protocmd="getaddr">
      <addr>3301</addr>
      <len>1</len>
      <unit>BA</unit>
      <description>Betriebsart M2</description>
      <device ID="2053"/>
    </command>
    <!-- SPARBETRIEB (SPARSCHWEIN) -->
    <command name="getBetriebSparM1" protocmd="getaddr">
      <addr>2302</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Betriebsart Spar M1</description>
      <device ID="2053"/>
    </command>
    <command name="getBetriebSparM2" protocmd="getaddr">
      <addr>3302</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Betriebsart Spar M2</description>
      <device ID="2053"/>
    </command>
    <!-- PARTYBETRIEB -->
    <command name="getBetriebPartyM1" protocmd="getaddr">
      <addr>2303</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Betriebsart Party M1</description>
      <device ID="2053"/>
    </command>
    <command name="getBetriebPartyM2" protocmd="getaddr">
      <addr>3303</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Betriebsart Party M2</description>
      <device ID="2053"/>
    </command>
    <command name="getTempPartyM1" protocmd="getaddr">
      <addr>2308</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Solltemperatur Partybetrieb M1</description>
      <device ID="2053"/>
    </command>
    <command name="setTempPartyM1" protocmd="setaddr">
      <addr>2308</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Setze die Warmwassersolltemperatur Party M1 in Grad C</description>
      <device ID="2053"/>
    </command>
    <command name="getTempPartyM2" protocmd="getaddr">
      <addr>3308</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Solltemperatur Partybetrieb M2</description>
      <device ID="2053"/>
    </command>
    <command name="setTempPartyM2" protocmd="setaddr">
      <addr>3308</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Setze die Warmwassersolltemperatur Party M2 in Grad C</description>
      <device ID="2053"/>
    </command>
    <command name="setBetriebPartyM1" protocmd="setaddr">
      <addr>2303</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Setze Betriebsart Party M1</description>
      <device ID="2053"/>
    </command>
    <command name="setBetriebPartyM2" protocmd="setaddr">
      <addr>3303</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Setze Betriebsart Party M2</description>
      <device ID="2053"/>
    </command>
    <!-- EXTERNES SPERREN und ANFORDERN -->
    <command name="getEinflussExtSperren" protocmd="getaddr">
      <addr>5732</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Einfluss Extern Sperren auf Pumpen</description>
    </command>
    <command name="getEinflussExtAnforderung" protocmd="getaddr">
      <addr>5734</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Einfluss Externe Anforderung auf Pumpen</description>
    </command>
    <command name="getExtSperren" protocmd="getaddr">
      <addr>0A81</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ist Extern Sperren aktiv</description>
    </command>
    <command name="getExtAnforderung" protocmd="getaddr">
      <addr>0A80</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ist Externe Anforderung aktiv</description>
    </command>
    <!-- FROSTSCHUTZ -->
    <command name="getStatusFrostM1" protocmd="getaddr">
      <addr>2500</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Status Frostwarnung M1</description>
      <device ID="2053"/>
    </command>
    <command name="getStatusFrostM2" protocmd="getaddr">
      <addr>3500</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Status Frostwarnung M2</description>
      <device ID="2053"/>
    </command>
    <!-- STOERUNG -->
    <command name="getStatusStoerung" protocmd="getaddr">
      <addr>7579</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Status Sammelstoerung</description>
      <device ID="2053"/>
      <device ID="20CB">
        <addr>0A82</addr>
        <len>1</len>
        <unit>RT</unit>
      </device>
    </command>
    <command name="getError0" protocmd="getaddr">
      <addr>7507</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 1</description>
      <device ID="2053">
        <addr>3F</addr>
        <len>1</len>
        <unit>ESG</unit>
      </device>
    </command>
    <command name="getError1" protocmd="getaddr">
      <addr>7510</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 2</description>
      <device ID="2053"/>
    </command>
    <command name="getError2" protocmd="getaddr">
      <addr>7519</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 3</description>
      <device ID="2053"/>
    </command>
    <command name="getError3" protocmd="getaddr">
      <addr>7522</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 4</description>
      <device ID="2053"/>
    </command>
    <command name="getError4" protocmd="getaddr">
      <addr>752B</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 5</description>
      <device ID="2053"/>
    </command>
    <command name="getError5" protocmd="getaddr">
      <addr>7534</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 6</description>
      <device ID="2053"/>
    </command>
    <command name="getError6" protocmd="getaddr">
      <addr>753D</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 7</description>
      <device ID="2053"/>
    </command>
    <command name="getError7" protocmd="getaddr">
      <addr>7546</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 8</description>
      <device ID="2053"/>
    </command>
    <command name="getError8" protocmd="getaddr">
      <addr>754F</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 9</description>
      <device ID="2053"/>
    </command>
    <command name="getError9" protocmd="getaddr">
      <addr>7558</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 10</description>
      <device ID="2053"/>
    </command>
    <!-- SYSTEMZEIT -->
    <command name="getSystemTime" protocmd="getaddr">
      <addr>088E</addr>
      <len>8</len>
      <unit>TI</unit>
      <description>Ermittle Systemzeit</description>
      <device ID="2053"/>
    </command>
    <command name="setSystemTime" protocmd="setaddr">
      <addr>088E</addr>
      <len>8</len>
      <unit>TI</unit>
      <description>Setze Systemzeit</description>
      <device ID="2053"/>
    </command>
    <!-- KONFIGURATION UND SETZEN -->
    <!-- xxx Heizkreispumpenlogik-Funktion xxx -->
    <!-- A5:0 Ohne Heizkreispumpenlogik-Funktion Mit Heizkreispumpenlogik−Funktion:
         Heizkreispumpe aus, wenn A5:1 AT > RTSoll + 5 K A5:2 AT > RTSoll + 4 K A5:3
         AT > RTSoll + 3 K A5:4 AT > RTSoll + 2 K A5:5 AT > RTSoll + 1 K A5:6 AT =
         RTSoll A5:7 AT > RTSoll -1 K bis A5:15 AT > RTSoll -9 K -->
    <command name="getKA5" protocmd="getaddr">
      <addr>27A5</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Ermittle die Heizkreispumpenlogik&#x2212;Funktion</description>
      <!-- KA5_KonfSommersparSchaltschwelle A5 0x27a5 -->
    </command>
    <command name="setKA5" protocmd="setaddr">
      <addr>27A5</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Setze die Heizkreispumpenlogik&#x2212;Funktion</description>
      <!-- KA5_KonfSommersparSchaltschwelle A5 0x27a5 1Byte -->
    </command>
    <command name="getKA5M2" protocmd="getaddr">
      <addr>37A5</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Ermittle die Heizkreispumpenlogik&#x2212;Funktion M2</description>
      <!-- KA5_KonfSommersparSchaltschwelleM2 A5 0x37a5 -->
    </command>
    <command name="setKA5M2" protocmd="setaddr">
      <addr>37A5</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Setze die Heizkreispumpenlogik&#x2212;Funktion M2</description>
      <!-- KA5_KonfSommersparSchaltschwelleM2 A5 0x37a5 1Byte -->
    </command>
    <command name="getKA5M3" protocmd="getaddr">
      <addr>47A5</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Ermittle die Heizkreispumpenlogik&#x2212;Funktion M3</description>
      <!-- KA5_KonfSommersparSchaltschwelleM2 A5 0x47a5 -->
    </command>
    <command name="setKA5M3" protocmd="setaddr">
      <addr>47A5</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Setze die Heizkreispumpenlogik&#x2212;Funktion M3</description>
      <!-- KA5_KonfSommersparSchaltschwelleM2 A5 0x47a5 1Byte -->
    </command>
    <!-- xxx Konfiguration A6 Erweiterte Sparschaltung xxx -->
    <!-- KA6_KonfiAbsolutSommersparM2 A6 0x37a6 -->
    <!-- A6:36 Erweiterte Sparschaltung nicht aktiv A6:5 bis A6:35 Erweiterte
         Sparschaltung aktiv, d.h. bei einem variabel einstellbaren Wert von 5 bis
         35 °C zuzüglich 1°C werden Brenner und Heizkreispumpe ausgeschaltet. Grundlage
         ist die gedämpfte Außentemperatur, die sich aus tatsächlicher Außentemperatur
         und einer Zeitkonstanten, die das Auskühlen eines durchschnittlichen Gebäudes
         berücksichtigt, zusammensetzt. -->
    <command name="getKA6" protocmd="getaddr">
      <addr>27A6</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Ermittle die AbsolutSommersparschaltung </description>
      <!-- KA6_KonfiAbsolutSommerspar A6 0x27a6 -->
    </command>
    <command name="setKA6" protocmd="setaddr">
      <addr>27A6</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Setze die AbsolutSommersparschaltung</description>
      <!-- KA6_KonfiAbsolutSommerspar A6 0x27a6 -->
    </command>
    <command name="getKA6M2" protocmd="getaddr">
      <addr>37A6</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Ermittle die AbsolutSommersparschaltung M2</description>
      <!-- KA6_KonfiAbsolutSommersparM2 A6 0x37a6 -->
    </command>
    <command name="setKA6M2" protocmd="setaddr">
      <addr>37A6</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Setze die AbsolutSommersparschaltung M2</description>
      <!-- KA6_KonfiAbsolutSommersparM2 A6 0x37a6 1Byte -->
    </command>
    <command name="getKA6M3" protocmd="getaddr">
      <addr>47A6</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Ermittle die AbsolutSommersparschaltung M3</description>
      <!-- KA6_KonfiAbsolutSommersparM3 A6 0x47a6 -->
    </command>
    <command name="setKA6M3" protocmd="setaddr">
      <addr>47A6</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Setze die AbsolutSommersparschaltung M3</description>
      <!-- KA6_KonfiAbsolutSommersparM3 A6 0x47a6 1Byte -->
    </command>
    <!-- Auswirkung Mischer auf interne Pumpe -->
    <command name="getKonfiWirkung_aufPumpe" protocmd="getaddr">
      <addr>37A8</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Ermittle Einfluss Mischer auf int UWP</description>
      <!-- 0 ohne 1 M2 setzt Anforderung an Int.Pumpe - nur sinnvoll bei Mischerkreis -->
      <!-- wenn nicht vorhanden Rückgabewert -1.0 -->
    </command>
    <command name="setKonfiWirkung_aufPumpe" protocmd="setaddr">
      <addr>37A8</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Setze Einfluss Mischer auf int UWP</description>
      <!-- 0 ohne 1 M2 setzt Anforderung an Int.Pumpe - nur sinnvoll bei Mischerkreis -->
      <!-- wenn nicht vorhanden Rückgabewert -1.0 -->
    </command>
    <command name="getKA3_KonfiFrostgrenzeM1_GWG" protocmd="getaddr">
      <addr>27A3</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Ermittle die eingestellte Frostgrenze A1M1</description>
      <!-- Range GradCelsius SByte 1 -9 15 -->
    </command>
    <command name="setKA3_KonfiFrostgrenzeM1_GWG" protocmd="setaddr">
      <addr>27A3</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Setze die Frostgrenze A1M1</description>
      <!-- Range GradCelsius SByte 1 -9 15 -->
    </command>
    <command name="getKA3_KonfiFrostgrenzeM2_GWG" protocmd="getaddr">
      <addr>37A3</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Ermittle die eingestellte Frostgrenze</description>
      <!-- Range GradCelsius SByte 1 -9 15 -->
    </command>
    <command name="setKA3_KonfiFrostgrenzeM2_GWG" protocmd="setaddr">
      <addr>37A3</addr>
      <len>1</len>
      <unit>UTI</unit>
      <description>Setze die Frostgrenze</description>
      <!-- Range GradCelsius SByte 1 -9 15 -->
    </command>
    <command name="setPumpeStatusZirku" protocmd="setaddr">
      <addr>0846</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Setze den Status der Zirkulationspumpe</description>
      <device ID="2053">
        <addr>01</addr>
        <len>1</len>
        <bit>2</bit>
        <precommand>getPumpeStatusZirku</precommand>
        <unit>BA</unit>
      </device>
    </command>
    <command name="setTimerM1Mo" protocmd="setaddr">
      <addr>2000</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Montag M1</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM1Di" protocmd="setaddr">
      <addr>2008</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Dienstag M1</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM1Mi" protocmd="setaddr">
      <addr>2010</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Mittwoch M1</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM1Do" protocmd="setaddr">
      <addr>2018</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Donnerstag M1</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM1Fr" protocmd="setaddr">
      <addr>2020</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Freitag M1</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM1Sa" protocmd="setaddr">
      <addr>2028</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Samstag M1</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM1So" protocmd="setaddr">
      <addr>2030</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Sonntag M1</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM2Mo" protocmd="setaddr">
      <addr>3000</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Montag M2</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM2Di" protocmd="setaddr">
      <addr>3008</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Dienstag M2</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM2Mi" protocmd="setaddr">
      <addr>3010</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Mittwoch M2</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM2Do" protocmd="setaddr">
      <addr>3018</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Donnerstag M2</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM2Fr" protocmd="setaddr">
      <addr>3020</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Freitag M2</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM2Sa" protocmd="setaddr">
      <addr>3028</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Samstag M2</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM2So" protocmd="setaddr">
      <addr>3030</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Sonntag M2</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerWWMo" protocmd="setaddr">
      <addr>2100</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Montag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerWWDi" protocmd="setaddr">
      <addr>2108</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Dienstag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerWWMi" protocmd="setaddr">
      <addr>2110</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Mittwoch Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerWWDo" protocmd="setaddr">
      <addr>2118</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Donnerstag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerWWFr" protocmd="setaddr">
      <addr>2120</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Freitag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerWWSa" protocmd="setaddr">
      <addr>2128</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Samstag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerWWSo" protocmd="setaddr">
      <addr>2130</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Sonntag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerZirkuMo" protocmd="setaddr">
      <addr>2200</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Montag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerZirkuDi" protocmd="setaddr">
      <addr>2208</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Dienstag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerZirkuMi" protocmd="setaddr">
      <addr>2210</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Mittwoch Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerZirkuDo" protocmd="setaddr">
      <addr>2218</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Donnerstag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerZirkuFr" protocmd="setaddr">
      <addr>2220</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Freitag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerZirkuSa" protocmd="setaddr">
      <addr>2228</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Samstag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerZirkuSo" protocmd="setaddr">
      <addr>2230</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Sonntag Zirku</description>
      <device ID="2053"/>
    </command>
    <!-- Gerätedaten -->
    <command name="getDevType" protocmd="getaddr">
      <addr>00F8</addr>
      <len>8</len>
      <unit>DT</unit>
      <description>Ermittle Device Typ der Anlage / SystemIdent_SX</description>
    </command>
    <command name="getCtrlId" protocmd="getaddr">
      <addr>00F9</addr>
      <len>7</len>
      <unit>DT</unit>
      <description>Reglerkennung</description>
    </command>
    <command name="getInventory" protocmd="getaddr">
      <addr>08E0</addr>
      <len>7</len>
      <unit>SN</unit>
      <description>Sachnummer</description>
    </command>
    <command name="getInvCodePlug" protocmd="getaddr">
      <addr>1010</addr>
      <len>7</len>
      <unit>SN</unit>
      <description>Sachnummer Codierstecker</description>
    </command>
    <command name="getPanelSWIndex" protocmd="getaddr">
      <addr>7330</addr>
      <len>8</len>
      <unit>DT</unit>
      <description>Bedienteil SW Index</description>
    </command>
    <command name="getKsCardType" protocmd="getaddr">
      <addr>7656</addr>
      <len>4</len>
      <unit>DT</unit>
      <description>KS Karte Typ</description>
    </command>
    <command name="getAnlagenschema" protocmd="getaddr">
      <addr>7700</addr>
      <len>2</len>
      <unit>ST</unit>
      <description>Ermittle das Anlagenschema</description>
      <!-- K00_KonfiAnlagenschemaGWG_W (00) Heizkreis-Warmwasserschema Adresse
           7700 1 A1 | 1 2 A1 + WW | 2 3 M2 | 3 4 M2 + WW | 4 5 A1 + M2 | 5 6 A1 + M2
           + WW | 6 -->
    </command>
    <command name="init" protocmd="getaddr">
      <description>Inintialisierung</description>
      <device ID="2053" protocmd="init">
        <addr>dummy</addr>
      </device>
    </command>
    <command name="get" protocmd="gettestaddr">
      <description>Testabfrage, Adresse eingeben</description>
      <device ID="20CB">
        <addr>dummy</addr>
        <len>2</len>
      </device>
      <device ID="2053">
        <addr>dummy</addr>
        <len>1</len>
      </device>
      <device ID="2094">
        <addr>dummy</addr>
        <len>2</len>
      </device>
    </command>
    <command name="vget" protocmd="getvtestaddr">
      <description>Testabfrage, Virtuelle Adresse eingeben</description>
      <device ID="2053">
        <addr>dummy</addr>
        <len>1</len>
      </device>
    </command>
    <command name="bget" protocmd="getbtestaddr">
      <description>Testabfrage, Bedienteil Adresse eingeben</description>
      <device ID="2053">
        <addr>dummy</addr>
        <len>1</len>
      </device>
    </command>
    <command name="pget" protocmd="getptestaddr">
      <description>Testabfrage, Port Adresse eingeben</description>
      <device ID="2053">
        <addr>dummy</addr>
        <len>1</len>
      </device>
    </command>
    <command name="eget" protocmd="getetestaddr">
      <description>Testabfrage, EEProm Adresse eingeben</description>
      <device ID="2053">
        <addr>dummy</addr>
        <len>1</len>
      </device>
    </command>
    <command name="xget" protocmd="getxtestaddr">
      <description>Testabfrage, XRAM Adresse eingeben</description>
      <device ID="2053">
        <addr>dummy</addr>
        <len>1</len>
      </device>
    </command>
    <command name="kmget" protocmd="getkmtestaddr">
      <description>Testabfrage, KM-Bus EEProm Adresse eingeben</description>
      <device ID="2053">
        <addr>dummy</addr>
        <len>1</len>
      </device>
    </command>
    <command name="getExtBA" protocmd="getxaddr">
      <description>Ermittle Zustand der externen Betriebsartenumschaltung</description>
      <device ID="2053">
        <addr>00</addr>
        <len>1</len>
        <bit>0</bit>
        <unit>BST</unit>
      </device>
    </command>
    <command name="getPumpeSollM1" protocmd="getaddr">
      <description>Ermittle Solldrehzahl Pumpe M1</description>
      <device ID="2053">
        <addr>B0</addr>
        <len>1</len>
        <unit>PR1</unit>
      </device>
    </command>
    <command name="getVentilStatus" protocmd="getpaddr">
      <description>Ermittle Zustand Umschaltventil WW/Heizen</description>
      <device ID="2053">
        <addr>01</addr>
        <len>1</len>
        <bit>1</bit>
        <unit>BST</unit>
      </device>
    </command>
    <command name="getUmschaltventil" protocmd="getaddr">
      <addr>0A10</addr>
      <len>1</len>
      <unit>USV</unit>
      <description>Ermittle den Status des Umschaltventils WW/Heizen</description>
      <device ID="2098"/>
      <device ID="2053">
        <addr>01</addr>
        <len>1</len>
        <bit>1</bit>
        <unit>BST</unit>
      </device>
    </command>
    <command name="setUmschaltventil" protocmd="setaddr">
      <addr>0A10</addr>
      <len>1</len>
      <unit>USV</unit>
      <description>Setze den Status des Umschaltventils WW/Heizen</description>
      <device ID="2098"/>
      <device ID="2053">
        <addr>01</addr>
        <len>1</len>
        <bit>1</bit>
        <unit>BST</unit>
      </device>
    </command>
    <command name="getHKPTyp" protocmd="geteaddr">
      <description>Ermittle Typ der Heizkreispumpe</description>
      <device ID="2053">
        <addr>05</addr>
        <len>1</len>
        <bit>5</bit>
        <unit>HKT</unit>
      </device>
    </command>
    <command name="getTempRueck" protocmd="getaddr">
      <description>Ermittle die Rueckauftemepratur</description>
      <device ID="2053">
        <addr>44</addr>
        <len>1</len>
        <bit>5</bit>
        <unit>UT1</unit>
      </device>
    </command>
  </commands>
</vito>
07070100000042000041ED000028240000003200000002607BF44800000000000000000000000000000000000000000000002E00000000vcontrold-v0.98.10+git20210418.977e6f5/xml/kw07070100000043000081A4000028240000003200000001607BF448000035B9000000000000000000000000000000000000003C00000000vcontrold-v0.98.10+git20210418.977e6f5/xml/kw/vcontrold.xml<?xml version="1.0"?>
<V-Control xmlns:vcontrol="http://www.openv.de/vcontrol">
  <unix>
    <config>
      <serial>
        <tty>192.168.1.153:3002</tty>
      </serial>
      <net>
        <port>3002</port>
      </net>
      <logging>
        <file>/tmp/vcontrold.log</file>
        <syslog>n</syslog>
        <debug>n</debug>
      </logging>
      <device ID="2053"/>
    </config>
  </unix>
  <units>
    <unit name="Temperatur">
      <abbrev>UT</abbrev>
      <calc get="V/10" set="V*10"/>
      <type>short</type>
      <entity>Grad Celsius</entity>
    </unit>
    <unit name="Neigung">
      <abbrev>UN</abbrev>
      <calc get="V/10" set="V*10"/>
      <type>short</type>
      <entity/>
    </unit>
    <unit name="Temperatur 1Byte">
      <abbrev>UT1</abbrev>
      <calc get="V/2" set="V*2"/>
      <type>char</type>
      <entity>Grad Celsius</entity>
    </unit>
    <unit name="Temperatur 1Byte unsigned">
      <abbrev>UT1U</abbrev>
      <calc get="V/2" set="V*2"/>
      <type>uchar</type>
      <entity>Grad Celsius</entity>
    </unit>
    <unit name="Temperatur 1Byte ganzzahlig">
      <abbrev>UTI</abbrev>
      <calc get="V" set="V"/>
      <type>uchar</type>
      <entity>Grad Celsius</entity>
    </unit>
    <unit name="Status">
      <abbrev>ST</abbrev>
      <calc get="V" set="V"/>
      <type>char</type>
      <entity/>
    </unit>
    <unit name="Counter">
      <abbrev>CO</abbrev>
      <calc get="V" set="V"/>
      <type>int</type>
      <entity/>
    </unit>
    <unit name="Prozent">
      <abbrev>PR</abbrev>
      <calc get="V/2" set="V*2"/>
      <type>short</type>
      <entity>%</entity>
    </unit>
    <unit name="Prozent 1 Byte ganzzahlig">
      <abbrev>PR1</abbrev>
      <calc get="V" set="V"/>
      <type>uchar</type>
      <entity>%</entity>
    </unit>
    <unit name="CounterS">
      <abbrev>CS</abbrev>
      <calc get="V/3600" set="V*3600"/>
      <type>uint</type>
      <entity>Stunden</entity>
    </unit>
    <unit name="CycleTime">
      <abbrev>CT</abbrev>
      <type>cycletime</type>
    </unit>
    <unit name="ReturnStatus">
      <abbrev>RT</abbrev>
      <type>enum</type>
      <enum bytes="00" text="0"/>
      <enum bytes="01" text="1"/>
      <enum text="NOT OK"/>
    </unit>
    <unit name="BetriebsArt">
      <abbrev>BA</abbrev>
      <type>enum</type>
      <enum bytes="00" text="WW"/>
      <enum bytes="01" text="RED"/>
      <enum bytes="02" text="NORM"/>
      <enum bytes="04" text="H+WW"/>
      <enum bytes="03" text="H+WW"/>
      <enum bytes="05" text="ABSCHALT"/>
      <enum text="UNKNOWN"/>
    </unit>
    <unit name="SetReturnStatus">
      <abbrev>SR</abbrev>
      <type>enum</type>
      <enum bytes="00" text="OK"/>
      <enum bytes="05" text="SYNC (NOT OK)"/>
      <enum text="NOT OK"/>
    </unit>
    <unit name="SystemTime">
      <abbrev>TI</abbrev>
      <type>systime</type>
    </unit>
    <unit name="ErrorState">
      <abbrev>ES</abbrev>
      <type>errstate</type>
      <enum bytes="00" text="Regelbetrieb (kein Fehler)"/>
      <enum bytes="0F" text="Wartung (fuer Reset Codieradresse 24 auf 0 stellen)"/>
      <enum bytes="10" text="Kurzschluss Aussentemperatursensor"/>
      <enum bytes="18" text="Unterbrechung Aussentemperatursensor"/>
      <enum bytes="20" text="Kurzschluss Vorlauftemperatursensor"/>
      <enum bytes="21" text="Kurzschluss Ruecklauftemperatursensor"/>
      <enum bytes="28" text="Unterbrechung Aussentemperatursensor"/>
      <enum bytes="29" text="Unterbrechung Ruecklauftemperatursensor"/>
      <enum bytes="30" text="Kurzschluss Kesseltemperatursensor"/>
      <enum bytes="38" text="Unterbrechung Kesseltemperatursensor"/>
      <enum bytes="40" text="Kurzschluss Vorlauftemperatursensor M2"/>
      <enum bytes="42" text="Unterbrechung Vorlauftemperatursensor M2"/>
      <enum bytes="50" text="Kurzschluss Speichertemperatursensor"/>
      <enum bytes="58" text="Unterbrechung Speichertemperatursensor"/>
      <enum bytes="92" text="Solar: Kurzschluss Kollektortemperatursensor"/>
      <enum bytes="93" text="Solar: Kurzschluss Sensor S3"/>
      <enum bytes="94" text="Solar: Kurzschluss Speichertemperatursensor"/>
      <enum bytes="9A" text="Solar: Unterbrechung Kollektortemperatursensor"/>
      <enum bytes="9B" text="Solar: Unterbrechung Sensor S3"/>
      <enum bytes="9C" text="Solar: Unterbrechung Speichertemperatursensor"/>
      <enum bytes="9F" text="Solar: Fehlermeldung Solarteil (siehe Solarregler)"/>
      <enum bytes="A7" text="Bedienteil defekt"/>
      <enum bytes="B0" text="Kurzschluss Abgastemperatursensor"/>
      <enum bytes="B1" text="Kommunikationsfehler Bedieneinheit"/>
      <enum bytes="B4" text="Interner Fehler (Elektronik)"/>
      <enum bytes="B5" text="Interner Fehler (Elektronik)"/>
      <enum bytes="B6" text="Ungueltige Hardwarekennung (Elektronik)"/>
      <enum bytes="B7" text="Interner Fehler (Kesselkodierstecker)"/>
      <enum bytes="B8" text="Unterbrechung Abgastemperatursensor"/>
      <enum bytes="B9" text="Interner Fehler (Dateneingabe wiederholen)"/>
      <enum bytes="BA" text="Kommunikationsfehler Erweiterungssatz fuer Mischerkreis M2"/>
      <enum bytes="BC" text="Kommunikationsfehler Fernbedienung Vitorol, Heizkreis M1"/>
      <enum bytes="BD" text="Kommunikationsfehler Fernbedienung Vitorol, Heizkreis M2"/>
      <enum bytes="BE" text="Falsche Codierung Fernbedienung Vitorol"/>
      <enum bytes="C1" text="Externe Sicherheitseinrichtung (Kessel kuehlt aus)"/>
      <enum bytes="C2" text="Kommunikationsfehler Solarregelung"/>
      <enum bytes="C5" text="Kommunikationsfehler drehzahlgeregelte Heizkreispumpe, Heizkreis M1"/>
      <enum bytes="C6" text="Kommunikationsfehler drehzahlgeregelte Heizkreispumpe, Heizkreis M2"/>
      <enum bytes="C7" text="Falsche Codierung der Heizkreispumpe"/>
      <enum bytes="C9" text="Stoermeldeeingang am Schaltmodul-V aktiv"/>
      <enum bytes="CD" text="Kommunikationsfehler Vitocom 100 (KM-BUS)"/>
      <enum bytes="CE" text="Kommunikationsfehler Schaltmodul-V"/>
      <enum bytes="CF" text="Kommunikationsfehler LON Modul"/>
      <enum bytes="D1" text="Brennerstoerung"/>
      <enum bytes="D4" text="Sicherheitstemperaturbegrenzer hat ausgeloest oder Stoermeldemodul nicht richtig gesteckt"/>
      <enum bytes="DA" text="Kurzschluss Raumtemperatursensor, Heizkreis M1"/>
      <enum bytes="DB" text="Kurzschluss Raumtemperatursensor, Heizkreis M2"/>
      <enum bytes="DD" text="Unterbrechung Raumtemperatursensor, Heizkreis M1"/>
      <enum bytes="DE" text="Unterbrechung Raumtemperatursensor, Heizkreis M2"/>
      <enum bytes="E4" text="Fehler Versorgungsspannung"/>
      <enum bytes="E5" text="Interner Fehler (Ionisationselektrode)"/>
      <enum bytes="E6" text="Abgas- / Zuluftsystem verstopft"/>
      <enum bytes="F0" text="Interner Fehler (Regelung tauschen)"/>
      <enum bytes="F1" text="Abgastemperaturbegrenzer ausgeloest"/>
      <enum bytes="F2" text="Temperaturbegrenzer ausgeloest"/>
      <enum bytes="F3" text="Flammensigal beim Brennerstart bereits vorhanden"/>
      <enum bytes="F4" text="Flammensigal nicht vorhanden"/>
      <enum bytes="F7" text="Differenzdrucksensor defekt"/>
      <enum bytes="F8" text="Brennstoffventil schliesst zu spaet"/>
      <enum bytes="F9" text="Geblaesedrehzahl beim Brennerstart zu niedrig"/>
      <enum bytes="FA" text="Geblaesestillstand nicht erreicht"/>
      <enum bytes="FD" text="Fehler Gasfeuerungsautomat"/>
      <enum bytes="FE" text="Starkes Stoerfeld (EMV) in der Naehe oder Elektronik defekt"/>
      <enum bytes="FF" text="Starkes Stoerfeld (EMV) in der Naehe oder interner Fehler"/>
      <enum text="UNKNOWN"/>
    </unit>
    <unit name="DeviceType">
      <abbrev>DT</abbrev>
      <type>enum</type>
      <enum bytes="20 53 01 2B" text="V200WB2 ID=2053 Protokoll:GWG_VBEM"/>
      <enum bytes="20 94" text="V200KW1 ID=2094 Protokoll:KW"/>
      <enum bytes="20 98" text="V200KW2 ID=2098 Protokoll:KW"/>
      <enum text="UNKNOWN"/>
    </unit>
    <unit name="Brenner Stunden">
      <abbrev>BH</abbrev>
      <type>uchar</type>
      <calc get="(B1 * 100)+B0"/>
      <entity>h</entity>
    </unit>
    <unit name="ErrorStateGWG">
      <abbrev>ESG</abbrev>
      <type>enum</type>
      <enum bytes="00" text="Keine Stoerung"/>
      <enum bytes="02" text="Fehler Sicherheitskette"/>
      <enum bytes="04" text="Brennerstoerung 04"/>
      <enum bytes="05" text="Brennerstoerung 05"/>
      <enum bytes="07" text="Brennerstoerung 07"/>
      <enum bytes="08" text="Brennerstoerung 09"/>
      <enum bytes="08" text="Brennerstoerung 09"/>
      <enum bytes="0A" text="Brennerstoerung 10"/>
      <enum text="UNKNOWN"/>
    </unit>
    <unit name="Bitstatus">
      <abbrev>BST</abbrev>
      <type>uchar</type>
      <icalc get="(B0 &amp; (0x01&lt;&lt;BP))&gt;&gt; BP"/>
      <enum bytes="00" text="0"/>
      <enum bytes="01" text="1"/>
      <enum text="UNKNOWN"/>
    </unit>
    <unit name="HKP Pumpentyp">
      <abbrev>HKT</abbrev>
      <type>uchar</type>
      <icalc get="(B0 &amp; (0x01&lt;&lt;BP))&gt;&gt; BP"/>
      <enum bytes="00" text="stufig"/>
      <enum bytes="01" text="drehzahlgeregelt"/>
      <enum text="UNKNOWN"/>
    </unit>
  </units>
  <protocols>
    <protocol name="KW2">
      <macros>
        <macro name="SYNC">
          <command>SEND 04;WAIT 05</command>
        </macro>
        <macro name="GETADDR">
          <command>SEND 01 F7</command>
        </macro>
        <macro name="SETADDR">
          <command>SEND 01 F4</command>
        </macro>
      </macros>
      <commands>
        <command name="getaddr">
          <send>SYNC;GETADDR $addr $hexlen;RECV $len $unit</send>
        </command>
        <command name="setaddr">
          <send>SYNC;SETADDR $addr $hexlen;SEND BYTES $unit;RECV 1 SR</send>
        </command>
      </commands>
    </protocol>
    <protocol name="GWG">
      <macros>
        <macro name="SYNC">
          <command>SEND 04;WAIT 05</command>
        </macro>
        <macro name="GETADDR">
          <command>SEND 01 CB</command>
        </macro>
        <macro name="GETBADDR">
          <command>SEND 01 9E</command>
        </macro>
        <macro name="GETVADDR">
          <command>SEND 01 C7</command>
        </macro>
        <macro name="GETPADDR">
          <command>SEND 01 6E</command>
        </macro>
        <macro name="GETEADDR">
          <command>SEND 01 AE</command>
        </macro>
        <macro name="GETXADDR">
          <command>SEND 01 C5</command>
        </macro>
        <macro name="GETKMADDR">
          <command>SEND 01 43</command>
        </macro>
      </macros>
      <commands>
        <command name="getaddr">
          <retry>3</retry>
          <recvTimeout>150</recvTimeout>
          <send>SYNC;GETADDR $addr $hexlen 04;RECV $len $unit</send>
        </command>
        <command name="gettestaddr">
          <retry>3</retry>
          <recvTimeout>150</recvTimeout>
          <send>SYNC;GETADDR;SEND BYTES;SEND 01 04;RECV 1</send>
        </command>
        <command name="getbaddr">
          <retry>3</retry>
          <recvTimeout>150</recvTimeout>
          <send>SYNC;GETBADDR $addr $hexlen 04;RECV $len $unit</send>
        </command>
        <command name="getbtestaddr">
          <retry>3</retry>
          <recvTimeout>150</recvTimeout>
          <send>SYNC;GETBADDR;SEND BYTES;SEND 01 04;RECV 1</send>
        </command>
        <command name="getpaddr">
          <retry>3</retry>
          <recvTimeout>150</recvTimeout>
          <send>SYNC;GETPADDR $addr $hexlen 04;RECV $len $unit</send>
        </command>
        <command name="getptestaddr">
          <retry>3</retry>
          <recvTimeout>150</recvTimeout>
          <send>SYNC;GETPADDR;SEND BYTES;SEND 01 04;RECV 1</send>
        </command>
        <command name="getvaddr">
          <retry>3</retry>
          <recvTimeout>150</recvTimeout>
          <send>SYNC;GETVADDR $addr $hexlen 04;RECV $len $unit</send>
        </command>
        <command name="getvtestaddr">
          <retry>3</retry>
          <recvTimeout>150</recvTimeout>
          <send>SYNC;GETVADDR;SEND BYTES;SEND 01 04;RECV 1</send>
        </command>
        <command name="geteaddr">
          <retry>3</retry>
          <recvTimeout>150</recvTimeout>
          <send>SYNC;GETEADDR $addr $hexlen 04;RECV $len $unit</send>
        </command>
        <command name="getetestaddr">
          <retry>3</retry>
          <recvTimeout>150</recvTimeout>
          <send>SYNC;GETEADDR;SEND BYTES;SEND 01 04;RECV 1</send>
        </command>
        <command name="getxaddr">
          <retry>3</retry>
          <recvTimeout>150</recvTimeout>
          <send>SYNC;GETXADDR $addr $hexlen 04;RECV $len $unit</send>
        </command>
        <command name="getxtestaddr">
          <retry>3</retry>
          <recvTimeout>150</recvTimeout>
          <send>SYNC;GETXADDR;SEND BYTES;SEND 01 04;RECV 1</send>
        </command>
        <command name="getkmaddr">
          <send>SYNC;GETKMDDR $addr $hexlen 04;RECV $len $unit</send>
          <retry>3</retry>
          <recvTimeout>150</recvTimeout>
        </command>
        <command name="getkmtestaddr">
          <retry>3</retry>
          <recvTimeout>150</recvTimeout>
          <send>SYNC;GETKMADDR;SEND BYTES;SEND 01 04;RECV 1</send>
        </command>
        <command name="init">
          <send>SEND 16 00 00;PAUSE 100;SEND 16 00 00;PAUSE 100;SEND 16 00 00;PAUSE 100;SEND 16 00 00;PAUSE 100</send>
        </command>
        <command name="setaddr">
          <send>SYNC;RECV 1</send>
        </command>
      </commands>
    </protocol>
  </protocols>
  <extern xmlns:xi="http://www.w3.org/2003/XInclude">
    <xi:include href="vito.xml" parse="xml"/>
  </extern>
</V-Control>
07070100000044000081A4000028240000003200000001607BF44800008755000000000000000000000000000000000000003700000000vcontrold-v0.98.10+git20210418.977e6f5/xml/kw/vito.xml<?xml version="1.0"?>
<vito>
  <devices>
    <device ID="2098" name="V200KW2" protocol="KW2"/>
    <device ID="2053" name="GWG_VBEM" protocol="GWG"/>
  </devices>
  <commands>
    <command name="getTempA" protocmd="getaddr">
      <addr>0800</addr>
      <len>2</len>
      <unit>UT</unit>
      <error>05 05</error>
      <description>Ermittle die Aussentemeratur in Grad C</description>
      <device ID="2053">
        <addr>6F</addr>
        <unit>UT1</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getTempWWist" protocmd="getaddr">
      <addr>0804</addr>
      <len>2</len>
      <unit>UT</unit>
      <error>05 05</error>
      <description>Ermittle die Warmwassertemperatur in Grad C</description>
      <device ID="2053">
        <addr>42</addr>
        <unit>UT1U</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getTempWWsoll" protocmd="getaddr">
      <addr>6300</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Ermittle die Warmwassersolltemperatur in Grad C</description>
      <device ID="2053">
        <addr>5C</addr>
        <unit>UT1</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getTempKist" protocmd="getaddr">
      <addr>0802</addr>
      <len>2</len>
      <unit>UT</unit>
      <error>05 05</error>
      <description>Ermittle die Kesseltemperatur in Grad C</description>
      <device ID="2053">
        <addr>70</addr>
        <unit>UT1</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getTempKsoll" protocmd="getaddr">
      <addr>5502</addr>
      <len>2</len>
      <unit>UT</unit>
      <error>05 05</error>
      <description>Ermittle die Kesselsolltemperatur in Grad C</description>
      <device ID="2053">
        <addr>71</addr>
        <unit>UT1</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getTempVListM1" protocmd="getaddr">
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Vorlauftemperatur M1 in Grad C</description>
      <device ID="2053"/>
    </command>
    <command name="getTempVListM2" protocmd="getaddr">
      <addr>080C</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Vorlauftemperatur M2 in Grad C</description>
      <device ID="2053">
        <addr>41</addr>
        <unit>UT1</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getTempVLsollM1" protocmd="getaddr">
      <addr>2544</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Vorlaufsolltemperatur M1 in Grad C</description>
      <device ID="2053">
        <addr>69</addr>
        <unit>UT1</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getTempVLsollM2" protocmd="getaddr">
      <addr>3544</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Vorlaufsolltemperatur M2 in Grad C</description>
      <device ID="2053">
        <addr>63</addr>
        <unit>UT1</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getTempVLsollM3" protocmd="getaddr">
      <addr>4544</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Vorlaufsolltemperatur M3 in Grad C</description>
      <device ID="2053"/>
    </command>
    <command name="getTempKol" protocmd="getaddr">
      <addr>6564</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Kollektortemperatur in Grad C</description>
      <device ID="2053"/>
    </command>
    <command name="getTempSpu" protocmd="getaddr">
      <addr>6566</addr>
      <len>2</len>
      <unit>UT</unit>
      <description>Ermittle die Speichertemperatur unten in Grad C</description>
      <device ID="2053"/>
    </command>
    <command name="getTempRaumNorSollM1" protocmd="getaddr">
      <addr>2306</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Ermittle die Raumsolltemperatur normal M1 in Grad C</description>
      <device ID="2053">
        <addr>53</addr>
        <len>1</len>
        <unit>UTI</unit>
      </device>
    </command>
    <command name="setTempRaumNorSollM1" protocmd="setaddr">
      <addr>2306</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Setze die Raumsolltemperatur normal M1 in Grad C</description>
      <device ID="2053">
        <addr>53</addr>
        <len>1</len>
        <unit>UTI</unit>
      </device>
    </command>
    <command name="getTempRaumNorSollM2" protocmd="getaddr">
      <addr>3306</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Ermittle die Raumsolltemperatur normal M2 in Grad C</description>
      <device ID="2053"/>
    </command>
    <command name="getTempRaumRedSollM1" protocmd="getaddr">
      <addr>2307</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Ermittle die Raumsolltemperatur reduziert M1 in Grad C</description>
      <device ID="2053">
        <addr>54</addr>
        <len>1</len>
        <unit>UTI</unit>
      </device>
    </command>
    <command name="setTempRaumRedSollM1" protocmd="setaddr">
      <addr>2307</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Setze die Raumsolltemperatur reduziert M1 in Grad C</description>
      <device ID="2053">
        <addr>54</addr>
        <len>1</len>
        <unit>UTI</unit>
      </device>
    </command>
    <command name="getTempRaumRedSollM2" protocmd="getaddr">
      <addr>3307</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Ermittle die Raumsolltemperatur reduziert M2 in Grad C</description>
      <device ID="2053"/>
    </command>
    <command name="getBrennerStatus" protocmd="getaddr">
      <addr>551E</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Brennerstatus</description>
      <device ID="2053">
        <addr>22</addr>
        <len>1</len>
        <bit>1</bit>
        <unit>BST</unit>
      </device>
    </command>
    <command name="getBrennerStarts" protocmd="getaddr">
      <addr>088A</addr>
      <len>2</len>
      <unit>CO</unit>
      <description>Ermittle die Brennerstarts</description>
      <device ID="2053"/>
    </command>
    <command name="getBrennerStunden1" protocmd="getaddr">
      <addr>08A7</addr>
      <len>4</len>
      <unit>CS</unit>
      <description>Ermittle die Brennerstunden Stufe 1</description>
      <device ID="2053" protocmd="geteaddr">
        <addr>17</addr>
        <len>2</len>
        <unit>BH</unit>
      </device>
    </command>
    <command name="getBrennerStunden2" protocmd="getaddr">
      <addr>08AB</addr>
      <len>4</len>
      <unit>CS</unit>
      <description>Ermittle die Brennerstunden Stufe 2</description>
      <device ID="2053"/>
    </command>
    <command name="getPumpeStatusM1" protocmd="getaddr">
      <addr>2906</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Status der Pumpe M1</description>
      <device ID="2053" protocmd="getpaddr">
        <addr>01</addr>
        <len>1</len>
        <bit>0</bit>
        <unit>BST</unit>
      </device>
    </command>
    <command name="getPumpeStatusSp" protocmd="getaddr">
      <addr>0845</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Status der Speicherladepumpe</description>
      <device ID="2053"/>
    </command>
    <command name="getPumpeStatusZirku" protocmd="getaddr">
      <addr>0846</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Status der Zirkulationspumpe</description>
      <device ID="2053" protocmd="getpaddr">
        <addr>01</addr>
        <len>1</len>
        <bit>2</bit>
        <unit>BST</unit>
      </device>
    </command>
    <command name="getPumpeStatusSolar" protocmd="getaddr">
      <addr>6552</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Status der Zirkulationspumpe Solar</description>
      <device ID="2053"/>
    </command>
    <command name="getPumpeStatusM2" protocmd="getaddr">
      <addr>3906</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Status der Pumpe M2</description>
      <device ID="2053"/>
    </command>
    <command name="getMischerM1" protocmd="getaddr">
      <addr>254C</addr>
      <len>1</len>
      <unit>PR</unit>
      <description>Ermittle Mischerposition M1</description>
      <device ID="2053"/>
    </command>
    <command name="getMischerM2" protocmd="getaddr">
      <addr>354C</addr>
      <len>1</len>
      <unit>PR</unit>
      <description>Ermittle Mischerposition M2</description>
      <device ID="2053"/>
    </command>
    <command name="getMischerM3" protocmd="getaddr">
      <addr>454C</addr>
      <len>1</len>
      <unit>PR</unit>
      <description>Ermittle Mischerposition M3</description>
      <device ID="2053"/>
    </command>
    <command name="getSolarStatusWW" protocmd="getaddr">
      <addr>6551</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Ermittle den Status der Nachladeunterdrueckung</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM1Mo" protocmd="getaddr">
      <addr>2000</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Montag M1</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM1Di" protocmd="getaddr">
      <addr>2008</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Dienstag M1</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM1Mi" protocmd="getaddr">
      <addr>2010</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit MIttwoch M1</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM1Do" protocmd="getaddr">
      <addr>2018</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Donnerstag M1</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM1Fr" protocmd="getaddr">
      <addr>2020</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Freitag M1</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM1Sa" protocmd="getaddr">
      <addr>2028</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Samstag M1</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM1So" protocmd="getaddr">
      <addr>2030</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Sonntag M1</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM2Mo" protocmd="getaddr">
      <addr>3000</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Montag M2</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM2Di" protocmd="getaddr">
      <addr>3008</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Dienstag M2</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM2Mi" protocmd="getaddr">
      <addr>3010</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Mittwoch M2</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM2Do" protocmd="getaddr">
      <addr>3018</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Donnerstag M2</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM2Fr" protocmd="getaddr">
      <addr>3020</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Freitag M2</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM2Sa" protocmd="getaddr">
      <addr>3028</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Samstag M2</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerM2So" protocmd="getaddr">
      <addr>3030</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Sonntag M2</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerWWMo" protocmd="getaddr">
      <addr>2100</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Montag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerWWDi" protocmd="getaddr">
      <addr>2108</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Dienstag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerWWMi" protocmd="getaddr">
      <addr>2110</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Mittwoch Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerWWDo" protocmd="getaddr">
      <addr>2118</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Donnerstag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerWWFr" protocmd="getaddr">
      <addr>2120</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Freitag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerWWSa" protocmd="getaddr">
      <addr>2128</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Samstag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerWWSo" protocmd="getaddr">
      <addr>2130</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Sonntag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerZirkuMo" protocmd="getaddr">
      <addr>2200</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Montag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerZirkuDi" protocmd="getaddr">
      <addr>2208</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Dienstag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerZirkuMi" protocmd="getaddr">
      <addr>2210</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Mittwoch Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerZirkuDo" protocmd="getaddr">
      <addr>2218</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Donnerstag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerZirkuFr" protocmd="getaddr">
      <addr>2220</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Freitag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerZirkuSa" protocmd="getaddr">
      <addr>2228</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Samstag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="getTimerZirkuSo" protocmd="getaddr">
      <addr>2230</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Sonntag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="getBetriebArtM1" protocmd="getaddr">
      <addr>2301</addr>
      <len>1</len>
      <unit>BA</unit>
      <description>Betriebsart M1</description>
      <device ID="2053">
        <addr>51</addr>
        <len>1</len>
        <unit>BA</unit>
      </device>
    </command>
    <command name="getBetriebArtM2" protocmd="getaddr">
      <addr>3301</addr>
      <len>1</len>
      <unit>BA</unit>
      <description>Betriebsart M2</description>
      <device ID="2053"/>
    </command>
    <command name="getBetriebSparM1" protocmd="getaddr">
      <addr>2302</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Betriebsart Spar M1</description>
      <device ID="2053"/>
    </command>
    <command name="getBetriebSparM2" protocmd="getaddr">
      <addr>3302</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Betriebsart Spar M2</description>
      <device ID="2053"/>
    </command>
    <command name="getBetriebPartyM1" protocmd="getaddr">
      <addr>2303</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Betriebsart Party M1</description>
      <device ID="2053"/>
    </command>
    <command name="getBetriebPartyM2" protocmd="getaddr">
      <addr>3303</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Betriebsart Party M2</description>
      <device ID="2053"/>
    </command>
    <command name="getSolarStunden" protocmd="getaddr">
      <addr>6568</addr>
      <len>2</len>
      <unit>CO</unit>
      <description>Solar Betriebsstunden</description>
      <device ID="2053"/>
    </command>
    <command name="getSolarLeistung" protocmd="getaddr">
      <addr>6560</addr>
      <len>4</len>
      <unit>CO</unit>
      <description>Solar Leistung Gesamt</description>
      <device ID="2053"/>
    </command>
    <command name="getStatusFrostM1" protocmd="getaddr">
      <addr>2500</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Status Frostwarnung M1</description>
      <device ID="2053"/>
    </command>
    <command name="getStatusFrostM2" protocmd="getaddr">
      <addr>3500</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Status Frostwarnung M2</description>
      <device ID="2053"/>
    </command>
    <command name="getStatusStoerung" protocmd="getaddr">
      <addr>7579</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Status Sammelstoerung</description>
      <device ID="2053"/>
    </command>
    <command name="getTempPartyM1" protocmd="getaddr">
      <addr>2308</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Solltemperatur Partybetrieb M1</description>
      <device ID="2053"/>
    </command>
    <command name="getTempPartyM2" protocmd="getaddr">
      <addr>3308</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Solltemperatur Partybetrieb M2</description>
      <device ID="2053"/>
    </command>
    <command name="getSystemTime" protocmd="getaddr">
      <addr>088E</addr>
      <len>8</len>
      <unit>TI</unit>
      <description>Ermittle Systemzeit</description>
      <device ID="2053"/>
    </command>
    <command name="setSystemTime" protocmd="setaddr">
      <addr>088E</addr>
      <len>8</len>
      <unit>TI</unit>
      <description>Setze Systemzeit</description>
      <device ID="2053"/>
    </command>
    <command name="setTempWWsoll" protocmd="setaddr">
      <addr>6300</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Setze die Warmwassersolltemperatur in Grad C</description>
      <device ID="2053"/>
    </command>
    <command name="setTempPartyM1" protocmd="setaddr">
      <addr>2308</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Setze die Warmwassersolltemperatur Party M1 in Grad C</description>
      <device ID="2053"/>
    </command>
    <command name="setTempPartyM2" protocmd="setaddr">
      <addr>3308</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Setze die Warmwassersolltemperatur Party M2 in Grad C</description>
      <device ID="2053"/>
    </command>
    <command name="setBetriebArtM1" protocmd="setaddr">
      <addr>2301</addr>
      <len>1</len>
      <unit>BA</unit>
      <description>Setze Betriebsart M1</description>
      <device ID="2053">
        <addr>51</addr>
        <len>1</len>
        <unit>BA</unit>
      </device>
    </command>
    <command name="setBetriebSparM1" protocmd="setaddr">
      <addr>2302</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Setze Betriebsart Spar M1</description>
      <device ID="2053"/>
    </command>
    <command name="setBetriebPartyM1" protocmd="setaddr">
      <addr>2303</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Setze Betriebsart Party M1</description>
      <device ID="2053"/>
    </command>
    <command name="setBetriebPartyM2" protocmd="setaddr">
      <addr>3303</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Setze Betriebsart Party M2</description>
      <device ID="2053"/>
    </command>
    <command name="setPumpeStatusZirku" protocmd="setaddr">
      <addr>0846</addr>
      <len>1</len>
      <unit>RT</unit>
      <description>Setze den Status der Zirkulationspumpe</description>
      <device ID="2053">
        <addr>01</addr>
        <len>1</len>
        <bit>2</bit>
        <precommand>getPumpeStatusZirku</precommand>
        <unit>BA</unit>
      </device>
    </command>
    <command name="setTimerM1Mo" protocmd="setaddr">
      <addr>2000</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Montag M1</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM1Di" protocmd="setaddr">
      <addr>2008</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Dienstag M1</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM1Mi" protocmd="setaddr">
      <addr>2010</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Mittwoch M1</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM1Do" protocmd="setaddr">
      <addr>2018</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Donnerstag M1</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM1Fr" protocmd="setaddr">
      <addr>2020</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Freitag M1</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM1Sa" protocmd="setaddr">
      <addr>2028</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Samstag M1</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM1So" protocmd="setaddr">
      <addr>2030</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Sonntag M1</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM2Mo" protocmd="setaddr">
      <addr>3000</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Montag M2</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM2Di" protocmd="setaddr">
      <addr>3008</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Dienstag M2</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM2Mi" protocmd="setaddr">
      <addr>3010</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Mittwoch M2</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM2Do" protocmd="setaddr">
      <addr>3018</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Donnerstag M2</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM2Fr" protocmd="setaddr">
      <addr>3020</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Freitag M2</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM2Sa" protocmd="setaddr">
      <addr>3028</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Samstag M2</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerM2So" protocmd="setaddr">
      <addr>3030</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Sonntag M2</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerWWMo" protocmd="setaddr">
      <addr>2100</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Montag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerWWDi" protocmd="setaddr">
      <addr>2108</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Dienstag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerWWMi" protocmd="setaddr">
      <addr>2110</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Mittwoch Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerWWDo" protocmd="setaddr">
      <addr>2118</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Donnerstag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerWWFr" protocmd="setaddr">
      <addr>2120</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Freitag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerWWSa" protocmd="setaddr">
      <addr>2128</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Samstag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerWWSo" protocmd="setaddr">
      <addr>2130</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Sonntag Warmwasser</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerZirkuMo" protocmd="setaddr">
      <addr>2200</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Montag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerZirkuDi" protocmd="setaddr">
      <addr>2208</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Dienstag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerZirkuMi" protocmd="setaddr">
      <addr>2210</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Mittwoch Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerZirkuDo" protocmd="setaddr">
      <addr>2218</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Donnerstag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerZirkuFr" protocmd="setaddr">
      <addr>2220</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Freitag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerZirkuSa" protocmd="setaddr">
      <addr>2228</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Samstag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="setTimerZirkuSo" protocmd="setaddr">
      <addr>2230</addr>
      <len>8</len>
      <unit>CT</unit>
      <description>Schaltzeit Sonntag Zirku</description>
      <device ID="2053"/>
    </command>
    <command name="getError0" protocmd="getaddr">
      <addr>7507</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 1</description>
      <device ID="2053">
        <addr>3F</addr>
        <len>1</len>
        <unit>ESG</unit>
      </device>
    </command>
    <command name="getError1" protocmd="getaddr">
      <addr>7510</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 2</description>
      <device ID="2053"/>
    </command>
    <command name="getError2" protocmd="getaddr">
      <addr>7519</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 3</description>
      <device ID="2053"/>
    </command>
    <command name="getError3" protocmd="getaddr">
      <addr>7522</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 4</description>
      <device ID="2053"/>
    </command>
    <command name="getError4" protocmd="getaddr">
      <addr>752B</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 5</description>
      <device ID="2053"/>
    </command>
    <command name="getError5" protocmd="getaddr">
      <addr>7534</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 6</description>
      <device ID="2053"/>
    </command>
    <command name="getError6" protocmd="getaddr">
      <addr>753D</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 7</description>
      <device ID="2053"/>
    </command>
    <command name="getError7" protocmd="getaddr">
      <addr>7546</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 8</description>
      <device ID="2053"/>
    </command>
    <command name="getError8" protocmd="getaddr">
      <addr>754F</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 9</description>
      <device ID="2053"/>
    </command>
    <command name="getError9" protocmd="getaddr">
      <addr>7558</addr>
      <len>9</len>
      <unit>ES</unit>
      <description>Ermittle Fehlerhistory Eintrag 10</description>
      <device ID="2053"/>
    </command>
    <command name="getNeigungM1" protocmd="getaddr">
      <addr>2305</addr>
      <len>1</len>
      <unit>UN</unit>
      <description>Ermittle Neigung Heizkennlinie M1</description>
      <device ID="2053">
        <addr>65</addr>
        <unit>UN</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getNeigungM2" protocmd="getaddr">
      <addr>3305</addr>
      <len>1</len>
      <unit>UN</unit>
      <description>Ermittle Neigung Heizkennlinie M2</description>
      <device ID="2053">
        <addr>C4</addr>
        <unit>UN</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getNiveauM1" protocmd="getaddr">
      <addr>2304</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Ermittle Niveau Heizkennlinie M1</description>
      <device ID="2053">
        <addr>64</addr>
        <unit>ST</unit>
        <len>1</len>
      </device>
    </command>
    <command name="getNiveauM2" protocmd="getaddr">
      <addr>3304</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Ermittle Niveau Heizkennlinie M2</description>
      <device ID="2053">
        <addr>C5</addr>
        <unit>ST</unit>
        <len>1</len>
      </device>
    </command>
    <command name="setNeigungM1" protocmd="setaddr">
      <addr>2305</addr>
      <len>1</len>
      <unit>UN</unit>
      <description>Setze Neigung Heizkennlinie M1</description>
      <device ID="2053"/>
    </command>
    <command name="setNeigungM2" protocmd="setaddr">
      <addr>3305</addr>
      <len>1</len>
      <unit>UN</unit>
      <description>Setze Neigung Heizkennlinie M2</description>
      <device ID="2053"/>
    </command>
    <command name="setNiveauM1" protocmd="setaddr">
      <addr>2304</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Setze Niveau Heizkennlinie M1</description>
      <device ID="2053"/>
    </command>
    <command name="setNiveauM2" protocmd="setaddr">
      <addr>3304</addr>
      <len>1</len>
      <unit>ST</unit>
      <description>Setze Niveau Heizkennlinie M2</description>
      <device ID="2053"/>
    </command>
    <command name="getDevType" protocmd="getaddr">
      <addr>00F8</addr>
      <len>2</len>
      <unit>DT</unit>
      <description>Ermittle Device Typ der Anlage</description>
      <device ID="2053" protocmd="getvaddr">
        <addr>F8</addr>
        <len>4</len>
        <error>05 05 05 05</error>
      </device>
    </command>
    <command name="init" protocmd="getaddr">
      <description>Inintialisierung</description>
      <device ID="2053" protocmd="init">
        <addr>dummy</addr>
      </device>
    </command>
    <command name="get" protocmd="gettestaddr">
      <description>Testabfrage, Adresse eingeben</description>
      <device ID="2053">
        <addr>dummy</addr>
        <len>1</len>
      </device>
    </command>
    <command name="vget" protocmd="getvtestaddr">
      <description>Testabfrage, Virtuelle Adresse eingeben</description>
      <device ID="2053">
        <addr>dummy</addr>
        <len>1</len>
      </device>
    </command>
    <command name="bget" protocmd="getbtestaddr">
      <description>Testabfrage, Bedienteil Adresse eingeben</description>
      <device ID="2053">
        <addr>dummy</addr>
        <len>1</len>
      </device>
    </command>
    <command name="pget" protocmd="getptestaddr">
      <description>Testabfrage, Port Adresse eingeben</description>
      <device ID="2053">
        <addr>dummy</addr>
        <len>1</len>
      </device>
    </command>
    <command name="eget" protocmd="getetestaddr">
      <description>Testabfrage, EEProm Adresse eingeben</description>
      <device ID="2053">
        <addr>dummy</addr>
        <len>1</len>
      </device>
    </command>
    <command name="xget" protocmd="getxtestaddr">
      <description>Testabfrage, XRAM Adresse eingeben</description>
      <device ID="2053">
        <addr>dummy</addr>
        <len>1</len>
      </device>
    </command>
    <command name="kmget" protocmd="getkmtestaddr">
      <description>Testabfrage, KM-Bus EEProm Adresse eingeben</description>
      <device ID="2053">
        <addr>dummy</addr>
        <len>1</len>
      </device>
    </command>
    <command name="getExtBA" protocmd="getxaddr">
      <description>Ermittle Zustand der externen Betriebsartenumschaltung</description>
      <device ID="2053">
        <addr>00</addr>
        <len>1</len>
        <bit>0</bit>
        <unit>BST</unit>
      </device>
    </command>
    <command name="getPumpeSollM1" protocmd="getaddr">
      <description>Ermittle Solldrehzahl Pumpe M1</description>
      <device ID="2053">
        <addr>B0</addr>
        <len>1</len>
        <unit>PR1</unit>
      </device>
    </command>
    <command name="getVentilStatus" protocmd="getpaddr">
      <description>Ermittle Zustand Umschaltventil WW/Heizen</description>
      <device ID="2053">
        <addr>01</addr>
        <len>1</len>
        <bit>1</bit>
        <unit>BST</unit>
      </device>
    </command>
    <command name="getHKPTyp" protocmd="geteaddr">
      <description>Ermittle Typ der Heizkreispumpe</description>
      <device ID="2053">
        <addr>05</addr>
        <len>1</len>
        <bit>5</bit>
        <unit>HKT</unit>
      </device>
    </command>
    <command name="getTempRueck" protocmd="getaddr">
      <description>Ermittle die Rueckauftemepratur</description>
      <device ID="2053">
        <addr>44</addr>
        <len>1</len>
        <bit>5</bit>
        <unit>UT1</unit>
      </device>
    </command>
  </commands>
</vito>
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!889 blocks
openSUSE Build Service is sponsored by