##############################################
# 00_THZ
# $Id: 00_THZ.pm 28264 2023-12-07 19:49:07Z immi $
# by immi 11/2023
my $thzversion = "0.210";
# this code is based on the hard work of Robert; I just tried to port it
# http://robert.penz.name/heat-pump-lwz/
########################################################################################
#
#  This programm 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 2 of the License, or
#  (at your option) any later version.
#
#  The GNU General Public License can be found at
#  http://www.gnu.org/copyleft/gpl.html.
#  A copy is found in the textfile GPL.txt and important notices to the license
#  from the author is found in LICENSE.txt distributed with these scripts.
#
#  This script 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.
#
########################################################################################


package main;
use strict;
use warnings;
use Time::HiRes qw(gettimeofday);
use SetExtensions;
use Blocking;
use DevIo;

sub THZ_Read($);
sub THZ_ReadAnswer($);
sub THZ_Ready($);
sub THZ_Write($$);
sub THZ_Parse1($$);
sub THZ_checksum($);
sub THZ_replacebytes($$$);
sub THZ_decode($);
sub THZ_overwritechecksum($);
sub THZ_encodecommand($$);
sub hex2int($);
sub quaters2time($);
sub time2quaters($);
sub THZ_debugread($);
sub THZ_GetRefresh($);
sub THZ_Refresh_all_gets($);
sub THZ_Get_Comunication($$);
sub THZ_PrintcurveSVG;
sub THZ_RemoveInternalTimer($);
sub THZ_Set($@);
sub function_heatSetTemp($$);
sub THZ_Get($@);
sub THZ_Val($;$$);

########################################################################################
#
# %parsinghash  - known type of message structure
# 
########################################################################################

my %parsinghash = (
  #msgtype => parsingrule  
  "01pxx206" => [["p37Fanstage1AirflowInlet: ", 4, 4, "hex", 1],	[" p38Fanstage2AirflowInlet: ", 8, 4, "hex", 1],	[" p39Fanstage3AirflowInlet: ", 12, 4, "hex", 1],
              [" p40Fanstage1AirflowOutlet: ", 16, 4, "hex", 1],	[" p41Fanstage2AirflowOutlet: ", 20, 4, "hex", 1],	[" p42Fanstage3AirflowOutlet: ", 24, 4, "hex", 1],
              [" p43UnschedVent3: ", 	       28, 4, "hex", 1],	[" p44UnschedVent2: ", 32, 4, "hex", 1],		[" p45UnschedVent1: ", 36, 4, "hex", 1],
              [" p46UnschedVent0: ", 	       40, 4, "hex", 1],	[" p75PassiveCooling: ", 44, 4, "hex", 1]
              ],
  "01pxx214" => [["p37Fanstage1AirflowInlet: ", 4, 2, "hex", 1],	[" p38Fanstage2AirflowInlet: ", 6, 2, "hex", 1],	[" p39Fanstage3AirflowInlet: ", 8, 2, "hex", 1],
	      [" p40Fanstage1AirflowOutlet: ", 10, 2, "hex", 1],	[" p41Fanstage2AirflowOutlet: ", 12, 2, "hex", 1],	[" p42Fanstage3AirflowOutlet: ", 14, 2, "hex", 1],
	      [" p43UnschedVent3: ", 	    16, 4, "hex", 1],		[" p44UnschedVent2: ", 20, 4, "hex", 1],		[" p45UnschedVent1: ", 24, 4, "hex", 1],
	      [" p46UnschedVent0: ", 	    28, 4, "hex", 1],		[" p75PassiveCooling: ", 32, 2, "hex", 1]
	      ],
  "03pxx206" => [["UpTempLimitDefrostEvaporatorEnd: ", 4, 4, "hex", 10],[" MaxTimeDefrostEvaporator: ", 8, 4, "hex", 1], 	[" LimitTempCondenserElectBoost: ", 12, 4, "hex", 10],
	      [" LimitTempCondenserDefrostTerm: ", 16, 4, "hex", 10],	[" p47CompressorRestartDelay: ", 20, 2, "hex", 1], 	[" p48MainFanSpeed: ", 22, 2, "hex", 1]
 	      ],
  "04pxx206" => [["MaxDefrostDurationAAExchenger: ", 4, 2, "hex", 1],	[" DefrostStartThreshold: ", 6, 4, "hex", 10],	[" VolumeFlowFilterReplacement: ", 10, 4, "hex", 1]
	      ],
  "05pxx206" => [["p13GradientHC1: ", 	4, 4, "hex", 10],	[" p14LowEndHC1: ", 8, 4, "hex", 10],			[" p15RoomInfluenceHC1: ",	12, 2, "hex", 10],
	      [" p16GradientHC2: ",	14, 4, "hex", 10],	[" p17LowEndHC2: ", 18, 4, "hex", 10],			[" p18RoomInfluenceHC2: ",	22, 2, "hex", 10],
	      [" p19FlowProportionHC1: ",24, 4, "hex", 1],	[" p20FlowProportionHC2: ", 28, 4, "hex", 1],		[" MaxSetHeatFlowTempHC1: ",	32, 4, "hex", 10],
	      [" MinSetHeatFlowTempHC1: ",36, 4, "hex", 10],	[" MaxSetHeatFlowTempHC2: ", 40, 4, "hex", 10],		[" MinSetHeatFlowTempHC2: ",	44, 4, "hex", 10],
	      ],
  "06pxx206" => [["p21Hyst1: ", 	 4, 2, "hex", 10],	[" p22Hyst2: ",			 6, 2, "hex", 10],	[" p23Hyst3: ", 		 8, 2, "hex", 10],
	      [" p24Hyst4: ", 		10, 2, "hex", 10],	[" p25Hyst5: ",			12, 2, "hex", 10],	[" p26Hyst6: ", 		14, 2, "hex", 10],
	      [" p27Hyst7: ", 		16, 2, "hex", 10],	[" p28Hyst8: ",			18, 2, "hex", 10],	[" p29HystAsymmetry: ",		20, 2, "hex", 1],
	      [" p30integralComponent: ",22, 4, "hex", 1],	[" p31MaxBoostStages: ",	26, 2, "hex", 1],	[" MaxHeatFlowTemp: ",		28, 4, "hex", 10],
	      [" p49SummerModeTemp: ", 	32, 4, "hex", 10],	[" p50SummerModeHysteresis: ",	36, 4, "hex", 10],	[" p77OutTempFilterTime: ",	40, 4, "hex", 1],
	      [" p78DualModePoint: ", 	44, 4, "hex2int", 10],	[" p79BoosterTimeoutHC: ",	48, 2, "hex", 1] 	      
          ],
  "07pxx206" => [["p32HystDHW: ", 	4, 2, "hex", 10],	[" p33BoosterTimeoutDHW: ",	6, 2, "hex", 1],	[" p34TempLimitBoostDHW: ",	8, 4, "hex2int", 10],	[" p35PasteurisationInterval: ", 12, 2, "hex", 1],
	      [" p36MaxDurationDHWLoad: ",14, 2, "hex", 1],	[" pasteurisationTemp: ",	16, 4, "hex", 10],	[" maxBoostStagesDHW: ",	20, 2, "hex", 1],
	      [" p84EnableDHWBuffer: ",	22, 2, "hex", 1]
	      ],
  "08pxx206" => [["p80EnableSolar: ", 	4, 2, "hex", 1],	[" p81DiffTempSolarLoading: ",	 6, 4, "hex", 10],	[" p82DelayCompStartSolar: ",	10, 2, "hex", 1],
	      [" p84DHWTempSolarMode: ",12, 4, "hex", 10],	[" HystDiffTempSolar: ",	16, 4, "hex", 10],	[" CollectLimitTempSolar: ",	20, 4, "hex", 10]
	      ],
  "09his" => [["compressorHeating: ", 	 4, 4,  "hex", 1],	[" compressorCooling: ",	 8, 4, "hex", 1],
	      [" compressorDHW: ",	12, 4, "hex", 1],	[" boosterDHW: ",		16, 4, "hex", 1],
	      [" boosterHeating: ",	20, 4, "hex", 1]
	      ],
  "09his206" => [["operatingHours1: ",	 4, 4, "hex", 1],	[" operatingHours2: ",	8, 4, "hex", 1],
	      [" heatingHours: ",	12, 4, "hex", 1],	[" DHWhours: ",		16, 4, "hex", 1],
	      [" coolingHours: ",	20, 4, "hex", 1]
	      ],
  "0Apxx206" => [["p54MinPumpCycles: ",	4, 2, "hex", 1],	[" p55MaxPumpCycles: ",	 6, 4, "hex", 1],	 [" p56OutTempMaxPumpCycles: ",	10, 4, "hex", 10],
	      [" p57OutTempMinPumpCycles: ",14, 4, "hex", 10],	[" p58SuppressTempCaptPumpStart: ", 18, 4, "hex", 1]
	      ],
  "0Bpxx206" => [["progHC1StartTime: ", 4, 4, "hex2time", 1], 	[" progHC1EndTime: ",	 8, 4, "hex2time", 1],
	      [" progHC1Monday: ",	13, 1, "bit0", 1],	[" progHC1Tuesday: ",	13, 1, "bit1", 1],	
		[" progHC1Wednesday: ",	13, 1, "bit2", 1],	[" progHC1Thursday: ",	13, 1, "bit3", 1],
		[" progHC1Friday: ",	12, 1, "bit0", 1],	[" progHC1Saturday: ",	12, 1, "bit1", 1],
		[" progHC1Sunday: ",	12, 1, "bit2", 1],	[" progHC1Enable: ",	14, 2, "hex", 1],
		[" progHC2StartTime: ",	16, 4, "hex2time", 1],	[" progHC2EndTime: ",	20, 4, "hex2time", 1],
		[" progHC2Monday: ", 	25, 1, "bit0", 1],	[" progHC2Tuesday: ",	25, 1, "bit1", 1],	
		[" progHC2Wednesday: ", 25, 1, "bit2", 1],	[" progHC2Thursday: ",	25, 1, "bit3", 1],
		[" progHC2Friday: ", 	24, 1, "bit0", 1],	[" progHC2Saturday: ",	24, 1, "bit1", 1],
		[" progHC2Sunday: ", 	24, 1, "bit2", 1],	[" progHC2Enable: ",	26, 2, "hex", 1]
	      ],
  "0Cpxx206" => [["progDHWStartTime: ",	 4, 4, "hex2time", 1],	[" progDHWEndTime: ",	 8, 4, "hex2time", 1],
		[" progDHWMonday: ",	13, 1, "bit0", 1],	[" progDHWTuesday: ",	13, 1, "bit1", 1],	
		[" progDHWWednesday: ",	13, 1, "bit2", 1],	[" progDHWThursday: ",	13, 1, "bit3", 1],
		[" progDHWFriday: ",	12, 1, "bit0", 1],	[" progDHWSaturday: ",	12, 1, "bit1", 1],
		[" progDHWSunday: ",	12, 1, "bit2", 1],	[" progDHWEnable: ",	14, 2, "hex", 1],
 	      ],
  "0Dpxx206" => [["progFAN1StartTime: ",4, 4, "hex2time", 1],	[" progFAN1EndTime: ",	 8, 4, "hex2time", 1],
		[" progFAN1Monday: ", 	13, 1, "bit0", 1],	[" progFAN1Tuesday: ",	13, 1, "bit1", 1],	
		[" progFAN1Wednesday: ",13, 1, "bit2", 1],	[" progFAN1Thursday: ",	13, 1, "bit3", 1],
		[" progFAN1Friday: ",	12, 1, "bit0", 1],	[" progFAN1Saturday: ",	12, 1, "bit1", 1],
		[" progFAN1Sunday: ",	12, 1, "bit2", 1],	[" progFAN1Enable: ",	14, 2, "hex", 1],
		[" progFAN2StartTime: ",16, 4, "hex2time", 1],	[" progFAN2EndTime: ",	20, 4, "hex2time", 1],
		[" progFAN2Monday: ",	25, 1, "bit0", 1],	[" progFAN2Tuesday: ",	25, 1, "bit1", 1],	
		[" progFAN2Wednesday: ",25, 1, "bit2", 1],	[" progFAN2Thursday: ",	25, 1, "bit3", 1],
		[" progFAN2Friday: ",	24, 1, "bit0", 1],	[" progFAN2Saturday: ",	24, 1, "bit1", 1],
		[" progFAN2Sunday: ",	24, 1, "bit2", 1],	[" progFAN2Enable: ",	26, 2, "hex", 1]
 	      ],  
  "0Epxx206" => [["p59RestartBeforeSetbackEnd: ", 4, 4, "hex", 1]
	      ], 
  "0Fpxx206" => [["pA0DurationUntilAbsenceStart: ", 4, 4, "hex", 10],	[" pA0AbsenceDuration: ",	 8, 4, "hex", 10], [" pA0EnableAbsenceProg: ", 12, 2, "hex", 1]
	      ], 	 
  "10pxx206" => [["p70StartDryHeat: ", 	 4, 2, "hex", 1],	[" p71BaseTemp: ",	 6, 4, "hex", 10],	[" p72PeakTemp: ", 10, 4, "hex", 10],
	      [" p73TempDuration: ", 	14, 4, "hex", 1],	[" p74TempIncrease: ",	18, 4, "hex", 10]
	      ],
  "16sol" => [["collectorTemp: ",	4, 4, "hex2int", 10],	[" dhwTemp: ", 	 8, 4, "hex2int", 10],
	      [" flowTemp: ",		12, 4, "hex2int", 10],	[" edSolPump: ",	16, 2, "hex2int", 1],
	      [" out: ",		26, 4, "raw", 1],	[" status: ",	30, 2, "raw", 1]
	      ],
  "17pxx206" => [["p01RoomTempDay: ", 	 4, 4,  "hex", 10],	[" p02RoomTempNight: ",	 8, 4, "hex", 10],
	      [" p03RoomTempStandby: ",	12, 4,  "hex", 10], 	[" p04DHWsetTempDay: ",	16, 4,  "hex", 10], 
	      [" p05DHWsetTempNight: ",	20, 4,  "hex", 10], 	[" p06DHWsetTempStandby: ",24, 4,  "hex", 10], 
	      [" p07FanStageDay: ",	28, 2,  "hex", 1], 	[" p08FanStageNight: ",	30, 2,  "hex", 1],
	      [" p09FanStageStandby: ",	32, 2,  "hex", 1], 	[" p10HCTempManual: ",	34, 4,  "hex", 10],
	      [" p11DHWsetTempManual: ",38, 4,  "hex", 10],  	[" p12FanStageManual: ",	42, 2,  "hex", 1],
	      ],
  "D1last" => [["number_of_faults: ",	 4, 2, "hex", 1],	
	      [" fault0CODE: ",		 8, 2, "faultmap", 1],	[" fault0TIME: ",	12, 4, "turnhex2time", 1],  [" fault0DATE: ",	16, 4, "turnhexdate", 1],
	      [" fault1CODE: ",		20, 2, "faultmap", 1],	[" fault1TIME: ",	24, 4, "turnhex2time", 1],  [" fault1DATE: ",	28, 4, "turnhexdate", 1],
	      [" fault2CODE: ",		32, 2, "faultmap", 1],	[" fault2TIME: ",	36, 4, "turnhex2time", 1],  [" fault2DATE: ",	40, 4, "turnhexdate", 1],
	      [" fault3CODE: ",		44, 2, "faultmap", 1],	[" fault3TIME: ",	48, 4, "turnhex2time", 1],  [" fault3DATE: ",	52, 4, "turnhexdate", 1]
	      ],
  "D1last206" => [["number_of_faults: ",	 4, 2, "hex", 1],	
	      [" fault0CODE: ",		 8, 4, "faultmap", 1],	[" fault0TIME: ",	12, 4, "hex2time", 1],  [" fault0DATE: ",	16, 4, "hexdate", 1],
	      [" fault1CODE: ",		20, 4, "faultmap", 1],	[" fault1TIME: ",	24, 4, "hex2time", 1],  [" fault1DATE: ",	28, 4, "hexdate", 1],
	      [" fault2CODE: ",		32, 4, "faultmap", 1],	[" fault2TIME: ",	36, 4, "hex2time", 1],  [" fault2DATE: ",	40, 4, "hexdate", 1],
	      [" fault3CODE: ",		44, 4, "faultmap", 1],	[" fault3TIME: ",	48, 4, "hex2time", 1],  [" fault3DATE: ",	52, 4, "hexdate", 1]
	      ],
  "E8fan"	=> [["inputFanSpeed: ",	58, 2, "hex", 1],    # like in sGlobal
		[" outputFanSpeed: ",		60, 2, "hex", 1],    # like in sGlobal
		[" pFanstageXAirflowInlet: ",	62, 4, "hex", 1],    # m3/h  corresponding to p37Fanstage1AirflowInlet or p38Fanstage2AirflowInlet
		[" pFanstageXAirflowOutlet: ",	66, 4, "hex", 1],    # m3/h corresponding to p40Fanstage1AirflowOutlet or p41Fanstage2AirflowOutlet
		[" inputFanPower: ",		70, 2, "hex", 1],    # like in sGlobal
		[" outputFanPower: ",		72, 2, "hex", 1],	 # like in sGlobal		
		  ],
  "E8fan206" => [["statusAFC: ",		4, 4, "hex", 1],		# 0=init air flow calibration (16:00) 4=normal fan operation
		[" supplyFanSpeedCAL: ",	 8, 4, "hex", 60],	# calibration speed
		[" exhaustFanSpeedCAL: ",	12, 4, "hex", 60], 	
		[" supplyFanAirflowCAL: ",	16, 4, "hex", 100], 	# calibration air flow volume
		[" exhaustFanAirflowCAL: ",	20, 4, "hex", 100],
		[" supplyFanSpeed: ",		24, 4, "hex", 1], 	# actual fan speed in 1/s
		[" exhaustFanSpeed: ",		28, 4, "hex", 1],
		[" supplyFanAirflowSet: ",	32, 4, "hex", 1],		# actual air flow volume setting in m3/h
		[" exhaustFanAirflowSet: ",	36, 4, "hex", 1],
		[" supplyFanSpeedTarget: ",	40, 4, "hex", 1],		# target fan speed in %
		[" exhaustFanSpeedTarget: ",	44, 4, "hex", 1],
		[" supplyFanSpeed0: ",		48, 4, "hex", 10], 		
		[" exhaustFanSpeed0: ",		52, 4, "hex", 10], 	
		[" supplyFanSpeed200: ",	56, 4, "hex", 10],
		[" exhaustFanSpeed200: ",	60, 4, "hex", 10],
		[" airflowTolerance: ",		64, 2, "hex", 1],
		[" airflowCalibrationInterval: ",66, 2, "hex", 1],		# calibration interval
		[" timeToCalibration: ",	68, 2, "hex", 1]		# days to next calibration
		  ],
  "EEprg206" => [["opMode: ",		4, 2, 	"opmode2", 1],	[" ProgStateHC: ", 	10, 2, "opmodehc", 1],	[" ProgStateDHW: ",	12, 2, "opmodehc", 1],
	      [" ProgStateFAN: ", 	14, 2, 	"opmodehc", 1],	[" BaseTimeAP0: ", 	16, 8, "hex", 1],		[" StatusAP0: ",		24, 2, "hex", 1],
	      [" StartTimeAP0: ",	26, 8, 	"hex", 1], 	[" EndTimeAP0: ", 	34, 8, "hex", 1]
	      ],
  "F2ctrl"  => [["heatRequest: ",		 4, 2, "hex", 1],	# 0=DHW 2=heat 5=off 6=defrostEva
		[" heatRequest2: ",		 6, 2, "hex", 1],	# same as heatRequest
		[" hcStage: ", 			 8, 2, "hex", 1],  	# 0=off 1=solar 2=heatPump 3=boost1 4=boost2 5=boost3
		[" dhwStage: ",			10, 2, "hex", 1],	# 0=off, 1=solar, 2=heatPump 3=boostMax
		[" heatStageControlModul: ",	12, 2, "hex", 1], 	# either hcStage or dhwStage depending from heatRequest
		[" compBlockTime: ", 		14, 4, "hex2int", 1],	# remaining compressor block time
		[" pasteurisationMode: ",	18, 2, "hex",  1],	# 0=off 1=on
		[" defrostEvaporator: ",	20, 2, "raw",  1],	# 10=off 30=defrostEva
		[" boosterStage2: ",		22, 1, "bit3", 1],	# booster 2		
		[" solarPump: ",		22, 1, "bit2", 1],	# solar pump
		[" boosterStage1: ",		22, 1, "bit1", 1],	# booster 1
		[" compressor: ",		22, 1, "bit0", 1],	# compressor
		[" heatPipeValve: ",		23, 1, "bit3", 1],	# heat pipe valve
		[" diverterValve: ",		23, 1, "bit2", 1],	# diverter valve
		[" dhwPump: ",			23, 1, "bit1", 1],	# dhw pump
		[" heatingCircuitPump: ",	23, 1, "bit0", 1],	# hc pump
		[" mixerOpen: ",		25, 1, "bit1", 1],	# mixer open
		[" mixerClosed: ",		25, 1, "bit0", 1],	# mixer closed
		[" sensorBits1: ", 		26, 2, "raw", 1],	# sensor condenser temperature ??
		[" sensorBits2: ", 		28, 2, "raw", 1],	# sensor low pressure ??
		[" boostBlockTimeAfterPumpStart: ", 30, 4, "hex2int", 1],# after each  pump start (dhw or heat circuit)
		[" boostBlockTimeAfterHD: ",	34, 4, "hex2int", 1]	# ??
          ],
  "F3dhw"  => [["dhwTemp: ",		 4, 4, "hex2int", 10],	[" outsideTemp: ", 	 8, 4, "hex2int", 10],
	      [" dhwSetTemp: ",		12, 4, "hex2int", 10],	[" compBlockTime: ",	16, 4, "hex2int", 1],
	      [" out: ", 		20, 4, "raw", 1],	[" heatBlockTime: ",	24, 4, "hex2int", 1],
	      [" dhwBoosterStage: ",	28, 2, "hex", 1],	[" pasteurisationMode: ",32, 2, "hex", 1],
	      [" dhwOpMode: ",		34, 2, "opmodehc", 1],	[" x36: ",		36, 4, "raw", 1]
  	      ],
  "F4hc1"  => [["outsideTemp: ",	4,  4, "hex2int", 10],	[" x08: ",	 	 8, 4, "hex2int", 10],
	      [" returnTemp: ",		12, 4, "hex2int", 10],	[" integralHeat: ",	16, 4, "hex2int", 1],
	      [" flowTemp: ",		20, 4, "hex2int", 10],	[" heatSetTemp: ", 	24, 4, "hex2int", 10], 
	      [" heatTemp: ",		28, 4, "hex2int", 10],  
	      [" seasonMode: ",		38, 2, "somwinmode", 1],#	[" x40: ",		40, 4, "hex2int", 1],
	      [" integralSwitch: ",	44, 4, "hex2int", 1],	[" hcOpMode: ",		48, 2, "opmodehc", 1],
		#[" x52: ",		52, 4, "hex2int", 1],
	      [" roomSetTemp: ",	56, 4, "hex2int", 10],	[" x60: ", 		60, 4, "hex2int", 10],
	      [" x64: ", 		64, 4, "hex2int", 10],	[" insideTempRC: ",	68, 4, "hex2int", 10],
	      [" x72: ", 		72, 4, "hex2int", 10],	[" x76: ", 		76, 4, "hex2int", 10],
	      [" onHysteresisNo: ", 	32, 2, "hex", 1],	[" offHysteresisNo: ",	34, 2, "hex", 1],
	      [" hcBoosterStage: ",	36, 2, "hex", 1]
         ],
  "F4hc1214" => [["outsideTemp: ",	 4, 4, "hex2int", 10],	[" x08: ",		 8, 4, "raw", 1],
 	      [" returnTemp: ",		12, 4, "hex2int", 10],	[" integralHeat: ",	16, 4, "hex2int", 1],
	      [" flowTemp: ",		20, 4, "hex2int", 10],	[" heatSetTemp: ", 	24, 4, "hex2int", 10], 
	      [" heatTemp: ",		28, 4, "hex2int", 10],  
	      [" seasonMode: ",		38, 2, "somwinmode", 1],
	      [" integralSwitch: ",	44, 4, "hex2int", 1],	[" hcOpMode: ",		48, 2, "opmodehc", 1], 	      
	      [" roomSetTemp: ",	62, 4, "hex2int", 10],	[" x60: ", 		60, 4, "hex2int", 10],
	      [" x64: ", 		64, 4, "raw", 1],	[" insideTempRC: ", 	68, 4, "hex2int", 10],
	      [" x72: ", 		72, 4, "raw", 1],	[" x76: ", 		76, 4, "raw", 1],
	      [" onHysteresisNo: ",	32, 2, "hex", 1],	[" offHysteresisNo: ",	34, 2, "hex", 1],
	      [" hcBoosterStage: ",	36, 2, "hex", 1]
         ],
  # contribution from joerg  hellijo  Antwort #1085  Juni 2022
   "F4hc1214j" => [["outsideTemp: ",	4, 4, "hex2int", 10],	[" x08: ",		8, 4, "raw", 1],
 	      [" returnTemp: ",		12, 4, "hex2int", 10],	[" integralHeat: ",	16, 4, "hex2int", 1],
	      [" flowTemp: ",		20, 4, "hex2int", 10],	[" heatSetTemp: ", 	24, 4, "hex2int", 10],
	      [" heatTemp: ",		28, 4, "hex2int", 10], 
	      [" seasonMode: ",		38, 2, "somwinmode", 1],
	      [" integralSwitch: ",	44, 4, "hex2int", 1],	[" hcOpMode: ",		48, 2, "opmodehc", 1],
	      [" roomSetTemp: ",	62, 4, "hex2int", 10],	[" x50: ", 		50, 4, "hex2int", 10],
	      [" x66: ", 		66, 4, "raw", 1],	[" insideTempRC: ", 	74, 4, "hex2int", 10],
	      [" x70: ", 		70, 4, "raw", 1],	[" x76: ", 		78, 4, "raw", 1],
	      [" onHysteresisNo: ",	32, 2, "hex", 1],	[" offHysteresisNo: ",	34, 2, "hex", 1],
	      [" hcStage: ",		36, 2, "hex", 1],# 0=Aus; 1=Solar; 2=V1
	      [" boosterStage2: ", 	40, 1, "bit3", 1],
	      [" x58: ", 		58, 4, "raw", 1],	[" x54: ", 		54, 4, "raw", 1],
	      [" blockTimeAfterCompStart: ", 82, 4, "hex2int", 1], [" insideTemp: ", 	86, 4, "hex2int", 10],
	      [" solarPump: ",		40, 1, 	"bit2", 1],	[" boosterStage1: ",	40, 1, 	"bit1", 1],
	      [" compressor: ",		40, 1, 	"bit0", 1],	[" heatPipeValve: ",	41, 1, 	"bit3", 1],
	      [" diverterValve: ",	41, 1, 	"bit2", 1],	[" dhwPump: ",		41, 1, 	"bit1", 1],
	      [" heatingCircuitPump: ",	41, 1, 	"bit0", 1],     [" mixerOpen: ",	43, 1, 	"bit1", 1],
	      [" mixerClosed: ",	43, 1, 	"bit0", 1]
         ],
  "F5hc2"  => [["outsideTemp: ",	4, 4, "hex2int", 10],	[" returnTemp: ",	 8, 4, "hex2int", 10],
	      [" vorlaufTemp: ",	12, 4, "hex2int", 10],	[" heatSetTemp: ",	16, 4, "hex2int", 10],
	      [" heatTemp: ", 		20, 4, "hex2int", 10],	[" stellgroesse: ",	24, 4, "hex2int", 10], 
	      [" seasonMode: ",		30, 2, "somwinmode",1],	[" hcOpMode: ",		36, 2, "opmodehc", 1] 
         ],
  "F6sys206" => [["userSetFanStage: ",	30, 2, "hex", 1],	[" userSetFanRemainingTime: ", 36, 4, "hex", 1],
	      [" lastErrors: ",		4, 8, "hex2error", 1],	     
         ],
  "FBglob" => [["outsideTemp: ",	8, 4, "hex2int", 10],	[" flowTemp: ",		12, 4, "hex2int", 10],
	      [" returnTemp: ",		16, 4, "hex2int", 10],	[" hotGasTemp: ", 	20, 4, "hex2int", 10],
	      [" dhwTemp: ",	 	24, 4, "hex2int", 10],	[" flowTempHC2: ",	28, 4, "hex2int", 10],
	      [" evaporatorTemp: ",	36, 4, "hex2int", 10],	[" condenserTemp: ",	40, 4, "hex2int", 10],
	      [" mixerOpen: ",		45, 1, "bit0", 1],	[" mixerClosed: ",	45, 1, "bit1", 1],
	      [" heatPipeValve: ",	45, 1, "bit2", 1],	[" diverterValve: ",	45, 1, "bit3", 1],
	      [" dhwPump: ",		44, 1, "bit0", 1],	[" heatingCircuitPump: ",44, 1, "bit1", 1],
	      [" solarPump: ",		44, 1, "bit3", 1],	[" compressor: ",	47, 1, "bit3", 1],
	      [" boosterStage3: ",	46, 1, "bit0", 1],	[" boosterStage2: ",	46, 1, "bit1", 1],
	      [" boosterStage1: ",	46, 1, "bit2", 1],	[" highPressureSensor: ",49, 1, "nbit0", 1],
	      [" lowPressureSensor: ",	49, 1, "nbit1", 1],	[" evaporatorIceMonitor: ",49, 1, "bit2", 1],
	      [" signalAnode: ",	49, 1, "bit3", 1],	[" evuRelease: ",	48, 1, "bit0", 1],
	      [" ovenFireplace: ",	48, 1, "bit1", 1],	[" STB: ",		48, 1, "bit2", 1],
	      [" outputVentilatorPower: ",50, 4, "hex", 10],	[" inputVentilatorPower: ",54, 4, "hex", 10],	[" mainVentilatorPower: ",	58, 4, "hex", 10],
	      [" outputVentilatorSpeed: ",62, 4, "hex", 1],	[" inputVentilatorSpeed: ",66, 4, "hex", 1],		[" mainVentilatorSpeed: ",	70, 4, "hex", 1],
	      [" outside_tempFiltered: ",74, 4, "hex2int", 10],	[" relHumidity: ",	78, 4, "hex2int", 10],
	      [" dewPoint: ",		82, 4, "hex2int", 10],
	      [" P_Nd: ",		86, 4, "hex2int", 100],	[" P_Hd: ",		90, 4, "hex2int", 100],
	      [" actualPower_Qc: ",	94, 8, "esp_mant", 1],	[" actualPower_Pel: ",	102, 8, "esp_mant", 1],
	      [" collectorTemp: ",	4,  4, "hex2int", 10],	[" insideTemp: ",	32, 4, "hex2int", 10], 
	      [" windowOpen: ",		47, 1, "bit2", 1], 	# board X18-1 clamp X4-FA (FensterAuf): window open - signal out 230V
	      [" quickAirVent: ",	48, 1, "bit3", 1], 	# board X15-8 clamp X4-SL (SchnellLüftung): quickAirVent - signal in 230V
	      [" flowRate: ",		110, 4, "hex", 100],	# board X51 sensor P5 (on newer models B1 flow temp as well) changed to l/min as suggested by TheTrumpeter Antwort #771
	      [" p_HCw: ",		114, 4, "hex", 100],	# board X4-1..3 sensor P4 HC water pressure
	      [" humidityAirOut: ",	154, 4, "hex", 100]	# board X4-4..6 sensor B15
          ],
  "FBglob214" => [["outsideTemp: ", 	8, 4, "hex2int", 10],	[" flowTemp: ",		12, 4, "hex2int", 10],
	      [" returnTemp: ",		16, 4, "hex2int", 10],	[" hotGasTemp: ", 	20, 4, "hex2int", 10],
	      [" dhwTemp: ",	 	24, 4, "hex2int", 10],	[" flowTempHC2: ",	28, 4, "hex2int", 10],
	      [" evaporatorTemp: ",	36, 4, "hex2int", 10],	[" condenserTemp: ",	40, 4, "hex2int", 10],
	      [" mixerOpen: ",		47, 1, "bit1", 1],	[" mixerClosed: ",	47, 1, "bit0", 1],
	      [" heatPipeValve: ",	45, 1, "bit3", 1],	[" diverterValve: ",	45, 1, "bit2", 1],
	      [" dhwPump: ",		45, 1, "bit1", 1],	[" heatingCircuitPump: ",	45, 1, "bit0", 1],
	      [" solarPump: ",		44, 1, "bit2", 1],	[" compressor: ",		44, 1, "bit0", 1],
	      [" boosterStage2: ",	44, 1, "bit3", 1],	[" boosterStage3: ",	44, 1, "n.a.", 1],
	      [" boosterStage1: ",	44, 1, "bit1", 1],	[" highPressureSensor: ",	54, 1, "bit3", 1],
	      [" lowPressureSensor: ",	54, 1, "bit2", 1],	[" evaporatorIceMonitor: ",55, 1, "bit3", 1],
	      [" signalAnode: ",	54, 1, "bit1", 1],	[" evuRelease: ",		48, 1, "n.a.", 1],
	      [" ovenFireplace: ",	54, 1, "bit0", 1],	[" STB: ",		48, 1, "n.a.", 1],
	      [" outputVentilatorPower: ",48, 2, "hex", 1],	[" inputVentilatorPower: ",50, 2, "hex", 1],	[" mainVentilatorPower: ",	52, 2, "hex", 255/100],         
	      [" outputVentilatorSpeed: ",56, 2, "hex", 1],	[" inputVentilatorSpeed: ",58, 2, "hex", 1],	[" mainVentilatorSpeed: ",	60, 2, "hex", 1],
	      [" outsideTempFiltered: ",64, 4, "hex2int", 10],	[" relHumidity: ",	70, 4, "n.a.", 1],
	      [" dewPoint: ",		5, 4, "n.a.", 1],
	      [" P_Nd: ",		5, 4, "n.a.", 1],	[" P_Hd: ",		5, 4, "n.a.", 1],
	      [" actualPower_Qc: ",	5, 8, "n.a.", 1],	[" actualPower_Pel: ",	5, 8, "n.a.", 1],
	      [" collectorTemp: ",	4,  4, "hex2int", 10],	[" insideTemp: ",	32, 4, "hex2int", 10]
	      ],
  "FBglob206" => [["outsideTemp: ", 	8, 4, "hex2int", 10],	[" flowTemp: ",		12, 4, "hex2int", 10],
	      [" returnTemp: ",		16, 4, "hex2int", 10],	[" hotGasTemp: ", 	20, 4, "hex2int", 10],
	      [" dhwTemp: ",		24, 4, "hex2int", 10],	[" flowTempHC2: ",	28, 4, "hex2int", 10],
	      [" evaporatorTemp: ",	36, 4, "hex2int", 10],	[" condenserTemp: ",	40, 4, "hex2int", 10],
	      [" mixerOpen: ",		47, 1, "bit1", 1],	[" mixerClosed: ",	47, 1, "bit0", 1],
	      [" heatPipeValve: ",	45, 1, "bit3", 1],	[" diverterValve: ",	45, 1, "bit2", 1],
	      [" dhwPump: ",		45, 1, "bit1", 1],	[" heatingCircuitPump: ",45, 1, "bit0", 1],
	      [" solarPump: ",		44, 1, "n.a", 1],	[" compressor: ",	44, 1, "bit0", 1],
	      [" boosterStage3: ",	44, 1, "bit3", 1],	[" boosterStage2: ",	44, 1, "bit2", 1], 	      
	      [" boosterStage1: ",	44, 1, "bit1", 1],	[" highPressureSensor: ",54, 1, "bit3", 1],
	      [" lowPressureSensor: ",	54, 1, "bit2", 1],	[" evaporatorIceMonitor: ",55, 1, "bit3", 1],
	      [" signalAnode: ",	54, 1, "bit1", 1],	[" evuRelease: ",	48, 1, "n.a.", 1],
	      [" ovenFireplace: ",	54, 1, "bit0", 1],	[" STB: ",		48, 1, "n.a.", 1],
	      [" outputVentilatorPower: ",48,2, "hex", 1],	[" inputVentilatorPower: ",50, 2, "hex", 1],	[" mainVentilatorPower: ",	52, 2, "hex", 255/100],          
	      [" outputVentilatorSpeed: ",56,2, "hex", 1],	[" inputVentilatorSpeed: ",58, 2, "hex", 1],	[" mainVentilatorSpeed: ",	60, 2, "hex", 1],
	      [" outsideTempFiltered: ",64, 4, "hex2int", 10],	[" relHumidity: ",	70, 4, "n.a.", 1],
	      [" dewPoint: ",		 5, 4, "n.a.", 1],
	      [" P_Nd: ",		 5, 4, "n.a.", 1],	[" P_Hd: ",		5, 4, "n.a.", 1],
	      [" actualPower_Qc: ",	 5, 8, "n.a.", 1],	[" actualPower_Pel: ",	5, 8, "n.a.", 1],
	      [" collectorTemp: ",	 4, 4, "hex2int", 10],	[" insideTemp: ",	32, 4, "hex2int", 10] 
	      ],
  "FCtime" => [["Weekday: ",	5, 1, "weekday", 1],	[" Hour: ",	6, 2, "hex", 1],
	      [" Min: ",	8, 2, "hex", 1],	[" Sec: ",	10, 2, "hex", 1],
	      [" Date: ", 	12, 2, "year", 1],	["/", 		14, 2, "hex", 1],
	      ["/", 		16, 2, "hex", 1]
	     ],
  "FCtime206" => [["Weekday: ",		7, 1,  "weekday", 1],	[" pClockHour: ",	8, 2, "hex", 1],
	      [" pClockMinutes: ",	10, 2, "hex", 1],	[" Sec: ",		12, 2, "hex", 1],
	      [" pClockYear: ",		14, 2, "hex", 1],	[" pClockMonth: ",	18, 2, "hex", 1],
	      [" pClockDay: ",		20, 2, "hex", 1]
	     ],
  "FDfirm" => [["version: ", 	4, 4, "hexdate", 1]
	     ],
  "FEfirmId" => [["HW: ",		30,  2, "hex", 1], 	[" SW: ",	32,  4, "swver", 1],
	      [" Date: ",		36, 22, "hex2ascii", 1]
	     ],
  "0A0176Dis" => [["switchingProg: ",	11, 1, "bit0", 1],  [" compressor: ",	11, 1, "bit1", 1],
	      [" heatingHC: ",		11, 1, "bit2", 1],  [" heatingDHW: ",	10, 1, "bit0", 1],
	      [" boosterHC: ",		10, 1, "bit1", 1],  [" filterBoth: ",	 9, 1, "bit0", 1],
	      [" ventStage: ",		 9, 1, "bit1", 1],  [" pumpHC: ",	 9, 1, "bit2", 1],
	      [" defrost: ",		 9, 1, "bit3", 1],  [" filterUp: ",	 8, 1, "bit0", 1],
	      [" filterDown: ",		 8, 1, "bit1", 1],  [" cooling: ",	11, 1, "bit3", 1],
	      [" service: ",		10, 1, "bit2", 1]
	      ],
  "0clean"    => [["", 8, 2, "hex", 1]             
              ],
  "1clean"    => [["", 8, 4, "hex2int", 1]             		#fix for celle; handl neg values 20NoV2021
              ],
  "2opmode"   => [["", 8, 2, "opmode", 1]             
              ],
  "4temp"     => [["", 8, 4, "hex2int",2560]             
	      ],
  "5temp"     => [["", 8, 4, "hex2int",10]             
	      ],
  "6gradient" => [["", 8, 4, "hex", 100]             
              ],
  "7prog"     => [["", 8, 2, "quater", 1],	["--", 10, 2, "quater", 1]
              ],
  "8party"    => [["", 10, 2, "quater", 1],	["--", 8, 2, "quater", 1]
              ],
  "9holy"     => [["", 10, 2, "quater", 1]
              ]
);


########################################################################################
#
# %sets - all supported protocols are listed  59E
# 
########################################################################################

my %setsX39technician =(
#   "zResetLast10errors"	=> {cmd2=>"D1",     argMin =>   "0",	argMax =>  "0",	type =>"0clean",  unit =>""},
   "zResetLast10errors"		=> {cmd2=>"D1",     argMin =>   "0",	argMax =>  "0",	type =>"D1last",  unit =>""},
#  "zPassiveCoolingtrigger"	=> {cmd2=>"0A0597", argMin =>   "0",	argMax =>  "50",	type =>"1clean",  unit =>""},
  "zPumpHC"			=> {cmd2=>"0A0052", argMin =>   "0",	argMax =>  "1",	type =>"0clean",  unit =>""},  
  "zPumpDHW"			=> {cmd2=>"0A0056", argMin =>   "0",	argMax =>  "1",	type =>"0clean",  unit =>""},
  "zControlValveDHW"  	 	=> {cmd2=>"0A0653", argMin =>   "0",    argMax =>  "1", type =>"1clean",  unit =>""}
 );



my %sets439539common = (
  "pOpMode"			=> {cmd2=>"0A0112", type   =>  "2opmode"},  # 1 Standby bereitschaft; 11 in Automatic; 3 DAYmode; SetbackMode; DHWmode; Manual; Emergency 
  "p01RoomTempDayHC1"		=> {cmd2=>"0B0005", argMin =>  "12",	argMax =>   "32",	type =>"5temp",  unit =>" °C"},
  "p02RoomTempNightHC1"		=> {cmd2=>"0B0008", argMin =>  "12",	argMax =>   "32",	type =>"5temp",  unit =>" °C"},
  "p03RoomTempStandbyHC1"	=> {cmd2=>"0B013D", argMin =>  "12",	argMax =>   "32",	type =>"5temp",  unit =>" °C"},
  "p01RoomTempDayHC1SummerMode"	=> {cmd2=>"0B0569", argMin =>  "12",	argMax =>   "32",	type =>"5temp",  unit =>" °C"},
  "p02RoomTempNightHC1SummerMode"=> {cmd2=>"0B056B", argMin =>  "12",	argMax =>   "32",	type =>"5temp",  unit =>" °C"},
  "p03RoomTempStandbyHC1SummerMode"=> {cmd2=>"0B056A", argMin =>  "12",	argMax =>   "32",	type =>"5temp",  unit =>" °C"},
  "p13GradientHC1"		=> {cmd2=>"0B010E", argMin => "0.1",	argMax =>    "5",	type =>"6gradient", unit =>""}, # 0..5 rappresentato/100
  "p14LowEndHC1"		=> {cmd2=>"0B059E", argMin =>   "0",	argMax =>   "10",	type =>"5temp",  unit =>" K"},   #in °K 0..20°K rappresentato/10
  "p15RoomInfluenceHC1"		=> {cmd2=>"0B010F", argMin =>   "0",	argMax =>  "100",	type =>"0clean", unit =>" %"},
  "p19FlowProportionHC1"	=> {cmd2=>"0B059D", argMin =>   "0",	argMax =>  "100",	type =>"1clean", unit =>" %"}, #in % 0..100%
  "p01RoomTempDayHC2"		=> {cmd2=>"0C0005", argMin =>  "12",	argMax =>   "32",	type =>"5temp",  unit =>" °C"},
  "p02RoomTempNightHC2"		=> {cmd2=>"0C0008", argMin =>  "12",	argMax =>   "32",	type =>"5temp",  unit =>" °C"},
  "p03RoomTempStandbyHC2"	=> {cmd2=>"0C013D", argMin =>  "12",	argMax =>   "32",	type =>"5temp",  unit =>" °C"},
  "p01RoomTempDayHC2SummerMode"	=> {cmd2=>"0C0569", argMin =>  "12",	argMax =>   "32",	type =>"5temp",  unit =>" °C"},
  "p02RoomTempNightHC2SummerMode"=> {cmd2=>"0C056B", argMin =>  "12",	argMax =>   "32",	type =>"5temp",  unit =>" °C"},
  "p03RoomTempStandbyHC2SummerMode"=> {cmd2=>"0C056A", argMin =>  "12",	argMax =>   "32",	type =>"5temp",  unit =>" °C"},
  "p16GradientHC2"		=> {cmd2=>"0C010E", argMin => "0.1",	argMax =>    "5",	type =>"6gradient", unit =>""}, # /100
  "p17LowEndHC2"		=> {cmd2=>"0C059E", argMin =>   "0",	argMax =>   "10", 	type =>"5temp",  unit =>" K"},
  "p18RoomInfluenceHC2"		=> {cmd2=>"0C010F", argMin =>   "0",	argMax =>  "100",	type =>"0clean", unit =>" %"}, 
  "p04DHWsetDayTemp"		=> {cmd2=>"0A0013", argMin =>  "10",	argMax =>   "55",	type =>"5temp",  unit =>" °C"},
  "p05DHWsetNightTemp"		=> {cmd2=>"0A05BF", argMin =>  "10",	argMax =>   "55",	type =>"5temp",  unit =>" °C"},
  "p83DHWsetSolarTemp"		=> {cmd2=>"0A05BE", argMin =>  "10",	argMax =>   "75",	type =>"5temp",  unit =>" °C"},
  "p06DHWsetStandbyTemp"	=> {cmd2=>"0A0581", argMin =>  "10",	argMax =>   "55",	type =>"5temp",  unit =>" °C"},
  "p11DHWsetManualTemp"		=> {cmd2=>"0A0580", argMin =>  "10",	argMax =>   "55",	type =>"5temp",  unit =>" °C"},
  "p36DHWMaxTime"		=> {cmd2=>"0A0180", argMin =>   "6",	argMax =>   "12",	type =>"1clean", unit =>""},
  "p07FanStageDay"		=> {cmd2=>"0A056C", argMin =>   "0",	argMax =>    "3",	type =>"1clean", unit =>""},
  "p08FanStageNight"		=> {cmd2=>"0A056D", argMin =>   "0",	argMax =>    "3",	type =>"1clean", unit =>""},
  "p09FanStageStandby"		=> {cmd2=>"0A056F", argMin =>   "0",	argMax =>    "3",	type =>"1clean", unit =>""},
  "p99FanStageParty"		=> {cmd2=>"0A0570", argMin =>   "0",	argMax =>    "3",	type =>"1clean", unit =>""},
  "p21Hyst1"			=> {cmd2=>"0A05C0", argMin =>   "0",	argMax =>   "10",	type =>"5temp",  unit =>" K"},
  "p22Hyst2"			=> {cmd2=>"0A05C1", argMin =>   "0",	argMax =>   "10",	type =>"5temp",  unit =>" K"},
  "p23Hyst3"			=> {cmd2=>"0A05C2", argMin =>   "0",	argMax =>    "5",	type =>"5temp",  unit =>" K"},
  "p24Hyst4"			=> {cmd2=>"0A05C3", argMin =>   "0",	argMax =>    "5",	type =>"5temp",  unit =>" K"},
  "p25Hyst5"			=> {cmd2=>"0A05C4", argMin =>   "0",	argMax =>    "5",	type =>"5temp",  unit =>" K"},
  "p29HystAsymmetry"		=> {cmd2=>"0A05C5", argMin =>   "1",	argMax =>    "5",	type =>"1clean", unit =>""}, 
  "p30integralComponent"	=> {cmd2=>"0A0162", argMin =>  "10",	argMax =>  "999",	type =>"1clean", unit =>" Kmin"},
  "p31MaxBoosterStgHtg"		=> {cmd2=>"0A059F", argMin =>   "0",	argMax =>    "3",	type =>"1clean", unit =>""},
  "p32HystDHW"			=> {cmd2=>"0A0140", argMin =>   "0",	argMax =>   "10",	type =>"5temp",  unit =>" K"},
  "p33BoosterTimeoutDHW"	=> {cmd2=>"0A0588", argMin =>   "0",	argMax =>  "200",	type =>"1clean", unit =>" min"}, #during DHW heating
  "p79BoosterTimeoutHC"		=> {cmd2=>"0A05A0", argMin =>   "0",	argMax =>   "60",	type =>"1clean", unit =>" min"}, #delayed enabling of booster heater
  "p46UnschedVent0"		=> {cmd2=>"0A0571", argMin =>   "0",	argMax => "1000",	type =>"1clean", unit =>" min"},	 #in min
  "p45UnschedVent1"		=> {cmd2=>"0A0572", argMin =>   "0",	argMax => "1000",	type =>"1clean", unit =>" min"},	 #in min
  "p44UnschedVent2"		=> {cmd2=>"0A0573", argMin =>   "0",	argMax => "1000",	type =>"1clean", unit =>" min"},	 #in min
  "p43UnschedVent3"		=> {cmd2=>"0A0574", argMin =>   "0",	argMax => "1000",	type =>"1clean", unit =>" min"},	 #in min
  "p37Fanstage1AirflowInlet"	=> {cmd2=>"0A0576", argMin =>  "50",	argMax =>  "300",	type =>"1clean", unit =>" m3/h"},	#zuluft 
  "p38Fanstage2AirflowInlet"	=> {cmd2=>"0A0577", argMin =>  "50",	argMax =>  "300",	type =>"1clean", unit =>" m3/h"},	#zuluft 
  "p39Fanstage3AirflowInlet"	=> {cmd2=>"0A0578", argMin =>  "50",	argMax =>  "300",	type =>"1clean", unit =>" m3/h"},	#zuluft 
  "p40Fanstage1AirflowOutlet"	=> {cmd2=>"0A0579", argMin =>  "50",	argMax =>  "300",	type =>"1clean", unit =>" m3/h"},	#abluft extrated
  "p41Fanstage2AirflowOutlet"	=> {cmd2=>"0A057A", argMin =>  "50",	argMax =>  "300",	type =>"1clean", unit =>" m3/h"},	#abluft extrated
  "p42Fanstage3AirflowOutlet"	=> {cmd2=>"0A057B", argMin =>  "50",	argMax =>  "300",	type =>"1clean", unit =>" m3/h"},	#abluft extrated
  "p49SummerModeTemp"		=> {cmd2=>"0A0116", argMin =>  "10",	argMax =>   "24",	type =>"5temp",  unit =>" °C"},		#threshold for summer mode !! 
  "p50SummerModeHysteresis"	=> {cmd2=>"0A05A2", argMin => "0.5",	argMax =>    "5",	type =>"5temp",  unit =>" K"},		#Hysteresis for summer mode !! 
  "p78DualModePoint"		=> {cmd2=>"0A01AC", argMin => "-10",	argMax =>   "20",	type =>"5temp",  unit =>" °C"},
  "p54MinPumpCycles"		=> {cmd2=>"0A05B8", argMin =>   "1",	argMax =>   "24",	type =>"1clean", unit =>""},
  "p55MaxPumpCycles"		=> {cmd2=>"0A05B7", argMin =>  "25",	argMax =>  "288",	type =>"1clean", unit =>""},
  "p56OutTempMaxPumpCycles"	=> {cmd2=>"0A05B9", argMin =>   "0",	argMax =>   "20",	type =>"5temp",  unit =>" °C"},
  "p57OutTempMinPumpCycles"	=> {cmd2=>"0A05BA", argMin =>   "0",	argMax =>   "25",	type =>"5temp",  unit =>" °C"},
  "p58SuppressTempCaptPumpStart"=> {cmd2=>"0A0611", argMin =>   "0",	argMax =>  "120",	type =>"1clean", unit =>" s"},
  "p76RoomThermCorrection"	=> {cmd2=>"0A0109", argMin =>  "-5",	argMax =>    "5",	type =>"4temp",  unit =>" K"},
  "p77OutThermFilterTime"	=> {cmd2=>"0A010C", argMin =>   "1",	argMax =>   "24",	type =>"0clean", unit =>" h"},
  "p35PasteurisationInterval"	=> {cmd2=>"0A0586", argMin =>   "1",	argMax =>   "30",	type =>"1clean", unit =>""},
  "p80EnableSolar"		=> {cmd2=>"0A03C1", argMin =>   "0",	argMax =>    "1",	type =>"1clean", unit =>""},
  "p35PasteurisationTemp"	=> {cmd2=>"0A0587", argMin =>  "10",	argMax =>   "65",	type =>"5temp",  unit =>" °C"},
  "p34BoosterDHWTempAct"	=> {cmd2=>"0A0589", argMin => "-10",	argMax =>   "10",	type =>"5temp",  unit =>" °C"},
  "p99DHWmaxFlowTemp"		=> {cmd2=>"0A058C", argMin =>  "10",	argMax =>   "75",	type =>"5temp",  unit =>" °C"},
  "p99HC1maxFlowTemp"		=> {cmd2=>"0A0027", argMin =>  "10",	argMax =>   "75",	type =>"5temp",  unit =>" °C"},
  "p89DHWeco"			=> {cmd2=>"0A058D", argMin =>   "0",	argMax =>    "1",	type =>"1clean", unit =>""},
  "p99startUnschedVent"		=> {cmd2=>"0A05DD", argMin =>   "0",	argMax =>    "3",	type =>"1clean", unit =>""},
  "p99FrostProtectionBoost"	=> {cmd2=>"0A05B3", argMin =>  "10",	argMax =>   "30",	type =>"5temp",  unit =>" °C"}, #added by TheTrumpeter __EINFRIERSCHUTZ NE
  "p99FrostProtectionCancel"	=> {cmd2=>"0A05B4", argMin =>   "0",	argMax =>   "20",	type =>"5temp",  unit =>" °C"}, #added by TheTrumpeter __ABTAUABBR.
  "pClockDay"			=> {cmd2=>"0A0122", argMin =>   "1",	argMax =>   "31",	type =>"0clean", unit =>""},
  "pClockMonth"			=> {cmd2=>"0A0123", argMin =>   "1",	argMax =>   "12",	type =>"0clean", unit =>""},
  "pClockYear"			=> {cmd2=>"0A0124", argMin =>  "12",	argMax =>   "20",	type =>"0clean", unit =>""},
  "pClockHour"			=> {cmd2=>"0A0125", argMin =>   "0",	argMax =>   "23",	type =>"0clean", unit =>""},
  "pClockMinutes"		=> {cmd2=>"0A0126", argMin =>   "0",	argMax =>   "59",	type =>"0clean", unit =>""},
  "pHolidayBeginDay"		=> {cmd2=>"0A011B", argMin =>   "1",	argMax =>   "31",	type =>"0clean", unit =>""},
  "pHolidayBeginMonth"		=> {cmd2=>"0A011C", argMin =>   "1",	argMax =>   "12",	type =>"0clean", unit =>""},
  "pHolidayBeginYear"		=> {cmd2=>"0A011D", argMin =>  "12",	argMax =>   "30",	type =>"0clean", unit =>""},
  "pHolidayBeginTime"		=> {cmd2=>"0A05D3", argMin =>  "00:00",	argMax =>"23:59",	type =>"9holy",  unit =>""},
  "pHolidayEndDay"		=> {cmd2=>"0A011E", argMin =>   "1",	argMax =>   "31",	type =>"0clean", unit =>""},
  "pHolidayEndMonth"		=> {cmd2=>"0A011F", argMin =>   "1",	argMax =>   "12",	type =>"0clean", unit =>""},
  "pHolidayEndYear"		=> {cmd2=>"0A0120", argMin =>  "12",	argMax =>   "30",	type =>"0clean", unit =>""},
  "pHolidayEndTime"		=> {cmd2=>"0A05D4", argMin =>  "00:00",	argMax =>"23:59",	type =>"9holy",  unit =>""}, # the answer look like  0A05D4-0D0A05D40029 for year 41 which is 10:15
  #"party-time"			=> {cmd2=>"0A05D1", argMin =>  "00:00",	argMax =>"23:59",	type =>"8party", unit =>""}, # value 1Ch 28dec is 7 ; value 1Eh 30dec is 7:30
  "programHC1_Mo_0"		=> {cmd2=>"0B1410", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},  #1 is monday 0 is first prog; start and end; value 1Ch 28dec is 7 ; value 1Eh 30dec is 7:30
  "programHC1_Mo_1"		=> {cmd2=>"0B1411", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Mo_2"		=> {cmd2=>"0B1412", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Tu_0"		=> {cmd2=>"0B1420", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Tu_1"		=> {cmd2=>"0B1421", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Tu_2"		=> {cmd2=>"0B1422", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_We_0"		=> {cmd2=>"0B1430", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_We_1"		=> {cmd2=>"0B1431", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_We_2"		=> {cmd2=>"0B1432", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Th_0"		=> {cmd2=>"0B1440", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Th_1"		=> {cmd2=>"0B1441", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Th_2"		=> {cmd2=>"0B1442", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Fr_0"		=> {cmd2=>"0B1450", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Fr_1"		=> {cmd2=>"0B1451", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Fr_2"		=> {cmd2=>"0B1452", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Sa_0"		=> {cmd2=>"0B1460", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Sa_1"		=> {cmd2=>"0B1461", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Sa_2"		=> {cmd2=>"0B1462", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_So_0"		=> {cmd2=>"0B1470", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_So_1"		=> {cmd2=>"0B1471", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_So_2"		=> {cmd2=>"0B1472", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Mo-Fr_0"		=> {cmd2=>"0B1480", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Mo-Fr_1"		=> {cmd2=>"0B1481", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Mo-Fr_2"		=> {cmd2=>"0B1482", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Sa-So_0"		=> {cmd2=>"0B1490", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Sa-So_1"		=> {cmd2=>"0B1491", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Sa-So_2"		=> {cmd2=>"0B1492", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Mo-So_0"		=> {cmd2=>"0B14A0", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Mo-So_1"		=> {cmd2=>"0B14A1", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC1_Mo-So_2"		=> {cmd2=>"0B14A2", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Mo_0"		=> {cmd2=>"0C1510", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},  #1 is monday 0 is first prog; start and end; value 1Ch 28dec is 7 ; value 1Eh 30dec is 7:30
  "programHC2_Mo_1"		=> {cmd2=>"0C1511", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Mo_2"		=> {cmd2=>"0C1512", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Tu_0"		=> {cmd2=>"0C1520", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Tu_1"		=> {cmd2=>"0C1521", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Tu_2"		=> {cmd2=>"0C1522", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_We_0"		=> {cmd2=>"0C1530", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_We_1"		=> {cmd2=>"0C1531", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_We_2"		=> {cmd2=>"0C1532", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Th_0"		=> {cmd2=>"0C1540", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Th_1"		=> {cmd2=>"0C1541", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Th_2"		=> {cmd2=>"0C1542", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Fr_0"		=> {cmd2=>"0C1550", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Fr_1"		=> {cmd2=>"0C1551", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Fr_2"		=> {cmd2=>"0C1552", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Sa_0"		=> {cmd2=>"0C1560", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Sa_1"		=> {cmd2=>"0C1561", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Sa_2"		=> {cmd2=>"0C1562", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_So_0"		=> {cmd2=>"0C1570", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_So_1"		=> {cmd2=>"0C1571", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_So_2"		=> {cmd2=>"0C1572", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Mo-Fr_0"		=> {cmd2=>"0C1580", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Mo-Fr_1"		=> {cmd2=>"0C1581", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Mo-Fr_2"		=> {cmd2=>"0C1582", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Sa-So_0"		=> {cmd2=>"0C1590", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Sa-So_1"		=> {cmd2=>"0C1591", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Sa-So_2"		=> {cmd2=>"0C1592", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Mo-So_0"		=> {cmd2=>"0C15A0", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Mo-So_1"		=> {cmd2=>"0C15A1", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programHC2_Mo-So_2"		=> {cmd2=>"0C15A2", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Mo_0"		=> {cmd2=>"0A1710", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Mo_1"		=> {cmd2=>"0A1711", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Mo_2"		=> {cmd2=>"0A1712", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Tu_0"		=> {cmd2=>"0A1720", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Tu_1"		=> {cmd2=>"0A1721", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Tu_2"		=> {cmd2=>"0A1722", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_We_0"		=> {cmd2=>"0A1730", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_We_1"		=> {cmd2=>"0A1731", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_We_2"		=> {cmd2=>"0A1732", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Th_0"		=> {cmd2=>"0A1740", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Th_1"		=> {cmd2=>"0A1741", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Th_2"		=> {cmd2=>"0A1742", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Fr_0"		=> {cmd2=>"0A1750", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Fr_1"		=> {cmd2=>"0A1751", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Fr_2"		=> {cmd2=>"0A1752", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Sa_0"		=> {cmd2=>"0A1760", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Sa_1"		=> {cmd2=>"0A1761", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Sa_2"		=> {cmd2=>"0A1762", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_So_0"		=> {cmd2=>"0A1770", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_So_1"		=> {cmd2=>"0A1771", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_So_2"		=> {cmd2=>"0A1772", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Mo-Fr_0"		=> {cmd2=>"0A1780", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Mo-Fr_1"		=> {cmd2=>"0A1781", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Mo-Fr_2"		=> {cmd2=>"0A1782", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Sa-So_0"		=> {cmd2=>"0A1790", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Sa-So_1"		=> {cmd2=>"0A1791", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Sa-So_2"		=> {cmd2=>"0A1792", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Mo-So_0"		=> {cmd2=>"0A17A0", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Mo-So_1"		=> {cmd2=>"0A17A1", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programDHW_Mo-So_2"		=> {cmd2=>"0A17A2", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Mo_0"		=> {cmd2=>"0A1D10", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Mo_1"		=> {cmd2=>"0A1D11", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Mo_2"		=> {cmd2=>"0A1D12", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Tu_0"		=> {cmd2=>"0A1D20", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Tu_1"		=> {cmd2=>"0A1D21", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Tu_2"		=> {cmd2=>"0A1D22", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_We_0"		=> {cmd2=>"0A1D30", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_We_1"		=> {cmd2=>"0A1D31", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_We_2"		=> {cmd2=>"0A1D32", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Th_0"		=> {cmd2=>"0A1D40", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Th_1"		=> {cmd2=>"0A1D41", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Th_2"		=> {cmd2=>"0A1D42", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Fr_0"		=> {cmd2=>"0A1D50", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Fr_1"		=> {cmd2=>"0A1D51", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Fr_2"		=> {cmd2=>"0A1D52", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Sa_0"		=> {cmd2=>"0A1D60", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Sa_1"		=> {cmd2=>"0A1D61", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Sa_2"		=> {cmd2=>"0A1D62", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_So_0"		=> {cmd2=>"0A1D70", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_So_1"		=> {cmd2=>"0A1D71", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_So_2"		=> {cmd2=>"0A1D72", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Mo-Fr_0"		=> {cmd2=>"0A1D80", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Mo-Fr_1"		=> {cmd2=>"0A1D81", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Mo-Fr_2"		=> {cmd2=>"0A1D82", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Sa-So_0"		=> {cmd2=>"0A1D90", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Sa-So_1"		=> {cmd2=>"0A1D91", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Sa-So_2"		=> {cmd2=>"0A1D92", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Mo-So_0"		=> {cmd2=>"0A1DA0", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Mo-So_1"		=> {cmd2=>"0A1DA1", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "programFan_Mo-So_2"		=> {cmd2=>"0A1DA2", argMin =>  "00:00",	argMax =>"24:00",	type =>"7prog",  unit =>""},
  "pOvenFireplace"		=> {cmd2=>"0A057C", argMin =>      "0",	argMax =>    "4",	type =>"1clean", unit =>""}, #Ofen / Kamin (0=Aus … 4= oeffner - ueberwachung)
    #OFF/    N-O_CONTACT_OFF/       N-C_CONTACT_OFF/     N-O_MONITORING/    N-C_MONITORING
    #LLWT = LuftLuftWärmeTauscher - AirAirHeatExchanger
  "p85MaxDefrostDur"		=> {cmd2=>"0A057D", argMin =>  "60",	argMax =>  "250",	type =>"1clean", unit =>" min"},
  "p85DefrStartThreshold"	=> {cmd2=>"0A057E", argMin =>   "0",	argMax =>   "50",	type =>"1clean", unit =>" %"},  #LLWT_Abtaubeginnschwelle (%) -
  "p85FilterSpeed"		=> {cmd2=>"0A057F", argMin =>   "0",	argMax =>  "100",	type =>"1clean", unit =>" %"},   #LLWT_DrehzahlFilter (%) - increase in VentSpeed to indicate dirt / replacement needed 
  "p86OutTempCorrection"	=> {cmd2=>"0A05A3", argMin =>   "-20",	argMax =>  "30",	type =>"5temp",  unit =>" K"}   
  );


  
my %sets439only =(
  "p75passiveCooling"		=> {cmd2=>"0A0575", argMin =>   "0",	argMax =>   "2",	type =>"1clean",  unit =>""}   
 );
  
my %sets539only =(
  "p75passiveCooling"		=> {cmd2=>"0A0575", argMin =>   "0",	argMax =>  "4",		type =>"1clean", unit =>""},    
  "p99PumpRateHC"		=> {cmd2=>"0A02CB", argMin =>   "0",	argMax =>  "100",	type =>"5temp",  unit =>" %"},  
  "p99PumpRateDHW"		=> {cmd2=>"0A02CC", argMin =>   "0",	argMax =>  "100",	type =>"5temp",  unit =>" %"} ,
  "p99CoolingHC1Switch"		=> {cmd2=>"0B0287", argMin =>   "0",	argMax =>  "1",		type =>"1clean", unit =>""},
  "p99CoolingHC1SetTemp"	=> {cmd2=>"0B0582", argMin =>  "12",	argMax =>  "27",	type =>"5temp",  unit =>" °C"},    #suggested by TheTrumpeter
  "p99CoolingHC1HysterFlowTemp"	=> {cmd2=>"0B0583", argMin =>  "0.5",	argMax =>  "5",		type =>"5temp",  unit =>" K"}, #suggested by TheTrumpeter
  "p99CoolingHC1HysterRoomTemp"	=> {cmd2=>"0B0584", argMin =>  "0.5",	argMax =>  "3",		type =>"5temp",  unit =>" K"},  #suggested by TheTrumpeter
  "p99CoolingHC2Switch"		=> {cmd2=>"0C0287", argMin =>   "0",	argMax =>  "1",		type =>"1clean", unit =>""},     #suggested by rett_de
  "p99CoolingHC2SetTemp"	=> {cmd2=>"0C0582", argMin =>  "12",	argMax =>  "27",	type =>"5temp",  unit =>" °C"},    #suggested by rett_de
  "p99CoolingHC2HysterFlowTemp"	=> {cmd2=>"0C0583", argMin =>  "0.5",	argMax =>  "5",		type =>"5temp",  unit =>" K"}, #suggested by rett_de
  "p99CoolingHC2HysterRoomTemp"	=> {cmd2=>"0C0584", argMin =>  "0.5",	argMax =>  "3",		type =>"5temp",  unit =>" K"}  #suggested by Trett_de
);
  

my %sets206 = (
  "p01RoomTempDay"		=> {parent=>"p01-p12",	argMin => "10",	argMax =>   "30",	type =>"pclean", unit =>" °C"},
  "p02RoomTempNight"		=> {parent=>"p01-p12",	argMin => "10",	argMax =>   "30",	type =>"pclean", unit =>" °C"},
  "p03RoomTempStandby"		=> {parent=>"p01-p12",	argMin => "10",	argMax =>   "30",	type =>"pclean", unit =>" °C"},
  "p04DHWsetTempDay"		=> {parent=>"p01-p12",	argMin => "10",	argMax =>   "55",	type =>"pclean", unit =>" °C"},
  "p05DHWsetTempNight"		=> {parent=>"p01-p12",	argMin => "10",	argMax =>   "55",	type =>"pclean", unit =>" °C"},
  "p06DHWsetTempStandby"	=> {parent=>"p01-p12",	argMin => "10",	argMax =>   "55",	type =>"pclean", unit =>" °C"},
  "p07FanStageDay"		=> {parent=>"p01-p12",	argMin => "0",	argMax =>    "3",	type =>"pclean", unit =>""},
  "p08FanStageNight"		=> {parent=>"p01-p12",	argMin => "0",	argMax =>    "3",	type =>"pclean", unit =>""},
  "p09FanStageStandby"		=> {parent=>"p01-p12",	argMin => "0",	argMax =>    "3",	type =>"pclean", unit =>""},
  "p10HCTempManual"		=> {parent=>"p01-p12",	argMin => "10",	argMax =>   "65",	type =>"pclean", unit =>" °C"},
  "p11DHWsetTempManual"		=> {parent=>"p01-p12",	argMin => "10",	argMax =>   "65",	type =>"pclean", unit =>" °C"},
  "p12FanStageManual"      	=> {parent=>"p01-p12",	argMin => "0",	argMax =>    "3",	type =>"pclean", unit =>""},
  "p13GradientHC1"		=> {parent=>"pHeat1", 	argMin => "0",	argMax =>    "5",	type =>"pclean", unit =>""},
  "p14LowEndHC1"		=> {parent=>"pHeat1",	argMin => "0",	argMax =>   "20",	type =>"pclean", unit =>" K"},
  "p15RoomInfluenceHC1"		=> {parent=>"pHeat1",	argMin => "0",	argMax =>   "10",	type =>"pclean", unit =>""},
  "p16GradientHC2"		=> {parent=>"pHeat1",	argMin => "0",	argMax =>    "5",	type =>"pclean", unit =>""},
  "p17LowEndHC2"	    	=> {parent=>"pHeat1",	argMin => "0",	argMax =>   "10",	type =>"pclean", unit =>" K"},
  "p18RoomInfluenceHC2"		=> {parent=>"pHeat1",	argMin => "0",	argMax =>   "10",	type =>"pclean", unit =>""},
  "p19FlowProportionHC1"	=> {parent=>"pHeat1",	argMin => "0",	argMax =>  "100",	type =>"pclean", unit =>" %"},
  "p20FlowProportionHC2"	=> {parent=>"pHeat1",	argMin => "0",	argMax =>  "100",	type =>"pclean", unit =>" %"},
  "p21Hyst1"			=> {parent=>"pHeat2",	argMin => "0",	argMax =>   "10",	type =>"pclean", unit =>" K"},
  "p22Hyst2"			=> {parent=>"pHeat2",	argMin => "0",	argMax =>   "10",	type =>"pclean", unit =>" K"},
  "p23Hyst3"			=> {parent=>"pHeat2",	argMin => "0",	argMax =>    "5",	type =>"pclean", unit =>" K"},
  "p24Hyst4"			=> {parent=>"pHeat2",	argMin => "0",	argMax =>    "5",	type =>"pclean", unit =>" K"},
  "p25Hyst5"			=> {parent=>"pHeat2",	argMin => "0",	argMax =>    "5",	type =>"pclean", unit =>" K"},
  "p29HystAsymmetry"		=> {parent=>"pHeat2",	argMin => "1",	argMax =>    "5",	type =>"pclean", unit =>""},
  "p30integralComponent"	=> {parent=>"pHeat2",	argMin => "10",	argMax =>  "999",	type =>"pclean", unit =>" Kmin"},
  "p32HystDHW"			=> {parent=>"pDHW",	argMin => "2",	argMax =>   "10",	type =>"pclean", unit =>" K"},
  "p33BoosterTimeoutDHW"	=> {parent=>"pDHW",	argMin => "0",	argMax =>  "240",	type =>"pclean", unit =>" min"},
  "p34TempLimitBoostDHW"    	=> {parent=>"pDHW",	argMin => "-10",argMax =>   "10",	type =>"pclean", unit =>" °C"},
  "p35PasteurisationInterval"  	=> {parent=>"pDHW",	argMin => "3",	argMax =>   "30",	type =>"pclean", unit =>" Days"},
  "p36MaxDurationDHWLoad"    	=> {parent=>"pDHW",	argMin => "6",	argMax =>   "12",	type =>"pclean", unit =>" h"},
  "p37Fanstage1AirflowInlet"	=> {parent=>"pFan",	argMin => "60",	argMax =>  "250",	type =>"pclean", unit =>" m3/h"},
  "p38Fanstage2AirflowInlet"	=> {parent=>"pFan",	argMin => "60",	argMax =>  "250",	type =>"pclean", unit =>" m3/h"},
  "p39Fanstage3AirflowInlet"	=> {parent=>"pFan",	argMin => "60",	argMax =>  "250",	type =>"pclean", unit =>" m3/h"},
  "p40Fanstage1AirflowOutlet"	=> {parent=>"pFan",	argMin => "60",	argMax =>  "250",	type =>"pclean", unit =>" m3/h"},
  "p41Fanstage2AirflowOutlet"	=> {parent=>"pFan",	argMin => "60",	argMax =>  "250",	type =>"pclean", unit =>" m3/h"},
  "p42Fanstage3AirflowOutlet"	=> {parent=>"pFan",	argMin => "60",	argMax =>  "250",	type =>"pclean", unit =>" m3/h"},
  "p43UnschedVent3"		=> {parent=>"pFan",	argMin => "0",	argMax => "1000",	type =>"pclean", unit =>" min"},
  "p44UnschedVent2"		=> {parent=>"pFan",	argMin => "0",	argMax => "1000",	type =>"pclean", unit =>" min"},
  "p45UnschedVent1"		=> {parent=>"pFan",	argMin => "0",	argMax => "1000",	type =>"pclean", unit =>" min"},
  "p46UnschedVent0"		=> {parent=>"pFan",	argMin => "0",	argMax => "1000",	type =>"pclean", unit =>" min"},
  "p47CompressorRestartDelay"	=> {parent=>"pDefrostEva",argMin => "0",argMax =>   "20",	type =>"pclean", unit =>" min"},
  "p48MainFanSpeed"		=> {parent=>"pDefrostEva",argMin => "0",argMax =>  "100",	type =>"pclean", unit =>" %"},
  "p49SummerModeTemp"		=> {parent=>"pHeat2",	argMin => "10",	argMax =>   "24",	type =>"pclean", unit =>" °C"},
  "p50SummerModeHysteresis"	=> {parent=>"pHeat2",	argMin => "1",	argMax =>    "5",	type =>"pclean", unit =>" K"},
  "p54MinPumpCycles"		=> {parent=>"pCircPump",argMin => "1",	argMax =>   "24",	type =>"pclean", unit =>" /Day"},
  "p55MaxPumpCycles"		=> {parent=>"pCircPump",argMin => "25",	argMax =>  "288",	type =>"pclean", unit =>" /Day"},
  "p56OutTempMaxPumpCycles"	=> {parent=>"pCircPump",argMin => "0",	argMax =>   "20",	type =>"pclean", unit =>" °C"},
  "p57OutTempMinPumpCycles"	=> {parent=>"pCircPump",argMin => "0",	argMax =>   "25",	type =>"pclean", unit =>" °C"},
  "p58SuppressTempCaptPumpStart"=> {parent=>"pCircPump",argMin => "0",	argMax =>  "120",	type =>"pclean", unit =>" s"},
  "p75PassiveCooling"		=> {parent=>"pFan", 	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "p77OutTempFilterTime"	=> {parent=>"pHeat2", 	argMin => "0",	argMax =>   "24",	type =>"pclean", unit =>" h"},
  "p78DualModePoint"		=> {parent=>"pHeat2",	argMin => "-10",argMax =>   "20",	type =>"pclean", unit =>" °C"},
  "p79BoosterTimeoutHC"		=> {parent=>"pHeat2",	argMin => "0",	argMax =>   "60",	type =>"pclean", unit =>" min"},
  "p80EnableSolar"		=> {parent=>"pSolar",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "pClockDay"			=> {parent=>"sTimedate",argMin => "1",	argMax =>   "31",	type =>"pclean", unit =>""},
  "pClockMonth"			=> {parent=>"sTimedate",argMin => "1",	argMax =>   "12",	type =>"pclean", unit =>""},
  "pClockYear"			=> {parent=>"sTimedate",argMin => "12",	argMax =>   "20",	type =>"pclean", unit =>""},
  "pClockHour"			=> {parent=>"sTimedate",argMin => "0",	argMax =>   "23",	type =>"pclean", unit =>""},
  "pClockMinutes"		=> {parent=>"sTimedate",argMin => "0",	argMax =>   "59",	type =>"pclean", unit =>""},
  "progDHWStartTime"		=> {parent=>"pDHWProg",	argMin =>"00:00",argMax =>"23:59",	type =>"ptime",  unit =>""},
  "progDHWEndTime"		=> {parent=>"pDHWProg",	argMin =>"00:00",argMax =>"23:59",	type =>"ptime",  unit =>""},
  "progDHWEnable"		=> {parent=>"pDHWProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progDHWMonday"		=> {parent=>"pDHWProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progDHWTuesday"		=> {parent=>"pDHWProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progDHWWednesday"		=> {parent=>"pDHWProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progDHWThursday"		=> {parent=>"pDHWProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progDHWFriday"		=> {parent=>"pDHWProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progDHWSaturday"		=> {parent=>"pDHWProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progDHWSunday"	 	=> {parent=>"pDHWProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progHC1StartTime"           	=> {parent=>"pHeatProg",argMin =>"00:00",argMax =>"23:59",	type =>"ptime",  unit =>""}, 
  "progHC1EndTime"		=> {parent=>"pHeatProg",argMin =>"00:00",argMax =>"23:59",	type =>"ptime",  unit =>""},
  "progHC1Enable"		=> {parent=>"pHeatProg",argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progHC1Monday"		=> {parent=>"pHeatProg",argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progHC1Tuesday"		=> {parent=>"pHeatProg",argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progHC1Wednesday"		=> {parent=>"pHeatProg",argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progHC1Thursday"		=> {parent=>"pHeatProg",argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progHC1Friday"		=> {parent=>"pHeatProg",argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progHC1Saturday"		=> {parent=>"pHeatProg",argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progHC1Sunday"		=> {parent=>"pHeatProg",argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progHC2StartTime"		=> {parent=>"pHeatProg",argMin =>"00:00",argMax =>"23:59",	type =>"ptime",  unit =>""}, 
  "progHC2EndTime"		=> {parent=>"pHeatProg",argMin =>"00:00",argMax =>"23:59",	type =>"ptime",  unit =>""},
  "progHC2Enable"		=> {parent=>"pHeatProg",argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progHC2Monday"		=> {parent=>"pHeatProg",argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progHC2Tuesday"		=> {parent=>"pHeatProg",argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progHC2Wednesday"		=> {parent=>"pHeatProg",argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progHC2Thursday"		=> {parent=>"pHeatProg",argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progHC2Friday"		=> {parent=>"pHeatProg",argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progHC2Saturday"		=> {parent=>"pHeatProg",argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progHC2Sunday"		=> {parent=>"pHeatProg",argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progFAN1StartTime"		=> {parent=>"pFanProg",	argMin =>"00:00",argMax =>"23:59",	type =>"ptime",  unit =>""},
  "progFAN1EndTime"		=> {parent=>"pFanProg",	argMin =>"00:00",argMax =>"23:59",	type =>"ptime",  unit =>""},
  "progFAN1Enable"		=> {parent=>"pFanProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progFAN1Monday"		=> {parent=>"pFanProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progFAN1Tuesday"		=> {parent=>"pFanProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progFAN1Wednesday"		=> {parent=>"pFanProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progFAN1Thursday"		=> {parent=>"pFanProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progFAN1Friday"		=> {parent=>"pFanProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progFAN1Saturday"		=> {parent=>"pFanProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progFAN1Sunday"		=> {parent=>"pFanProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progFAN2StartTime"		=> {parent=>"pFanProg",	argMin =>"00:00",argMax =>"23:59",	type =>"ptime",  unit =>""},
  "progFAN2EndTime"		=> {parent=>"pFanProg",	argMin =>"00:00",argMax =>"23:59",	type =>"ptime",  unit =>""},
  "progFAN2Enable"		=> {parent=>"pFanProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progFAN2Monday"		=> {parent=>"pFanProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progFAN2Tuesday"		=> {parent=>"pFanProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progFAN2Wednesday"		=> {parent=>"pFanProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progFAN2Thursday"		=> {parent=>"pFanProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progFAN2Friday"		=> {parent=>"pFanProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progFAN2Saturday"		=> {parent=>"pFanProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""},
  "progFAN2Sunday"		=> {parent=>"pFanProg",	argMin => "0",	argMax =>    "1",	type =>"pclean", unit =>""} 
 );

my %setsonly214 = (
  "ResetErrors"			=> {cmd2=>"F8",		argMin => "0",	 argMax =>   "0",	type =>"0clean", unit =>""}
);


########################################################################################
#
# %gets - all supported protocols are listed without header and footer
#
########################################################################################

my %getsonly439 = (
#"debug_read_raw_register_slow"	=> { },
  "sSol"		=> {cmd2=>"16",     type =>"16sol",  unit =>""},
  "sHistory"		=> {cmd2=>"09",     type =>"09his",  unit =>""},
  "sLast10errors"	=> {cmd2=>"D1",     type =>"D1last", unit =>""},
  "sFan"  		=> {cmd2=>"E8",     type =>"E8fan",  unit =>""},
  "sDHW"		=> {cmd2=>"F3",     type =>"F3dhw",  unit =>""},
  "sHC1"		=> {cmd2=>"F4",     type =>"F4hc1",  unit =>""},
  "sHC2"		=> {cmd2=>"F5",     type =>"F5hc2",  unit =>""},
  "sControl"		=> {cmd2=>"F2",     type =>"F2ctrl", unit =>""},
  "sGlobal"		=> {cmd2=>"FB",     type =>"FBglob", unit =>""},  #allFB
  "sTimedate"		=> {cmd2=>"FC",     type =>"FCtime", unit =>""},
  "sFirmware"		=> {cmd2=>"FD",     type =>"FDfirm", unit =>""},
  "sFirmware-Id" 	=> {cmd2=>"FE",     type =>"FEfirmId", unit =>""},
  "sDisplay"		=> {cmd2=>"0A0176", type =>"0A0176Dis", unit =>""},
  "sBoostDHWTotal"	=> {cmd2=>"0A0924", cmd3=>"0A0925",	type =>"1clean", unit =>" kWh"},
  "sBoostHCTotal"	=> {cmd2=>"0A0928", cmd3=>"0A0929",	type =>"1clean", unit =>" kWh"},
  "sHeatRecoveredDay"	=> {cmd2=>"0A03AE", cmd3=>"0A03AF",	type =>"1clean", unit =>" Wh"},
  "sHeatRecoveredTotal"	=> {cmd2=>"0A03B0", cmd3=>"0A03B1",	type =>"1clean", unit =>" kWh"},
  "sHeatDHWDay"		=> {cmd2=>"0A092A", cmd3=>"0A092B",	type =>"1clean", unit =>" Wh"},
  "sHeatDHWTotal"	=> {cmd2=>"0A092C", cmd3=>"0A092D",	type =>"1clean", unit =>" kWh"},
  "sHeatHCDay"		=> {cmd2=>"0A092E", cmd3=>"0A092F",	type =>"1clean", unit =>" Wh"},
  "sHeatHCTotal" 	=> {cmd2=>"0A0930", cmd3=>"0A0931",	type =>"1clean", unit =>" kWh"},
  "sElectrDHWDay"	=> {cmd2=>"0A091A", cmd3=>"0A091B",	type =>"1clean", unit =>" Wh"},
  "sElectrDHWTotal" 	=> {cmd2=>"0A091C", cmd3=>"0A091D",	type =>"1clean", unit =>" kWh"},
  "sElectrHCDay" 	=> {cmd2=>"0A091E", cmd3=>"0A091F",	type =>"1clean", unit =>" Wh"},
  "sElectrHCTotal"	=> {cmd2=>"0A0920", cmd3=>"0A0921",	type =>"1clean", unit =>" kWh"},
  "party-time"		=> {cmd2=>"0A05D1", argMin =>"00:00", argMax =>"23:59", type =>"8party", unit =>""} # value 1Ch 28dec is 7 ; value 1Eh 30dec is 7:30
  );


my %getsonly539 = (  #info from belu and godmorgon
  "sFlowRate"		=> {cmd2=>"0A033B", type =>"1clean", unit =>" cl/min"},
  "sHumMaskingTime"	=> {cmd2=>"0A064F", type =>"1clean", unit =>" min"},
  "sHumThreshold"	=> {cmd2=>"0A0650", type =>"1clean", unit =>" %"},
  "sHeatingRelPower"	=> {cmd2=>"0A069A", type =>"1clean", unit =>" %"},
  "sComprRelPower"	=> {cmd2=>"0A069B", type =>"1clean", unit =>" %"},
  "sComprRotUnlimit"	=> {cmd2=>"0A069C", type =>"1clean", unit =>" Hz"},
  "sComprRotLimit"	=> {cmd2=>"0A069D", type =>"1clean", unit =>" Hz"},
  "sOutputReduction"	=> {cmd2=>"0A06A4", type =>"1clean", unit =>" %"},
  "sOutputIncrease"	=> {cmd2=>"0A06A5", type =>"1clean", unit =>" %"},
  "sHumProtection"	=> {cmd2=>"0A09D1", type =>"1clean", unit =>""},
  "sSetHumidityMin"	=> {cmd2=>"0A09D2", type =>"1clean", unit =>" %"},
  "sSetHumidityMax"	=> {cmd2=>"0A09D3", type =>"1clean", unit =>" %"},
  "sCoolHCTotal"	=> {cmd2=>"0A0648", cmd3 =>"0A0649", type =>"1clean", unit =>" kWh"},
  "sDewPointHC1"	=> {cmd2=>"0B0264", type =>"5temp",  unit =>" °C"},
  "sDewPointHC2"	=> {cmd2=>"0C0264", type =>"5temp",  unit =>" °C"}
 );
%getsonly539=(%getsonly539, %getsonly439);

my %getsonly2xx = (
  "pDefrostEva"		=> {cmd2=>"03", type =>"03pxx206", unit =>""},
  "pDefrostAA"		=> {cmd2=>"04", type =>"04pxx206", unit =>""},
  "pHeat1"		=> {cmd2=>"05", type =>"05pxx206", unit =>""},
  "pHeat2"		=> {cmd2=>"06", type =>"06pxx206", unit =>""},
  "pDHW"		=> {cmd2=>"07", type =>"07pxx206", unit =>""},
  "pSolar"		=> {cmd2=>"08", type =>"08pxx206", unit =>""},
  "sHistory"		=> {cmd2=>"09", type =>"09his206", unit =>""},
  "pCircPump"		=> {cmd2=>"0A", type =>"0Apxx206", unit =>""},
  "pHeatProg"		=> {cmd2=>"0B", type =>"0Bpxx206", unit =>""},
  "pDHWProg"		=> {cmd2=>"0C", type =>"0Cpxx206", unit =>""},
  "pFanProg"   		=> {cmd2=>"0D", type =>"0Dpxx206", unit =>""},
  "pRestart"		=> {cmd2=>"0E", type =>"0Epxx206", unit =>""},
  "pAbsence"		=> {cmd2=>"0F", type =>"0Fpxx206", unit =>""},
  "pDryHeat"		=> {cmd2=>"10", type =>"10pxx206", unit =>""},
  "sSol"		=> {cmd2=>"16", type =>"16sol",    unit =>""},
  "p01-p12"		=> {cmd2=>"17", type =>"17pxx206", unit =>""},
  "sProgram"  		=> {cmd2=>"EE", type =>"EEprg206", unit =>""},
  "sFan"  		=> {cmd2=>"E8", type =>"E8fan206", unit =>""},
  "sControl"  		=> {cmd2=>"F2", type =>"F2ctrl",   unit =>""},
  "sDHW"		=> {cmd2=>"F3", type =>"F3dhw",    unit =>""},
  "sHC2"		=> {cmd2=>"F5", type =>"F5hc2",    unit =>""},
  "sSystem"		=> {cmd2=>"F6", type =>"F6sys206", unit =>""},
  "sTimedate" 		=> {cmd2=>"FC", type =>"FCtime206",unit =>""},
  "inputVentilatorSpeed"=> {parent=>"sGlobal",		unit =>" %"},
  "outputVentilatorSpeed"=> {parent=>"sGlobal",		unit =>" %"},
  "mainVentilatorSpeed"	=> {parent=>"sGlobal",		unit =>" %"},
  "inputVentilatorPower"=> {parent=>"sGlobal",		unit =>" %"},
  "outputVentilatorPower"=> {parent=>"sGlobal",		unit =>" %"},
  "mainVentilatorPower"	=> {parent=>"sGlobal",		unit =>" %"}, 
 );
my %getsonly206 = (
  "sHC1"		=> {cmd2=>"F4", type =>"F4hc1",    unit =>""},
  "pFan"		=> {cmd2=>"01", type =>"01pxx206", unit =>""},
  "sLast10errors"	=> {cmd2=>"D1", type =>"D1last206",unit =>""},
  "sFirmware"		=> {cmd2=>"FD", type =>"FDfirm",   unit =>""},
  "sGlobal"		=> {cmd2=>"FB", type =>"FBglob206",unit =>""}
 );

my %getsonly214 = (
  "pFan"		=> {cmd2=>"01", type =>"01pxx214", unit =>""},
  "pExpert"		=> {cmd2=>"02", type =>"02pxx206", unit =>""},
  "sControl"  		=> {cmd2=>"F2", type =>"F2type",   unit =>""},
  "sHC1"		=> {cmd2=>"F4", type =>"F4hc1214", unit =>""},
  #"sLVR"  		=> {cmd2=>"E8", type =>"E8tyype",  unit =>""},
  #"sF0"  		=> {cmd2=>"F0", type =>"F0type",   unit =>""},
  #"sF1"  		=> {cmd2=>"F1", type =>"F1type",   unit =>""},
  #"sEF"  		=> {cmd2=>"EF", type =>"EFtype",   unit =>""},
  "sGlobal"	     	=> {cmd2=>"FB", type =>"FBglob214",unit =>""}  
 ); 

 my %getsonly214j = (  # contribution from joerg  hellijo  Antwort #1085  Juni 2022
  "pFan"		=> {cmd2=>"01", type =>"01pxx214", unit =>""},
  "pExpert"		=> {cmd2=>"02", type =>"02pxx206", unit =>""},
  "sControl"  		=> {cmd2=>"F2", type =>"F2type",   unit =>""},
  "sHC1"		=> {cmd2=>"F4", type =>"F4hc1214j", unit =>""},
  #"sLVR"  		=> {cmd2=>"E8", type =>"E8tyype",  unit =>""},
  #"sF0"  		=> {cmd2=>"F0", type =>"F0type",   unit =>""},
  #"sF1"  		=> {cmd2=>"F1", type =>"F1type",   unit =>""},
  #"sEF"  		=> {cmd2=>"EF", type =>"EFtype",   unit =>""},
  "sGlobal"	     	=> {cmd2=>"FB", type =>"FBglob214",unit =>""}  
 ); 


my %sets=       (%sets439539common, %sets439only);
my %gets=       (%getsonly439, %sets);
my %OpMode=     ("1" =>"standby", "11" => "automatic", "3" =>"DAYmode", "4" =>"setback", "5" =>"DHWmode", "14" =>"manual", "0" =>"emergency");   
my %Rev_OpMode= reverse %OpMode;
my %OpModeHC=   ("1" =>"normal", "2" => "setback", "3" =>"standby", "4" =>"restart", "5" =>"restart");
my %opMode2=    ("0" =>"manual", "1" => "automatic");
my %SomWinMode= ("01" =>"winter", "02" => "summer");
my %weekday=    ("0" =>"Monday", "1" => "Tuesday", "2" =>"Wednesday", "3" => "Thursday", "4" => "Friday", "5" =>"Saturday", "6" => "Sunday" );
#my %weekdaymap= ( "1" =>"Mon", "2" => "Tue", "3" =>"Wed", "4" => "Thu", "5" => "Fri", "6" =>"Sat", "7" => "Sun" );
my %faultmap=   ( "0" =>"n.a.", "1" => "F01_AnodeFault", "2" => "F02_SafetyTempDelimiterEngaged", "3" => "F03_HighPreasureGuardFault", "4" => "F04_LowPreasureGuardFault", "5" => "F05_OutletFanFault", "6" => "F06_InletFanFault", "7" => "F07_MainOutputFanFault", "11" => "F11_LowPreasureSensorFault", "12"=> "F12_HighPreasureSensorFault", "15" => "F15_DHW_TemperatureFault",  "17" => "F17_DefrostingDurationExceeded", "20" => "F20_SolarSensorFault", "21" => "F21_OutsideTemperatureSensorFault", "22" => "F22_HotGasTemperatureFault", "23" => "F23_CondenserTemperatureSensorFault", "24" => "F24_EvaporatorTemperatureSensorFault", "26" => "F26_ReturnTemperatureSensorFault", "28" => "F28_FlowTemperatureSensorFault", "29" => "F29_DHW_TemperatureSensorFault", "30" => "F30_SoftwareVersionFault", "31" => "F31_RAMfault", "32" => "F32_EEPromFault", "33" => "F33_ExtractAirHumiditySensor", "34" => "F34_FlowSensor", "35" => "F35_minFlowCooling", "36" => "F36_MinFlowRate", "37" => "F37_MinWaterPressure", "40" => "F40_FloatSwitch", "50" => "F50_SensorHeatPumpReturn", "51" => "F51_SensorHeatPumpFlow",  "52" => "F52_SensorCondenserOutlet" );
my $firstLoadAll= 0;
my $noanswerreceived= 0;
my $internalHash;
 
########################################################################################
#
# THZ_Initialize($)
# 
# Parameter hash
#
########################################################################################
sub THZ_Initialize($) {
  my ($hash) = @_;

# Provider
  $hash->{ReadFn}  = "THZ_Read";
  $hash->{WriteFn} = "THZ_Write";
  $hash->{ReadyFn} = "THZ_Ready";
  
# Normal devices
  $hash->{DefFn}   = "THZ_Define";
  $hash->{UndefFn} = "THZ_Undef";
  $hash->{GetFn}   = "THZ_Get";
  $hash->{SetFn}   = "THZ_Set";
  $hash->{NotifyFn} = "THZ_Notify";
  $hash->{AttrFn}  = "THZ_Attr";
  $hash->{FW_detailFn}  ="THZ_detailFn";
  $hash->{AttrList}= "IODev do_not_notify:1,0  ignore:0,1 dummy:1,0 showtime:1,0 "
		    ."interval_sGlobal:0,60,120,180,300,600,3600,7200,43200,86400 "
		    ."interval_sSol:0,60,120,180,300,600,3600,7200,43200,86400 "
		    ."interval_sDHW:0,60,120,180,300,600,3600,7200,43200,86400 "
		    ."interval_sHC1:0,60,120,180,300,600,3600,7200,43200,86400 "
		    ."interval_sHC2:0,60,120,180,300,600,3600,7200,43200,86400 "
		    ."interval_sFan:0,60,120,180,300,600,3600,7200,43200,86400 "
		    ."interval_sHistory:0,3600,7200,28800,43200,86400 "
		    ."interval_sLast10errors:0,3600,7200,28800,43200,86400 "
		    ."interval_sHeatRecoveredDay:0,1200,3600,7200,28800,43200,86400 "
		    ."interval_sHeatRecoveredTotal:0,3600,7200,28800,43200,86400 "
		    ."interval_sHeatDHWDay:0,1200,3600,7200,28800,43200,86400 "
		    ."interval_sHeatDHWTotal:0,3600,7200,28800,43200,86400 "
		    ."interval_sHeatHCDay:0,1200,3600,7200,28800,43200,86400 "
		    ."interval_sHeatHCTotal:0,3600,7200,28800,43200,86400 "
		    ."interval_sElectrDHWDay:0,1200,3600,7200,28800,43200,86400 "
		    ."interval_sElectrDHWTotal:0,3600,7200,28800,43200,86400 "
		    ."interval_sElectrHCDay:0,1200,3600,7200,28800,43200,86400 "
		    ."interval_sElectrHCTotal:0,3600,7200,28800,43200,86400 "
		    ."interval_sBoostDHWTotal:0,3600,7200,28800,43200,86400 "
		    ."interval_sBoostHCTotal:0,3600,7200,28800,43200,86400 "
		    ."interval_sFlowRate:0,3600,7200,28800,43200,86400 "
		    ."interval_sDisplay:0,60,120,180,300 "
		    ."firmware:4.39,2.06,2.14,2.14j,5.39,4.39technician,5.39technician "
		    ."interval_sDewPointHC1:0,60,120,180,300 "
		    ."simpleReadTimeout:0.25,0.5,0.75,1,2,4,6 " #standard has been 0.75 since msg468515 If blocking attribut is NOT enabled then set the timeout value to a maximum value of 0.75 sec.
		    ."nonblocking:0,1 "
		    . $readingFnAttributes;
  $data{FWEXT}{"/THZ_PrintcurveSVG"}{FUNC} = "THZ_PrintcurveSVG";
}


########################################################################################
#
# THZ_define
#
# Parameter hash and configuration
#
########################################################################################
sub THZ_Define($$) {
  my ($hash, $def) = @_;
  my @a = split("[ \t][ \t]*", $def);
  my $name = $a[0];
  $hash->{VERSION} = $thzversion;
  return "wrong syntax. Correct is: define <name> THZ ".
  				"{devicename[\@baudrate]|ip:port}"
  				 if(@a != 3);
  				
  DevIo_CloseDev($hash);
  my $dev  = $a[2];
  if($dev eq "none") {
     Log3 $name, 2, "$name device is none, commands will be echoed only";
    $attr{$name}{dummy} = 1;
    return undef;
  }
  $hash->{DeviceName} = $dev;
  $attr{$name}{devStateIcon} = 'opened:10px-kreis-gruen disconnected:10px-kreis-rot';
  #my $ret = DevIo_OpenDev($hash, 0, "THZ_Refresh_all_gets");
  my $ret = DevIo_OpenDev($hash, 0, undef);
  THZ_Refresh_all_gets($hash) if($init_done);
  #THZ_Testloopapproach($hash);
  return $ret;
}


########################################################################################
#
# THZ_Notify - used at startup to delay THZ_Refresh_all_gets after init_done
# 
#
########################################################################################


sub THZ_Notify($$) {
  my ($hash, $dev) = @_;
  return "" if (IsDisabled($hash->{NAME}));
  if ($dev->{NAME} eq "global" && grep (m/^INITIALIZED|REREADCFG$/, @{$dev->{CHANGED}})) {
    #Log3 $hash->{NAME}, 3, "thzversion = $thzversion ";
    THZ_Refresh_all_gets($hash);
  }
  return undef;
}





########################################################################################
#
# THZ_Refresh_all_gets - Called once refreshes current reading for all gets and initializes the regular interval calls
# refresh all registers; the register with interval_command ne 0 will keep on refreshing
# Parameter $hash
# 
########################################################################################
sub THZ_Refresh_all_gets($) {
  my ($hash) = @_;
  RemoveInternalTimer(0, "THZ_GetRefresh");
  Log3 $hash->{NAME}, 5, "thzversion = $thzversion ";
  my $timedelay= 65; 						#5 seconds were ok but considering winter 2017/2018 I prefer to increase
  foreach  my $cmdhash  (keys %gets) {   
        my %par = (hash => $hash, command => $cmdhash);
#        my %par = (hash => $hash, command => $cmdhash, NAME => $hash->{NAME} ); #does not group in apptime
        InternalTimer((gettimeofday() + $timedelay), "THZ_GetRefresh", \%par, 0);		#increment 0.6 $timedelay++
        $timedelay += 2;                      #0.6 seconds were ok but considering winter 2017/2018 I prefer to increase
	$timedelay += 5 if (AttrVal($hash->{NAME}, "firmware" , "4.39")  =~ /^2/);  # I added 5 more seconds for joerg 05/2022
  }  
}


########################################################################################
#
# THZ_GetRefresh - Called in regular intervals to obtain current reading
#
# Parameter (hash => $hash, command => "allFB" )
# it get the intervall directly from a attribute; the register with interval_command ne 0 will keep on refreshing
########################################################################################
sub THZ_GetRefresh($) {
    my ($par)=@_;
    my $hash=$par->{hash};
    my $command=$par->{command};    
    my $name =$hash->{NAME};
    
    if (ReadingsAge($name ,'z_Last_fhem_err', 120) < 120) {
            Log3 $hash, 5, "[$name] THZ_GetRefresh($command) rescheduled (stop 120s after one error)";
            InternalTimer(gettimeofday() + 200, "THZ_GetRefresh", $par, 1);
            return;
    }
    if (ReadingsAge($name ,$command, 41) < 41) {
            Log3 $hash, 5, "[$name] THZ_GetRefresh($command) rescheduled (the reading has been updated in the last 40s)";
            InternalTimer(gettimeofday() + 300, "THZ_GetRefresh", $par, 1);
            return;
    }
    
    if (AttrVal($name, "nonblocking" , "0")  =~ /1/ ) {
        if (!(exists($hash->{helper}{RUNNING_PID}))) {
            DevIo_CloseDev($hash);          #close device in parent process
            #$hash->{STATE}="disconnected";
            my $bctimeout = (AttrVal($name, "simpleReadTimeout", "0.5"))*2 +1;
            my $dev = $hash->{DeviceName};
            #$hash->{connection} =$hash->{STATE} ."||". $hash->{USBDev} ."||". $hash->{TCPDev} ."||". $hash->{conn} ."||". $hash->{FD} ."||". $selectlist{"$name.$dev"} ;
            $hash->{connection} =$hash->{STATE} ."||". $hash->{TCPDev} ."||". $hash->{FD} ."||". $selectlist{"$name.$dev"} ;
            Log3 $hash, 3,  $hash->{connection};
            
            $hash->{helper}{RUNNING_PID} = BlockingCall("THZ_GetNB", $name."|".$command, "THZ_GetNBDone", $bctimeout, "THZ_GetNBAbort", $hash);
            Log3 $hash, 3, "[$name] THZ_GetRefresh($command) BlockingCall started";
        }
        else {
            Log3 $hash, 3, "[$name] THZ_GetRefresh($command) rescheduled (Blocking Call is still running)";
            InternalTimer(gettimeofday() + 4, "THZ_GetRefresh", $par, 1);
            return;
        }
    }
    elsif (($hash->{STATE} ne "disconnected") and (ReadingsVal($name, "state", " ")  ne "disconnected" )) {
        THZ_Get($hash, $name, $command);        
    }
    
    my $interval = AttrVal($name, ("interval_".$command), 0);
    if (($interval) and ($hash->{STATE} ne "disconnected"))   {
            $interval = 60 if ($interval < 60); #do not allow intervall <60 sec 
			InternalTimer(gettimeofday()+ $interval, "THZ_GetRefresh", $par, 1) ;
    }
	
    if ($command =~ "sFirmware") {  # model summary for statistics
        my $sFirmwareId = join('', (split(/ |:/, ReadingsVal($name,"sFirmware-Id"," : : : ")))[0..6]);
        my $sFirmware= (split(/ /, ReadingsVal($name,"sFirmware","  ")))[1];
        $hash->{model}= sprintf("%.5s%s%s", AttrVal($name,"firmware","n.a.")."______", $sFirmware, $sFirmwareId);
       }
    
    return;
}



#####################################
# THZ_Write -- simple write
# Parameter:  hash and message HEX
#
########################################################################################
sub THZ_Write($$) {
    my ($hash,$bstring) = @_;
    my $name = $hash->{NAME};
    Log3 $hash->{NAME}, 5, "$hash->{NAME} sending $bstring"; 
    DevIo_SimpleWrite($hash, $bstring, 1);
}

#####################################
# sub THZ_Read($)
# called from the global loop, when the select for hash reports data
# used just for testing the interface
########################################################################################
sub THZ_Read($) {
    my ($hash) = @_;
    my $buf = DevIo_SimpleRead($hash);
    return "" if(!defined($buf));
    my $name = $hash->{NAME};
    my $msg=uc(unpack('H*', $buf));
    my $err;
    if ($msg =~ m/^02|/^1002) {
	THZ_Write($hash,  "10");
	($err, $msg) = THZ_ReadAnswer($hash);	
	THZ_Write($hash,  "10");  
	($err, $msg) = THZ_decode($msg)  if  (!defined($err));
	$msg= THZ_Parse1($hash,$msg)	 if  (!defined($err));  #only missing unit and readingupdate
	$msg.= $err 			 if  (defined($err));
     }
    Log3 $name, 3, "$name/RAW: $msg";
}



sub THZ_testtimer($) {
    my ($hash) = @_;
    my $counter=1;
    my $stringa = ("starttest \n");
    foreach my $a (keys %intAt) {
        if ($intAt{$a}{FN} eq "THZ_GetRefresh")	{
            $stringa = $stringa . ("timer ". $counter ." ARG". $intAt{$a}{ARG} ."fn " . $intAt{$a}{FN} ."\n") ;
            $counter+=1;
        }
    }
    Log3 $hash->{NAME}, 5, $stringa;
}

     
#####################################
#
# THZ_Ready($) - Cchecks the status
#
# Parameter hash
#
########################################################################################
sub THZ_Ready($) {
    my ($hash) = @_;
    if($hash->{STATE} eq "disconnected") { #RemoveInternalTimer(0, "THZ_GetRefresh"); #non necessario in THZ_getrefresh non vengono piu' rinnoovati
        #THZ_testtimer($hash);
        select(undef, undef, undef, 0.010); #equivalent to sleep 10ms
        #Log3 $hash->{NAME}, 3, "THZ_Ready: readyevent";
        return DevIo_OpenDev($hash, 1, "THZ_Refresh_all_gets")
    }	
    # This is relevant for windows/USB only
    my $po = $hash->{USBDev};
    if($po) {
        my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
        return ($InBytes>0);
    }
}

#####################################
#
# THZ_Set - provides a method for setting the heatpump
#
# Parameters: hash and command to be sent to the interface
#
########################################################################################
sub THZ_Set($@){
    my ($hash,  @a) = @_;
    my $dev = $hash->{DeviceName};
    my $name = $hash->{NAME}; 
    return "\"set $name\" needs at least two parameters: <device-parameter> and <value-to-be-modified>" if(@a < 2);
    my $cmd = $a[1];
    my $arg = $a[2];
    my $arg1 = "00:00";
    my ($err, $msg) =("", " ");
    my $cmdhash = $sets{$cmd};
    #return "Unknown argument $cmd, choose one of " . join(" ", sort keys %sets) if(!defined($cmdhash));
    if(!defined($cmdhash)) {
        my $setList;
        foreach my $key (sort keys %sets) {
            my $value = $sets{$key};
            $setList .= $key;
            #if (($value->{type} eq "0clean" or $value->{type} eq "1clean") and $value->{unit} eq "") {
            if ($value->{type} =~ /clean/ ) {
                #if (($value->{argMax} - $value->{argMin})<2 ) {$setList .= ":uzsuToggle," . join (",", ($value->{argMin} .. $value->{argMax})) . " ";}
                if (($value->{argMax} - $value->{argMin})<5 ) {$setList .= ":uzsuSelectRadio," . join (",", ($value->{argMin} .. $value->{argMax})) . " ";}
                else  					 {$setList .= ":textField ";}
            }
            elsif ($value->{type} eq "2opmode"){
                $setList .= ":" . join (",", (sort {lc $a cmp lc $b} values %OpMode)) . " ";
            }
            else {
                #$setList .= ":textField ";
                $setList .= " ";
            }
        }
        return "Unknown argument $cmd, choose one of  $setList";
    }
    return "\"set $name $cmd\" needs at least one further argument: <value-to-be-modified>" if(!defined($arg));
    my $cmdHex2 = $cmdhash->{cmd2};
    my $argMax = $cmdhash->{argMax};
    my $argMin = $cmdhash->{argMin};
    #-- check the parameter range
    if ($cmdhash->{type} =~ /ptime/) {          
        $arg1=undef;
        return "Argument does not match the allowed inerval Min $argMin ...... Max $argMax " if (($arg ne "n.a.")  and ($arg !~ /^(?:\d|[01]\d|2[0-3]):[0-5]\d$/ )) ;
    }
    elsif ($cmdhash->{type} =~ /7prog|8party/) {          
        ($arg, $arg1)=split('--', $arg);
        return "Argument does not match the allowed inerval Min $argMin ...... Max $argMax " if (($arg ne "n.a.") and ($arg1 ne "n.a.") and (($arg !~ /^(?:\d|[01]\d|2[0-3]):[0-5]\d$/ )  or ($arg1 !~ /^24:00|(?:\d|[01]\d|2[0-3]):[0-5]\d$/ )) ) ;
    }
    elsif ($cmdhash->{type} eq "2opmode") {
        $arg1=undef;
        $arg=$Rev_OpMode{$arg};
        return "Unknown argument $arg1: $cmd supports  " . join(" ", sort values %OpMode) 	if(!defined($arg));
    }
    else {
        $arg1=undef;
        return "Argument does not match the allowed inerval Min $argMin ...... Max $argMax " if(($arg > $argMax) or ($arg < $argMin));
    }
    #--
    my $i=0;  my $parsingrule;
    THZ_AvoidCollisions($hash);
    my $parent = $cmdhash->{parent};
    #if I have a father read from it: important for older firmwares
    if(defined($parent) ) {
        my $parenthash=$gets{$parent};
        $cmdHex2 = $parenthash->{cmd2};	#overwrite $cmdHex2 with the parent
        Log3 $hash->{NAME}, 5, "searching for parent; parenthash= $parenthash, parent = $parent, cmdHex2 = $cmdHex2  ";
        $cmdHex2=THZ_encodecommand($cmdHex2, "get");  #read before write the register
        ($err, $msg) = THZ_Get_Comunication($hash,  $cmdHex2);
        if (defined($err))     {
            $err="THZ_Set: error reading register: '$err'";
            Log3 $hash->{NAME}, 3, $err;
            readingsSingleUpdate($hash, "z_Last_fhem_err", $err, 0);
            return ($msg ."\n msg " . $err);
        }
        substr($msg, 0, 2, ""); 		#remove the checksum from the head of the payload
        Log3 $hash->{NAME}, 5, "read before write from THZ: $msg";
        #--
        $parsingrule = $parsinghash{$parenthash->{type}};
        for (@$parsingrule) {
            last if ((@$parsingrule[$i]->[0]) =~ m/$cmd/);
            $i++;
        }
        select(undef, undef, undef, 0.25);
    }
    else {
        $msg =  $cmdHex2 . "0000";
        my $msgtype =$cmdhash->{type};
        $parsingrule = $parsinghash{$msgtype} if(defined($msgtype));
    }
    my $pos = @$parsingrule[$i]->[1] -2; #I removed the checksum
    my $len = @$parsingrule[$i]->[2];
    my $parsingtype = @$parsingrule[$i]->[3];
    my $dec = @$parsingrule[$i]->[4];
    Log3 $hash->{NAME}, 5, "write command (parsed element/pos/len/dec/parsingtype): $i / $pos / $len / $dec / $parsingtype";
    $arg *= $dec                                                  if ($dec != 1);
    $arg  = time2quaters($arg)                                    if ($parsingtype eq "quater");
    $arg= join('', (split(':', $arg)))                            if ($parsingtype eq "hex2time"); # only in firmware 2.x
    #$arg= eval(join('*100+', (split(':', $arg))))                 if ($parsingtype eq "hex2time"); #just in case the above does not work
    $arg=(hex(substr($msg, $pos, 1)) & (15-2**$1)) | (2**$1*$arg) if ($parsingtype =~ /bit(\d)/);
    $arg  = substr((sprintf(("%0".$len."X"), $arg)), (-1*$len)); #04X converts to hex and fills up 0s; for negative, it must be trunckated. 
    substr($msg, $pos, $len, $arg);
    if (defined($arg1))  {		#only in case of "8party" or "7prog" 
        $arg1  = time2quaters($arg1);
        $arg1  = substr((sprintf(("%02X"), $arg1)), -2);
        $pos = @$parsingrule[($i+1)]->[1] -2;
        substr($msg, $pos, $len, $arg1);
    }
    Log3 $hash->{NAME}, 5, "THZ_Set: '$cmd $arg $msg' ... Check if port is open. State = '($hash->{STATE})'";
    $cmdHex2=THZ_encodecommand($msg,"set");
    ($err, $msg) = THZ_Get_Comunication($hash,  $cmdHex2);
    #$err=undef;
    if (defined($err))  {
        $err="THZ_Set: Error msg:  $err -- $cmdHex2 -> $msg";
        Log3 $hash->{NAME}, 3, $err;
        readingsSingleUpdate($hash, "z_Last_fhem_err", $err, 0);
        return($cmdHex2 . "-". $msg ."--" . $err);
    }
    else {
        select(undef, undef, undef, 0.25);
        if (defined($gets{$cmd}))   {$msg=THZ_Get($hash, $name, $cmd);}
        else                        {$msg=$cmd.": OK";}   
        #because of F8 reset introduced by andre topic=33211 msg695420
        #take care of program of the week
        if ($a[1] =~ /Mo-So/){
            select(undef, undef, undef, 0.05);
            $a[1] =~ s/Mo-So/Mo-Fr/;	$msg.= "\n" . THZ_Set($hash, @a);
            select(undef, undef, undef, 0.05);
            $a[1] =~ s/Mo-Fr/Sa-So/;	$msg.="\n" . THZ_Set($hash, @a);
        }
        elsif ($a[1] =~ /Mo-Fr/)  	{
            select(undef, undef, undef, 0.05);
            $a[1] =~ s/_Mo-Fr_/_Mo_/;	$msg.="\n" . THZ_Set($hash, @a);
            select(undef, undef, undef, 0.05);
            $a[1] =~ s/_Mo_/_Tu_/ ;	$msg.="\n" . THZ_Set($hash, @a);
            select(undef, undef, undef, 0.05);
            $a[1] =~ s/_Tu_/_We_/ ;	$msg.="\n" . THZ_Set($hash, @a);
            select(undef, undef, undef, 0.05);
            $a[1] =~ s/_We_/_Th_/ ;	$msg.="\n" . THZ_Set($hash, @a);
            select(undef, undef, undef, 0.05);
            $a[1] =~ s/_Th_/_Fr_/ ;  	$msg.="\n" . THZ_Set($hash, @a);
        }
        elsif ($a[1] =~ /Sa-So/){
            select(undef, undef, undef, 0.05);
            $a[1] =~ s/_Sa-So_/_Sa_/; 	$msg.="\n" . THZ_Set($hash, @a);
            select(undef, undef, undef, 0.05);
            $a[1] =~ s/_Sa_/_So_/ ;  	$msg.="\n" . THZ_Set($hash, @a);
        }
        #split _ mo-fr when [3] undefined do nothing, when mo-fr  chiama gli altri
        return ($msg);
    }
}

########################################################################################
#
# THZ_GetNB - NonBlocking Get parameter from heatpump
#
# Parameters: "name | command" (single string)
#
########################################################################################
sub THZ_GetNB($){
    my ($string) = @_;
    my ($name, $cmd) = split("\\|", $string);
    my $hash = $defs{$name};
    my $dev = $hash->{DeviceName};
    my $ret = DevIo_OpenDev($hash, 0, undef);         #open device in child process
    #($hash->{STATE}, $hash->{USBDev} , $hash->{TCPDev} , $hash->{conn} , $hash->{FD} , $selectlist{"$name.$dev"}) =  split("\\|", $hash->{connection});  
    #($hash->{STATE},  $hash->{TCPDev} ,  $hash->{FD} , $selectlist{"$name.$dev"}) =  split("\\|", $hash->{connection});
    #$selectlist{"$name.$dev"} = $hash;
    #open (MYFILE, '>>data.txt');
    #print MYFILE ($hash->{connection} . "\n");
    #close (MYFILE);
    if (defined($ret)) {
        Log3 $hash, 3, "[$name] THZ_GetNB: open device $hash->{DeviceName} error:$ret";
        return ("$name|$cmd|$ret");
    }
    my $msg = THZ_Get($hash, $name, $cmd);
    DevIo_CloseDev($hash);                            #close device in child process
    if ($msg =~ m/\n/m) {                             #error message from get contains \n
        return ("$name|$cmd|[ERROR]");
    }
    else {
        return ("$name|$cmd|$msg");
    }
}

########################################################################################
#
# THZ_GetNBDone - Finish Function
#
# Parameters: "name | command | message" (single string)
#
########################################################################################
sub THZ_GetNBDone($){
    my ($string) = @_;
    my ($name, $cmd, $msg) = split("\\|", $string);
    my $hash = $defs{$name};
    Log3 $hash, 4, "[$name] THZ_GetNBDone: $cmd - $msg";
    readingsSingleUpdate($hash, $cmd, $msg, 1) if ($msg ne "[ERROR]");
    delete($hash->{helper}{RUNNING_PID}) if (defined($hash->{helper}{RUNNING_PID}));
    DevIo_OpenDev($hash, 1, undef); #  if($hash->{STATE} ne "opened");         #reopen device for parent process
    return;
}

########################################################################################
#
# THZ_GetNBAbort - Abort Function
#
# Parameter: hash
#
########################################################################################
sub THZ_GetNBAbort($){
    my ($hash) = @_;
    delete($hash->{helper}{RUNNING_PID}) if (defined($hash->{helper}{RUNNING_PID}));
    DevIo_OpenDev($hash, 1, undef);#   if($hash->{STATE} ne "opened");         #reopen device for parent process
    Log3 $hash->{NAME}, 3, "BlockingCall for ".$hash->{NAME}." was aborted";
    return;
}

########################################################################################
#
# THZ_AvoidCollisions - prevents collisions between parent and child process is used at the beginning of THZ_Get and THZ_Set
#
# Parameter: hash
#
########################################################################################
sub THZ_AvoidCollisions($) {
    my ($hash) = @_;
    # if child found, wait 0,25 second, and kill it
    # maybe after 1 second child would have finished, but its THZ_GetNBDone is blocked
    if (defined($hash->{helper}{RUNNING_PID})){
        select(undef, undef, undef, 0.25);     
        BlockingKill($hash->{helper}{RUNNING_PID});
        delete($hash->{helper}{RUNNING_PID});
        DevIo_OpenDev($hash, 1, undef); #if($hash->{STATE} ne "opened");
        #reset heatpump
        THZ_Write($hash,  "10"); 		    	
        select(undef, undef, undef, 0.1);
        THZ_ReadAnswer($hash);	
        THZ_Write($hash,  "10");
        select(undef, undef, undef, 0.1);
        Log3 $hash->{NAME}, 3, "Possible collision in ".$hash->{NAME}." was aborted";
    }
    return;
}

#####################################
#
# THZ_Get - provides a method for polling the heatpump
#
# Parameters: hash and command to be sent to the interface
#
########################################################################################
sub THZ_Get($@) {
    my ($hash, @a) = @_;
    my $dev = $hash->{DeviceName};
    my $name = $hash->{NAME};
    return "\"get $name\" needs one parameter" if(@a != 2);
    my $cmd = $a[1];
    my ($err, $msg2) =("", " ");
    if ($cmd eq "debug_read_raw_register_slow") {
        THZ_debugread($hash);
        return ("all raw registers read and saved");
    }
    if ($cmd eq "zBackupParameters") {
        $err=THZ_backup_readings($hash);
        return $err;
    }
    my $cmdhash = $gets{$cmd};
    if(!defined($cmdhash)) {
        my $getList;
        foreach my $key (sort keys %gets) {$getList .= "$key:noArg ";}
        $getList .= "zBackupParameters:noArg";
        return "Unknown argument $cmd, choose one of  $getList";
    }
    Log3 $hash->{NAME}, 5, "THZ_Get: Try to get '$cmd'";
    THZ_AvoidCollisions($hash);
    my $parent = $cmdhash->{parent};	#if I have a father read from it
    if(defined($parent) ) {
        my ($seconds, $microseconds) = gettimeofday();
        $seconds= abs($seconds - time_str2num(ReadingsTimestamp($name, $parent, "1970-01-01 01:00:00")));
        my $risultato=ReadingsVal($name, $parent, 0);
        $risultato=THZ_Get($hash, $name, $parent) if ($seconds > 29 );	#update of the parent: if under 29sec use the current value
        #$risultato=THZ_Parse1($hash,"B81700C800BE00A001C20190006402010000E601D602");
        my $parenthash=$gets{$parent}; my $parsingrule = $parsinghash{$parenthash->{type}};
        my $i=0; 
        for  (@$parsingrule) {
            last if ((@$parsingrule[$i]->[0]) =~ m/$cmd/);
            $i++;
            }
        $msg2=(split ' ', $risultato)[$i*2+1];
        Log3 $hash->{NAME}, 5, "THZ_split: $msg2 --- $risultato";
    }
    else { 
        my $cmdHex2 = $cmdhash->{cmd2};
        if(defined($cmdHex2) ) {#empty
            ($err, $msg2) = THZ_Get_Comunication($hash, THZ_encodecommand($cmdHex2,"get") );
            if (defined($err))     {
                $err="THZ_Get: Error msg2:  $err -- $cmdHex2 -> $msg2";
                Log3 $hash->{NAME}, 3, $err;
                readingsSingleUpdate($hash, "z_Last_fhem_err", $err, 0);
                return ($msg2 ."\n msg2 " . $err);
            }
            $msg2 = THZ_Parse1($hash,$msg2);
        }
        my $cmdHex3 = $cmdhash->{cmd3};
        if(defined($cmdHex3)) {
            my $msg3= " ";      #empty 
            ($err, $msg3) = THZ_Get_Comunication($hash,  THZ_encodecommand($cmdHex3,"get"));
            if (defined($err))     {
                $err="THZ_Get: Error msg3:  $err -- $cmdHex3 -> $msg3";
                Log3 $hash->{NAME}, 3, $err;
                readingsSingleUpdate($hash, "z_Last_fhem_err", $err, 0);
                return ($msg3 ."\n msg3 " . $err);
            }
            $msg2 = THZ_Parse1($hash,$msg3) * 1000 + $msg2  ;
        }	            		
    } 
    my $unit = $cmdhash->{unit};
    $msg2 = $msg2 .  $unit  if(defined($unit)) ;  
    my $activatetrigger =1;
    readingsSingleUpdate($hash, $cmd, $msg2, $activatetrigger);
    return ($msg2);	       
}

#####################################
#
# THZ_Get_Comunication- provides a method for comunication called from THZ_Get or THZ_Set
#
# Parameter hash and CMD2 or 3 
#
########################################################################################
sub THZ_Get_Comunication($$) {
    my ($hash, $cmdHex) = @_;
    my ($err, $msg) =("", " ");
    Log3 $hash->{NAME}, 5, "THZ_Get_Comunication: Check if port is open. State = '($hash->{STATE})'";
    if  (!(($hash->{STATE}) eq "opened"))  { return("closed connection", "");}
    
    # ---------step0 --> STX start of text
    THZ_Write($hash,  "02"); 			
    ($err, $msg) = THZ_ReadAnswer($hash);
    #Expectedanswer0    is  "10"  DLE data link escape
    if  ($msg ne "10")       {$err .= " THZ_Get_Com: error found at step0 $msg"; $err .=" NAK!!" if ($msg eq "15"); select(undef, undef, undef, 0.1); return($err, $msg) ;}
    
    # ---------step1 --> send request   SOH start of heading -- Null 	-- ?? -- DLE data link escape -- EOT End of Text  
    THZ_Write($hash,  $cmdHex);
    ($err, $msg) = THZ_ReadAnswer($hash);
    if  (defined($err))     {$err .=  " THZ_Get_Com: error found at step1 "; select(undef, undef, undef, 0.1); return($err, $msg) ;}
    # Expectedanswer1     is "1002",		DLE data link escape -- STX start of text    
    if  ($msg eq "10") 	    {select(undef, undef, undef, 0.005) if (AttrVal($hash->{NAME}, "firmware" , "4.39")  =~ /^2/); ($err, $msg) = THZ_ReadAnswer($hash);} # read again sometimes "10" pause "02"
    if  ($msg ne "1002" and $msg ne "02") {$err .= " THZ_Get_Com: error found at step1 $msg"; $err .=" NAK!!" if ($msg eq "15"); select(undef, undef, undef, 0.1); return($err, $msg) ;}
    
    # ---------step2 send  DLE data link escape
    THZ_Write($hash,  "10");
    ($err, $msg) = THZ_ReadAnswer($hash);	
    THZ_Write($hash,  "10");  
    if  (defined($err))     {$err .= " THZ_Get_Com: error found at step2"; select(undef, undef, undef, 0.1);return($err, $msg) ;} 
    # Expectedanswer2 // read from the heatpump
    
    ($err, $msg) = THZ_decode($msg); 	#clean up and remove footer and header
    return($err, $msg);
}

#####################################
#
# THZ_ReadAnswer- provides a method for simple read
#
# Parameter hash and command to be sent to the interface
#
########################################################################################
sub THZ_ReadAnswer($) {
    my ($hash) = @_;
    my $name = $hash->{NAME};
    Log3 $hash->{NAME}, 5, "$name start Function THZ_ReadAnswer";
    my $buf =undef;
    my $rtimeout = (AttrVal($name, "simpleReadTimeout", "0.8"));
    $rtimeout +=1 if (AttrVal($hash->{NAME}, "firmware" , "4.39")  =~ /^2/);
    $rtimeout = minNum($rtimeout,1.8) if (AttrVal($name, "nonblocking", "0") eq 0); # set to max 1.8s if nonblocking disabled
    my $count = 0; my $countmax = 300;
    while ((!defined($buf)) and ($count <= $countmax)) {
        select(undef, undef, undef, 0.002) if (AttrVal($hash->{NAME}, "firmware" , "4.39")  =~ /^2/);   # possible fix for joerg may2022, add 5ms wait time to the above -----    
	if ($^O =~ /Win/){
            select(undef, undef, undef, 0.002);  ###delay of 2 ms for windows-OS, because SimpleReadWithTimeout does not wait
            $buf =    DevIo_DoSimpleRead($hash);
            $buf =  undef if (length($buf)==0);
        }
        else {
            $buf = DevIo_SimpleReadWithTimeout($hash, (minNum($count,1)*$rtimeout/$countmax + 0.001)); ##pay attention with DevIo_SimpleRead: it closes the connection if no answe given; DevIo_SimpleReadWithTimeout does not close
        }
        $count ++;
    }
    return ("THZ_ReadAnswer: InterfaceNotRespondig. Maybe too slow", "") if(!defined($buf)) ;
    my $data =  uc(unpack('H*', $buf));
    $count =1; $countmax = 120;    #increased to 120 for LeJoke his raspi4 is very fast reading and reaches 60 evry time
    while (( (length($data) == 1) or (($data =~ m/^01/) and ($data !~ m/1003$/m ))) and ($count <= $countmax)){ 
        my $buf1=undef;
        if ($^O =~ /Win/){
            select(undef, undef, undef, 0.005);  ###delay of 5 ms for windows-OS, because SimpleReadWithTimeout does not wait
            $buf1 =DevIo_DoSimpleRead($hash);
        }
        else {
            $buf1 = DevIo_SimpleReadWithTimeout($hash, 0.02);
        }
	Log3($hash->{NAME}, 5, "double read $count activated $data");
        if(defined($buf1)) {
            $buf .=  $buf1 ;
            $data =  uc(unpack('H*', $buf));
            Log3($hash->{NAME}, 5, "double read $count result with buf1  $data");
            $count ++;
	    }
        else{ $count += 10; }	# increased to 10 because doubled the countmax
    }
    return ("THZ_ReadAnswer: Interface max repeat limited to $countmax ", $data) if ($count == ($countmax +1));
    Log3 $hash->{NAME}, 5, "THZ_ReadAnswer: uc unpack: '$data'";
    return (undef, $data);
}
 
#####################################
#
# THZ_checksum - takes a string, removes the footer (4bytes) and computes checksum (without checksum of course)
#
# Parameter string
# returns the checksum 2bytes
#
########################################################################################
sub THZ_checksum($) {
    my ($stringa) = @_;
    my $ml = length($stringa) - 4;
    my $checksum = 0;
    for(my $i = 0; $i < $ml; $i += 2) {
        ($checksum= $checksum + hex(substr($stringa, $i, 2))) if ($i != 4);
    }
    return (sprintf("%02X", ($checksum %256)));
}

#####################################
#
# hex2int - convert from hex to int with sign 16bit 
#
########################################################################################
sub hex2int($) {
    my ($num) = @_;
    $num = unpack('s', pack('S', hex($num)));
    return $num;
}

####################################
#
# quaters2time - convert from hex to time; specific to the week programm registers
#
# parameter 1 byte representing number of quarter from midnight
# returns   string representing time
#
# example: value 1E is converted to decimal 30 and then to a time  7:30 
########################################################################################
sub quaters2time($) {
    my ($num) = @_;
    return("n.a.") if($num eq "80"); 
    my $quarters= hex($num) %4;
    my $hour= (hex($num) - $quarters)/4 ;
    my $time = sprintf("%02u", ($hour)) . ":" . sprintf("%02u", ($quarters*15));
    return $time;
}

####################################
#
# time2quarters - convert from time to quarters in hex; specific to the week programm registers
#
# parameter: string representing time
# returns: 1 byte representing number of quarter from midnight
#
# example: a time  7:30  is converted to decimal 30 
########################################################################################
sub time2quaters($) {
    my ($stringa) = @_;
    return("128") if($stringa eq "n.a."); 
    my ($h,$m) = split(":", $stringa);
    $m = 0 if(!$m);
    $h = 0 if(!$h);
    my $num = $h*4 +  int($m/15);
    return ($num);
}

####################################
#
# bitmap2string - convert from bitmap to concatenated string
#
# parameter: bitmap representing yes/no, hashref to stringmapping
# returns: concatenated string
#
########################################################################################
sub bitmap2string($$) {
    my($bitmap, $href) = @_;
    my $idx = 1;
    my $res = "";
    foreach my $bit (split //, $bitmap) {
        $res .= $href->{$idx} if ($bit);
        $idx++;
    }
    return $res;
}

####################################
#
# THZ_replacebytes - replaces bytes in string
#
# parameters: string, bytes to be searched, replacing bytes 
# retunrns changed string
#
########################################################################################
sub THZ_replacebytes($$$) {
    my ($stringa, $find, $replace) = @_; 
    my $leng_str = length($stringa);
    my $leng_find = length($find);
    my $new_stringa ="";
    for(my $i = 0; $i < $leng_str; $i += 2) {
        if (substr($stringa, $i, $leng_find) eq $find){
            $new_stringa=$new_stringa . $replace;
            if ($leng_find == 4) {$i += 2;}
        }
        else {$new_stringa=$new_stringa . substr($stringa, $i, 2);};
    }
    return ($new_stringa);
}

####################################
#
## usage THZ_overwritechecksum("0100XX". $cmd."1003"); not needed anymore
#
########################################################################################
sub THZ_overwritechecksum($) {
    my ($stringa) = @_;
    my $checksumadded=substr($stringa,0,4) . THZ_checksum($stringa) . substr($stringa,6);
    return($checksumadded);
}


####################################
#
## usage {THZ_Val("sGlobal",3,0)} register, position or subregistername, defaultval
## adding an optional parameter to ReadingsVal which selects the column in the reading
##{THZ_Val("sGlobal",5)} is equivalent to {THZ_Val("sGlobal","returnTemp")}
## 
#
########################################################################################
sub THZ_Val($;$$){
  my ($n,$col,$default) = @_;
  my $d; #normally Mythz but could be defined differently
  foreach (keys %defs) { 
        $d=$_;
        last if(($defs{$_}{TYPE}) =~ "THZ");
  }
  if(defined($defs{$d}) &&
     defined($defs{$d}{READINGS}) &&
     defined($defs{$d}{READINGS}{$n}) &&
     defined($defs{$d}{READINGS}{$n}{VAL})) {
        my $tmp=$defs{$d}{READINGS}{$n}{VAL};
        if (defined($col)) {
            #Log3 "Mythz", 3, "EXCEPTION in THZ_Val: $col";
            if  (($col =~ (/^\d+$/) ) && (defined((split ' ',$tmp)[$col])))    {return((split ' ',$tmp)[$col]);}
            if  ($tmp =~ m/$col\w*: ((-|\w|\d)+\.?\d*)/)                       {return($1);}
        }
        return $tmp;
    }
  if (!defined($default)) { return 0};
  return $default;
}


####################################
#
# THZ_encodecommand - creates a telegram for the heatpump with a given command 
#
# usage THZ_encodecommand($cmd,"get") or THZ_encodecommand($cmd,"set");
# parameter string, 
# retunrns encoded string
#
########################################################################################
sub THZ_encodecommand($$) {
    my ($cmd,$getorset) = @_;
    my $header = "0100";
    $header = "0180" if ($getorset eq "set");	# "set" and "get" have differnt header
    my $footer ="1003";
    my $checksumadded=THZ_checksum($header . "XX" . $cmd . $footer) . $cmd;
    # each 2B byte must be completed by byte 18
    # each 10 byte must be repeated (duplicated)
    my $find = "10";
    my $replace = "1010";
    #$checksumadded =~ s/$find/$replace/g; #problems in 1% of the cases, in middle of a byte
    $checksumadded=THZ_replacebytes($checksumadded, $find, $replace);
    $find = "2B";
    $replace = "2B18";
    #$checksumadded =~ s/$find/$replace/g;
    $checksumadded=THZ_replacebytes($checksumadded, $find, $replace);
    return($header. $checksumadded .$footer);
}

####################################
#
# THZ_decode -	decodes a telegram from the heatpump -- no parsing here
#
# Each response has the same structure as request - header (four bytes), optional data and footer:
#   Header: 01
#    Read/Write: 00 for Read (get) response, 80 for Write (set) response; when some error occured, then device stores error code here; actually, I know only meaning of error 03 = unknown command
#    Checksum: ? 1 byte - the same algorithm as for request
#    Command: ? 1 byte - should match Request.Command
#    Data: ? only when Read, length depends on data type
#    Footer: 10 03
#
########################################################################################
sub THZ_decode($) {
    my ($message_orig) = @_;
    #  raw data received from device have to be de-escaped before header evaluation and data use:
    my $find = "1010";    # - each sequece 10 10 must be replaced with single byte 10
    my $replace = "10";
    $message_orig=THZ_replacebytes($message_orig, $find, $replace);
    $find = "2B18";       # - each sequece 2B 18 must be replaced with single byte 2B
    $replace = "2B";
    $message_orig=THZ_replacebytes($message_orig, $find, $replace);
    #Check if answer is NAK
    if (length($message_orig) == 2 && $message_orig eq "15") {
        return("decode: NAK received from device",$message_orig);
    }
    #check header and if ok 0100, check checksum and return the decoded msg
    my $header = substr($message_orig,0,4);
    if ($header eq "0100")  {
        if (THZ_checksum($message_orig) eq substr($message_orig,4,2)) {
            $message_orig =~ /0100(.*)1003/; 
            my $message = $1;
            return (undef, $message);
        }
        else {return(THZ_checksum($message_orig) . "decode: crc_error in answer", $message_orig)};
    }
    if ($header eq "0101")  { return ("decode: timing issue", $message_orig);}
    if ($header eq "0103")  { return ("decode: command not known", $message_orig);}
    if ($header eq "0102")  { return("decode: CRC error in request", $message_orig);}
    if ($header eq "0104")  { return("decode: UNKNOWN Register REQUEST", $message_orig);}
    if ($header eq "0180")  { return(undef, $message_orig);}
    return("decode: new unknown answer " , $message_orig);
}

###############################
#added by jakob do not know if needed
#
###############################

local $SIG{__WARN__} = sub {
  my $message = shift;
  
  if (!defined($internalHash)) {
    Log3 "Mythz", 3, "EXCEPTION in THZ: '$message'";
  }
  else
  {
    Log3 $internalHash->{NAME},3, "EXCEPTION in THZ: '$message'";
  }  
};


#######################################
#THZ_Parse1($) could be used in order to test an external config file; I do not know if I want it
#e.g. {THZ_Parse1(undef,"F70B000500E6")}
#######################################
sub THZ_Parse1($$) {
    my ($hash,$message) = @_;  
    Log3 $hash->{NAME}, 5, "Parse message: $message";	  
    my $length = length($message);
    Log3 $hash->{NAME}, 5, "Message length: $length";
    my $parsingcmd = substr($message,2,2);
    $parsingcmd = substr($message,2,6) if (($parsingcmd =~ m/(0A|0B|0C)/) and (AttrVal($hash->{NAME}, "firmware" , "4.39")  !~ /^2/) );
    my $msgtype;
    my $parsingrule;
    my $parsingelement;
    # search for the type in %gets
    foreach  my $cmdhash  (values %gets) {
        if (defined ($cmdhash->{cmd2}) and ($cmdhash->{cmd2} eq $parsingcmd)) {
        $msgtype = $cmdhash->{type} ;
        last
        }
        elsif (defined ($cmdhash->{cmd3})){
            if ($cmdhash->{cmd3} eq $parsingcmd) {
                $msgtype = $cmdhash->{type} ;
                last
            }
        }
    }
    $parsingrule = $parsinghash{$msgtype} if(defined($msgtype));
    my $ParsedMsg = $message;
    if(defined($parsingrule)) {
        $ParsedMsg = "";
        for  my $parsingelement  (@$parsingrule) {
            my $parsingtitle = $parsingelement->[0];
            my $positionInMsg = $parsingelement->[1];
            my $lengthInMsg = $parsingelement->[2];
            my $Type = $parsingelement->[3];
            my $divisor = $parsingelement->[4];
            #check if parsing out of message, and fill with zeros; the other possibility is to skip the step.
            if (length($message) < ($positionInMsg + $lengthInMsg))    {
                Log3 $hash->{NAME}, 5, "THZ_Parsing: offset($positionInMsg) + length($lengthInMsg) is longer then message : '$message'"; 
                #$message.= '0' x ($positionInMsg + $lengthInMsg - length($message)); # fill up with 0s to the end if needed
                #line above redundant because of  else below added 9.2018; 
                #Log3 $hash->{NAME},3, "after: '$message'"; 
            }
            else {
                my $value = substr($message, $positionInMsg, $lengthInMsg);
                if    ($Type eq "hex")		{$value= hex($value);}
                elsif ($Type eq "year")		{$value= hex($value)+2000;}
                elsif ($Type eq "hex2int")	{$value= hex2int($value);}
                elsif ($Type eq "turnhexdate")	{$value= substr($value, 2,2) . substr($value, 0,2); $value= sprintf("%02u.%02u", hex($value)/100, hex($value)%100); }
                elsif ($Type eq "hexdate")	{$value= sprintf("%02u.%02u", hex($value)/100, hex($value)%100) ;}
                #elsif ($Type eq "turnhex2time")	{$value= sprintf(join(':', split("\\.", hex(substr($value, 2,2) . substr($value, 0,2))/100))) ;}
                #elsif ($Type eq "hex2time")	{$value= sprintf(join(':', split("\\.", hex(substr($value, 0,2) . substr($value, 2,2))/100))) ;}
                elsif ($Type eq "turnhex2time")	{$value= substr($value, 2,2) . substr($value, 0,2); $value= sprintf("%02u:%02u", hex($value)/100, hex($value)%100); }
                elsif ($Type eq "hex2time")	{$value= sprintf("%02u:%02u", hex($value)/100, hex($value)%100) ;}
                elsif ($Type eq "swver")		{$value= sprintf("%01u.%02u", hex(substr($value, 0,2)), hex(substr($value, 2,2)));}
                elsif ($Type eq "hex2ascii")	{$value= uc(pack('H*', $value));}
                elsif ($Type eq "opmode")		{$value= $OpMode{hex($value)};}
                elsif ($Type eq "opmode2")	{$value= $opMode2{hex($value)};}
                elsif ($Type eq "opmodehc")	{$value= $OpModeHC{hex($value)};}
                elsif ($Type eq "esp_mant") 	{$value= sprintf("%.3f", unpack('f', pack( 'L',  reverse(hex($value)))));}
                elsif ($Type eq "somwinmode")	{$value= $SomWinMode{($value)};}
                #elsif ($Type eq "hex2wday")	{$value= bitmap2string(unpack('b7', pack('H*',$value)), \%weekdaymap);}
                elsif ($Type eq "hex2error")	{$value= bitmap2string(unpack('b32', pack('H*',$value)), \%faultmap);}
                elsif ($Type eq "weekday")	{$value= $weekday{($value)};}
                elsif ($Type eq "faultmap")	{$value= $faultmap{(hex($value))};}
                elsif ($Type eq "quater")		{$value= quaters2time($value);}
                elsif ($Type eq "bit0")		{$value= (hex($value) &  0b0001) / 0b0001;}
                elsif ($Type eq "bit1")		{$value= (hex($value) &  0b0010) / 0b0010;}
                elsif ($Type eq "bit2")		{$value= (hex($value) &  0b0100) / 0b0100;}
                elsif ($Type eq "bit3")		{$value= (hex($value) &  0b1000) / 0b1000;}
                elsif ($Type eq "nbit0")		{$value= 1-((hex($value) &  0b0001) / 0b0001);}
                elsif ($Type eq "nbit1")		{$value= 1-((hex($value) &  0b0010) / 0b0010);}
                elsif ($Type eq "raw")		{;}
                elsif ($Type eq "n.a.")		{$value= "n.a.";}
                $value = $value/$divisor if ($divisor != 1); 
                $ParsedMsg .= $parsingtitle . $value; 
            }
        }
    }
    return($ParsedMsg);
}

########################################################################################
# only for debug; start with "get Mythz debug_read_raw_register_slow"
#
########################################################################################
sub THZ_debugread($){
    my ($hash) = @_;
    my ($err, $msg) =("", " ");
    my @numbers=('01', '09', '16', 'D1', 'D2', 'E8', 'E9', 'F2', 'F3', 'F4', 'F5', 'F6', 'F8', 'FB', 'FC', 'FD', 'FE', 'FF');
    #my @numbers=('FB',  '0A0BA3', '0A057C', '0A057D', '0A057E', '0A057F' );
    #my @numbers=(1, 3, 4, 5, 8, 12, 13, 14, 15, 17, 18, 19, 20, 22, 26, 39, 40, 82, 83, 86, 87, 96, 117, 128, 239, 265, 268, 269, 270, 271, 274, 275, 278, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 297, 299, 317, 320, 354, 384, 410, 428, 440, 442, 443, 444, 445, 446, 603, 607, 612, 613, 634, 647, 650, 961, 1385, 1386, 1387, 1388, 1389, 1391, 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1399, 1400, 1401, 1402, 1403, 1404, 1405, 1406, 1407, 1408, 1409, 1410, 1411, 1412, 830, 1414, 1415, 1416, 1417, 1418, 1419, 1420, 1421, 1422, 1423, 1424, 1425, 1426, 1427, 1428, 1429, 1430, 1431, 1432, 1433, 1434, 1435, 1436, 1437, 1438, 1439, 1440, 1441, 1442, 1443, 1444, 1445, 1446, 1447, 1448, 1449, 1450, 1451, 1452, 1453, 1454, 1455, 1456, 1457, 1458, 1459, 1460, 1461, 1462, 1463, 1464, 1465, 1466, 1467, 1468, 1469, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, 1478, 1479, 1480, 1481, 2970, 2971, 2974, 2975, 2976, 2977, 2978, 2979, 1413, 1426, 1427, 474, 1499, 757, 758, 952, 955, 1501, 1502, 374, 1553, 1554, 1555, 272, 1489, 1490, 1491, 1492, 1631, 933, 934, 1634, 928, 718, 64990, 64991, 64992, 64993, 2372, 2016, 936, 937, 938, 939, 1632, 2350, 2351, 2352, 2353, 2346, 2347, 2348, 2349, 2334, 2335, 2336, 2337, 2330, 2331, 2332, 2333, 2344, 2345, 2340, 2341, 942, 943, 944, 945, 328, 2029, 2030, 2031, 2032, 2033);
    #my @numbers=(1, 3, 12, 13, 14, 15, 19, 20, 22, 26, 39,  82, 83, 86, 87, 96, 239, 265, 268, 274, 278, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 320, 354, 384, 410, 428, 440, 442, 443, 444, 445, 446, 613, 634, 961, 1388, 1389, 1391, 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1399, 1400, 1401, 1402, 1403, 1404, 1405, 1406, 1407, 1408, 1409,  1414, 1415, 1416, 1417, 1418, 1419, 1420, 1421, 1422, 1423, 1430, 1431, 1432, 1433, 1434, 1435, 1436, 1439, 1440, 1441, 1442, 1443, 1444, 1445, 1446, 1447, 1448, 1449, 1450, 1451, 1452, 1453, 1454, 1455, 1456, 1457, 1458, 1459, 1460, 1461, 1462, 1463, 1464, 1465, 1466, 1467, 1468, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, 1478, 1479, 2970, 2971, 2975, 2976, 2977, 2978, 2979, 474, 1499, 757, 758, 952, 955, 1501, 1502, 374, 1553, 1554, 272, 1489, 1491, 1492, 1631, 718, 64990, 64991, 64992, 64993, 2372, 2016, 936, 937, 938, 939, 1632, 2350, 2351, 2352, 2353, 2346, 2347, 2348, 2349, 2334, 2335, 2336, 2337, 2330, 2331, 2332, 2333, 2344, 2345, 2340, 2341, 942, 943, 944, 945, 328, );
    #my @numbers=(239, 410, 603, 607, 634, 830, 1424, 1425, 1426, 1427, 1428, 1429, 1430, 1431, 1432, 1433, 1434, 1435, 1444, 1445, 1446, 1447, 1448, 1449, 1450, 1451, 1452, 1453, 1454, 1455, 1456, 1457, 1467, 1468, 1469, 1478, 1479, 1480, 1481, 2970, 2971, 2974, 2975, 2976, 2977, 2978, 2979, 1413, 1426, 1427, 474, 1501, 1502, 374, 1631, 718, 2372, 328);
    #my @numbers = (1..256);
    #my @numbers = (1..65535);
    #my @numbers = (1..3179);
    #my @numbers = ('01', '09');
    my $indice= "FF";
    unlink("data.txt"); #delete  debuglog
    #my $i=0;
    foreach my $indice(@numbers) {
        # $i=$i+1;
        #my $cmd = sprintf("%02X", $indice);
        #my $cmd = sprintf("%04X", $indice);
        #my $cmd = "0A" . sprintf("%04X",  $indice);
        my $cmd = $indice;
        my $cmdHex2 = THZ_encodecommand($cmd,"get");
	#$cmd="0B013D00D2"; #set temp standby to D2hex = 210 dec to be decided by 10 --> 21°C
	#$cmd="D10000"; #delete last 10 errors 
	#my $cmdHex2 = THZ_encodecommand($cmd,"set",);
        #($err, $msg) = THZ_Get_Comunication($hash,  $cmdHex2);
        #STX start of text
        THZ_Write($hash,  "02");
        ($err, $msg) = THZ_ReadAnswer($hash);
        #select(undef, undef, undef, (0.05*$i));
        #if you wait here more than 600ms connection dropped
        # send request
        THZ_Write($hash,  $cmdHex2);
        #select(undef, undef, undef, (0.05*$i));
        ($err, $msg) = THZ_ReadAnswer($hash);
        #expected 1002; if not following if takes care
        if ($msg eq "10") {
            select(undef, undef, undef, 0.01);
            ($err, $msg) = THZ_ReadAnswer($hash);
        }  
        # ack datatranfer and read from the heatpump        
        select(undef, undef, undef, 0.015);
        THZ_Write($hash,  "10");
        select(undef, undef, undef, 0.001);
        ($err, $msg) = THZ_ReadAnswer($hash);
        THZ_Write($hash,  "10");
        if (defined($err))  {return ($msg ."\n" . $err);}
        else {   #clean up and remove footer and header
            ($err, $msg) = THZ_decode($msg);
            if (defined($err)) {
                $msg = THZ_Parse1($hash,$msg);
                $msg=$cmdHex2 ."-". $msg ."-". $err;
                } 
            my $activatetrigger =1;
            # readingsSingleUpdate($hash, $cmd, $msg, $activatetrigger);
            my $MYFILE;
            open ($MYFILE, '>>', 'data.txt');
            print $MYFILE ($cmdHex2 ."-". $cmd . "-" . $msg . "\n");
            close ($MYFILE);
            #Log3 $hash->{NAME}, 3, "$cmd  -  $msg";
        }
        select(undef, undef, undef, 0.2); 
    }
}

#######################################
#
#THZ_Attr($) 
#in case of change of attribute starting with interval_ refresh all
#
########################################################################################
sub THZ_Attr(@) {
    my ($cmd, $name, $attrName, $attrVal) = @_;
    my $hash = $defs{$name};
    $attrVal = "4.39" if (($cmd eq "del") and ($attrName eq "firmware"));
    if ( $attrName eq "firmware" )  {  
        if ($attrVal eq "2.06") {
            %sets = %sets206;
            %gets = (%getsonly2xx, %getsonly206, %sets);
        }
        elsif ($attrVal eq "2.14") {
            %sets = (%sets206, %setsonly214);
            %gets = (%getsonly2xx, %getsonly214, %sets206);
        }
        elsif ($attrVal eq "2.14j") {
            %sets = (%sets206, %setsonly214);
            %gets = (%getsonly2xx, %getsonly214j, %sets206);
        }
        elsif ($attrVal eq "5.39") {
            %sets=(%sets439539common, %sets539only);
            %gets=(%getsonly539, %sets);
        }
        elsif ($attrVal eq "5.39technician") {
            %sets=(%sets439539common, %sets539only, %setsX39technician);
            %gets=(%getsonly539, %sets);
        }
        elsif ($attrVal eq "4.39technician") {
            %sets=(%sets439539common, %sets439only, %setsX39technician);
            %gets=(%getsonly439, %sets);
        }
        else { #in all other cases I assume $attrVal eq "4.39" cambiato nella v0140
            %sets=(%sets439539common, %sets439only);
            %gets=(%getsonly439, %sets);
        }
        THZ_Refresh_all_gets($hash) if($init_done);
    }
    if( $attrName =~ /^interval_/ ) {
        THZ_Refresh_all_gets($hash) if($init_done);
    }
    return undef;
}


#####################################
sub THZ_Undef($$) {
    my ($hash, $arg) = @_;
    my $name = $hash->{NAME};
    RemoveInternalTimer(0, "THZ_GetRefresh");
    foreach my $d (sort keys %defs) {
        if(defined($defs{$d}) && defined($defs{$d}{IODev}) && $defs{$d}{IODev} == $hash) {
            my $lev = ($reread_active ? 4 : 2);
            Log3 $hash->{NAME}, $lev, "deleting port for $d";
            delete $defs{$d}{IODev};
        }
    }
    BlockingKill($hash->{helper}{RUNNING_PID}) if(defined($hash->{helper}{RUNNING_PID}));
    DevIo_CloseDev($hash); 
    return undef;
}

##########################################
# nearest rounds to the nearrest value multiple of the first argumen
# nearest_ceil(10, 45); --> 50
# nearest_floor(10, 45); --> 40
# for all other values outside the middlevalues they take he nearest
# modified takes as an argument the function to be called, not the argument
########################################################################################
sub nearest_ceil($$) {
    my $targ = abs(shift);
    my $Math1 = 0.5000000000003;
    my @res  = map { $targ * POSIX::floor(($_ + $Math1 * $targ) / $targ) } @_;
    return wantarray ? @res : $res[0];
}

sub nearest_floor($$) {
    my $targ = abs(shift);
    my $Math1 = 0.5000000000003;
    my @res  = map { $targ * POSIX::ceil(($_ - $Math1 * $targ) / $targ) } @_;
    return wantarray ? @res : $res[0];
}

##########################################
# THZ_RemoveInternalTimer($) 
# modified takes as an argument the function to be called, not the argument
########################################################################################
#sub THZ_RemoveInternalTimer($){
#  my ($callingfun) = @_;
#  foreach my $a (keys %intAt) {
#    delete($intAt{$a}) if($intAt{$a}{FN} eq $callingfun);
#  }
#}
################################
#
sub function_heatSetTemp($$) {
    my ($start, $stop) = @_;
    my ($p13GradientHC1, $p14LowEndHC1, $p15RoomInfluenceHC1);
    my $pOpMode = " ";
    my $devname; #normally Mythz but could be defined differently
    foreach (keys %defs) { 
        $devname=$_;
        last if(($defs{$_}{TYPE}) =~ "THZ");
    }
    if (AttrVal($devname, "firmware" , "4.39")  =~ /^2/ )  {
        ($p13GradientHC1, $p14LowEndHC1, $p15RoomInfluenceHC1) = (split ' ',ReadingsVal($devname,"pHeat1",0))[1,3,5];
    }  
    else {
        $pOpMode = ReadingsVal($devname,"pOpMode"," ");
        $p13GradientHC1 	  = ReadingsVal($devname,"p13GradientHC1",0.4);
        $p15RoomInfluenceHC1 = (split ' ',ReadingsVal($devname,"p15RoomInfluenceHC1",0))[0];
        $p14LowEndHC1 	  = (split ' ',ReadingsVal($devname,"p14LowEndHC1",0))[0];
    }
    my ($heatSetTemp, $roomSetTemp, $insideTemp) = (split ' ',ReadingsVal($devname,"sHC1",0))[11,21,27];
    my $outside_tempFiltered =(split ' ',ReadingsVal($devname,"sGlobal",0))[65];
    if (!$roomSetTemp) {
        $insideTemp=23.8 ; $roomSetTemp = 19.5; $p13GradientHC1 = 0.31; $heatSetTemp = 15; $p15RoomInfluenceHC1 = 80;
        $pOpMode ="DEMO: no data";
        $outside_tempFiltered = 0; $p14LowEndHC1 =0.5; 
    }
    my $a= 0.7 + ($roomSetTemp * (1 + $p13GradientHC1 * 0.87)) + $p14LowEndHC1 + ($p15RoomInfluenceHC1 * $p13GradientHC1 * ($roomSetTemp - $insideTemp) /10); 
    my $a1= 0.7 + ($roomSetTemp * (1 + $p13GradientHC1 * 0.87)) + $p14LowEndHC1;
    my $b= -14 * $p13GradientHC1 / $roomSetTemp; 
    my $c= -1 * $p13GradientHC1 /75;
    my $Simul_heatSetTemp; my $Simul_heatSetTemp_simplified;  my @ret; 
    foreach ($start..$stop) {
        my $tmp =$_ * $_ * $c + $_ * $b;
        $Simul_heatSetTemp 		 = sprintf("%.1f", maxNum(5,( $tmp + $a)));
        #$Simul_heatSetTemp 		 =  8 if ($pOpMode eq "DHWmode"); # DHWmode is always at 8 grad C 
        $Simul_heatSetTemp_simplified = sprintf("%.1f", maxNum(5,($tmp + $a1)));
        push(@ret, [$_, $Simul_heatSetTemp, $Simul_heatSetTemp_simplified]);
    }
    my $titlestring =  'roomSetTemp=' . $roomSetTemp . '°C p13GradientHC1=' . $p13GradientHC1 . ' p14LowEndHC1=' . $p14LowEndHC1  .  'K p15RoomInfluenceHC1=' . $p15RoomInfluenceHC1;
    $titlestring .= "%" if (AttrVal($devname, "firmware" , "4.39")  !~ /^2/ ); 
    $titlestring .= " insideTemp=" . $insideTemp .'°C';
    return (\@ret, $titlestring, $heatSetTemp, $outside_tempFiltered, $pOpMode);
}

#####################################
# sub THZ_PrintcurveSVG
# plots heat curve
#define wl_hr weblink htmlCode {THZ_PrintcurveSVG}
# da mettere dentro lo style per funzionare sopra        svg      { height:200px; width:800px;}
#define wl_hr2 weblink htmlCode <div class="SVGplot"><embed src="/fhem/THZ_PrintcurveSVG/" type="image/svg+xml" width="800" height="160" name="wl_7"/></div> <a href="/fhem?detail=wl_hr2">wl_hr2</a><br>
#####################################

sub THZ_PrintcurveSVG {
    my ($ycurvevalues, $titlestring, $heatSetTemp, $outside_tempFiltered, $pOpMode) = function_heatSetTemp(-15,20);
    my $v0min = minNum(15, ($ycurvevalues->[33][1]), ($ycurvevalues->[33][2]), $heatSetTemp);	#lower offset than 15, if out of scale
    $v0min = maxNum(5, nearest_ceil(5, $v0min));					#start only from a multiple of 5, but do not go below 5
    my $vstep= 5;
    $vstep= 10 if ((($ycurvevalues->[0][1])>($v0min+4*$vstep)) or (($ycurvevalues->[0][2])>($v0min+4*$vstep))); #increase step, if out of scale
    my $v1=$v0min+$vstep; my $v2=$v1+$vstep; my $v3=$v2+$vstep; my $v4=$v3+$vstep;
    my $ret =  <<'END';
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE svg> <svg width="800" height="164" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" >
<style type="text/css"><![CDATA[
text       { font-family:Times; font-size:12px; }
text.title { font-size:14px; }
text.copy  { text-decoration:underline; stroke:none; fill:blue;    }
text.paste { text-decoration:underline; stroke:none; fill:blue;    }
polyline { stroke:black; fill:none; }
.border  { stroke:black; fill:url(#gr_bg); }
.vgrid   { stroke:gray;  stroke-dasharray:2,6; }
.hgrid   { stroke:gray;  stroke-dasharray:2,6; }
.pasted  { stroke:black; stroke-dasharray:1,1; }
.l0 { stroke:red;     }  text.l0 { stroke:none; fill:red;     } 
.l1 { stroke:green;   }  text.l1 { stroke:none; fill:green;   }
.l3 { stroke:blue;   }  text.l3 { stroke:none; fill:blue;   }
.l0dot   { stroke:red;   stroke-dasharray:2,4; }  text.ldot { stroke:none; fill:red; } 
]]></style>
<defs>
  <linearGradient id="gr_bg" x1="0%" y1="0%" x2="0%" y2="100%">
    <stop offset="0%" style="stop-color:#FFFFF7; stop-opacity:1"/>
    <stop offset="100%" style="stop-color:#FFFFC7; stop-opacity:1"/>
  </linearGradient>
  <linearGradient id="gr_0" x1="0%" y1="0%" x2="0%" y2="100%">
    <stop offset="0%" style="stop-color:#f00; stop-opacity:.6"/>
    <stop offset="100%" style="stop-color:#f88; stop-opacity:.4"/>
  </linearGradient>
  <linearGradient id="gr_1" x1="0%" y1="0%" x2="0%" y2="100%">
    <stop offset="0%" style="stop-color:#291; stop-opacity:.6"/>
    <stop offset="100%" style="stop-color:#8f7; stop-opacity:.4"/>
  </linearGradient>
  <pattern id="gr0_stripe" width="4" height="4" patternUnits="userSpaceOnUse" patternTransform="rotate(-45 2 2)">
      <path d="M -1,2 l 6,0" stroke="#f00" stroke-width="0.5"/>
  </pattern>
  <pattern id="gr1_stripe" width="4" height="4" patternUnits="userSpaceOnUse" patternTransform="rotate(45 2 2)">
      <path d="M -1,2 l 6,0" stroke="green" stroke-width="0.5"/>
  </pattern>
  <linearGradient id="gr0_gyr" x1="0%" y1="0%" x2="0%" y2="100%">
    <stop offset="0%" style="stop-color:#f00; stop-opacity:.6"/>
    <stop offset="50%" style="stop-color:#ff0; stop-opacity:.6"/>
    <stop offset="100%" style="stop-color:#0f0; stop-opacity:.6"/>
  </linearGradient>
</defs>
<rect x="48"  y="19.2" width="704" height="121.6" rx="8" ry="8" fill="none" class="border"/>
<text x="12"  y="80" text-anchor="middle" class="ylabel" transform="rotate(270,12,80)">HC1 heat SetTemp °C</text>
<text x="399" y="163.5" class="xlabel" text-anchor="middle">outside temperature filtered °C</text>
<text x="44"  y="155" class="ylabel" text-anchor="middle">-15</text>
<text x="145" y="155" class="ylabel" text-anchor="middle">-10</text>	<polyline points="145,19 145,140" class="hgrid"/>
<text x="246" y="155" class="ylabel" text-anchor="middle">-5</text>		<polyline points="246,19 246,140" class="hgrid"/>
<text x="347" y="155" class="ylabel" text-anchor="middle">0</text>   	<polyline points="347,19 347,140" class="hgrid"/>
<text x="448" y="155" class="ylabel" text-anchor="middle">5</text>   	<polyline points="448,19 448,140" class="hgrid"/>
<text x="549" y="155" class="ylabel" text-anchor="middle">10</text>  	<polyline points="549,19 549,140" class="hgrid"/>
<text x="650" y="155" class="ylabel" text-anchor="middle">15</text>  	<polyline points="650,19 650,140" class="hgrid"/>
<text x="751" y="155" class="ylabel" text-anchor="middle">20</text>  	<polyline points="751,19 751,140" class="hgrid"/>
<g>
END
    $ret .= '<polyline points="44,140 49,140"/>   <text x="39"  y="144" class="ylabel" text-anchor="end">'  . $v0min . '</text>'
         .  '<polyline points="44,110 49,110"/>   <text x="39"  y="114" class="ylabel" text-anchor="end">'  . $v1    . '</text>'
         .  '<polyline points="44,80 49,80"/>     <text x="39"  y="84"  class="ylabel" text-anchor="end">'  . $v2    . '</text>'
         .  '<polyline points="44,49 49,49"/>     <text x="39"  y="53"  class="ylabel" text-anchor="end">'  . $v3    . '</text>'
         .  '<polyline points="44,19 49,19"/>     <text x="39"  y="23"  class="ylabel" text-anchor="end">'  . $v4    . '</text> </g> <g>'
         .  '<polyline points="751,140 756,140"/> <text x="760" y="144" class="ylabel">'                    . $v0min . '</text>'
         .  '<polyline points="751,110 756,110"/> <text x="760" y="114" class="ylabel">'                    . $v1    . '</text>'
         .  '<polyline points="751,80 756,80"/>   <text x="760" y="84"  class="ylabel">'                    . $v2    . '</text>'
         .  '<polyline points="751,49 756,49"/>   <text x="760" y="53"  class="ylabel">'                    . $v3    . '</text>'
         .  '<polyline points="751,19 756,19"/>   <text x="760" y="23"  class="ylabel">'                    . $v4    . '</text></g>' ."\n";
    #labels ######################
    $ret .= '<text line_id="line_1"  x="70"  y="100" class="l1"> --- heat curve with insideTemp influence</text>' 
         .  '<text line_id="line_3"  x="70"  y="115" class="l3"> --- heat curve simplified</text>' 
         .  '<text  line_id="line_0" x="70"  y="130" class="l0"> --- working point: outside_tempFiltered='
         . $outside_tempFiltered . '°C heatSetTemp=' . $heatSetTemp . '°C </text>'
         .  '<text line_id="line_3"  x="650" y="50"  class="title"> -'. $pOpMode . '- </text>' ." \n"; 
    #title ######################
    $ret .= '<text id="svg_title" x="400" y="14.4"  class="title" text-anchor="middle">'. $titlestring .' </text>' . "\n";
    #point ######################
    $ret .='<polyline id="line_0"   style="stroke-width:2" class="l0" points="';
    my ($px,$py) = (sprintf("%.1f", (($outside_tempFiltered+15)*(750-49)/(15+20)+49)),sprintf("%.1f", (($heatSetTemp-$v4)*(140-19)/($v0min-$v4)+19))); 
    $ret.= ($px-3) . "," . ($py)   ." " . ($px)  . "," . ($py-3) ." " . ($px+3) . "," . ($py) ." " . ($px)   . "," . ($py+3)  ." " . ($px-3)   . "," . ($py)  ." " . '"/>' . "\n";
    #curve with inside temperature correction ######################
    $ret .='<polyline id="line_1"  title="Heat Curve with insideTemp correction" style="stroke-width:1" class="l1" points="';
    foreach (@{$ycurvevalues}) {
        $ret.= (sprintf("%.1f", ($_->[0]+15)*(750-49)/(15+20)+49) ). "," . sprintf("%.1f", (($_->[1]-$v4)*(140-19)/($v0min-$v4)+19)) ." ";
    }
    $ret .= '"/> ' . "\n";
    #curve without inside temperature correction ######################
    $ret .='<polyline id="line_3"  title="Heat Curve simplified" style="stroke-width:1" class="l3" points="';
    foreach (@{$ycurvevalues}) {
        $ret.= (sprintf("%.1f", ($_->[0]+15)*(750-49)/(15+20)+49) ). "," . sprintf("%.1f", (($_->[2]-$v4)*(140-19)/($v0min-$v4)+19)) ." ";
    }
    $ret .= '"/> ' . "\n";
    $ret .= '</svg>';
    my $FW_RETTYPE = "image/svg+xml";
    return ($FW_RETTYPE, $ret);
}

sub THZ_detailFn(@){
    my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn.
    my $hash = $defs{$d}; #$d is the name of the defined device 
    return '<div class="SVGplot"><embed src="/fhem/THZ_PrintcurveSVG/" type="image/svg+xml"  name="wl_hr22"/></div> <br>';
}

#####################################
sub THZ_backup_readings($){
    my ($hash) = @_;
    return "No statefile specified" if(!$attr{global}{statefile});
    my $backupfile=$attr{global}{statefile};
    my $t = localtime;
    my ($sec,$min,$hour,$mday,$month,$year,$wday,$yday,$isdst) = localtime;
    $month++;
    $year+=1900;
    #$year+=1900 - 2000;
    my $replacestr= "$hash->{NAME}-$year-$month-$mday.backup"; 
    $backupfile=~ s/fhem.save/$replacestr/g;  #saving to statefile path
    my $BAFH;
    if (!open($BAFH, '>', $backupfile)) {
        my $msg = "WriteStateFile: Cannot open $backupfile: $!";
        Log3 $hash->{NAME},3, $msg;
        return $msg;
    }
    print $BAFH "#$t\n";
    my $r = $hash->{READINGS};
    foreach my $c (sort keys %{$r}) {
        my $rd = $r->{$c};
            if(($c =~ /^p[HOr0-9]/) and (defined($rd->{VAL}))) {
            my $val = $rd->{VAL};
            $val =~ s/;/;;/g;
            $val =~ s/\n/\\\n/g;
            print $BAFH "set $hash->{NAME}  $c $val\n";
        }
    }
    return "$backupfile: $!" if(!close($BAFH));
    return "saved Readings in $backupfile";
}

1;

=pod
=item device
=item summary Monitors and controls Tecalor/StiebelEltron heatpumps via RS232 or ser2net
=item summary_DE Kommuniziert mittels RS232 oder ser2net mit einer Tecalor/SE Wärmepumpe
=item cloudfree
=item openapi
=begin html

<a name="THZ"></a>
<h3>THZ</h3>
<ul>
  THZ module: comunicate through serial interface RS232/USB (eg /dev/ttyxx) or through ser2net (e.g 10.0.x.x:5555) with a Tecalor/Stiebel Eltron heatpump. <br>
   Tested on a THZ303/Sol (with serial speed 57600/115200@USB) and a THZ403 (with serial speed 115200) with the same Firmware 4.39. <br>
   Tested on a LWZ404 (with serial speed 115200) with Firmware 5.39. <br>
   Tested on fritzbox, nas-qnap, raspi and macos.<br>
   Implemented: read of status parameters and read/write of configuration parameters.
   A complete description can be found in the 00_THZ wiki http://www.fhemwiki.de/wiki/Tecalor_THZ_Heatpump
  <br><br>

  <a name="THZdefine"></a>
  <b>Define</b>
  <ul>
    <code>define &lt;name&gt; THZ &lt;device&gt;</code> <br>
    <br>
    <code>device</code> can take the same parameters (@baudrate, @directio,
    TCP/IP, none) like the <a href="#CULdefine">CUL</a>,  e.g  57600 baud or 115200.<br>
    Example:
    direct connection   
    <ul><code>
      define Mytecalor 			THZ   /dev/ttyUSB0@115200<br>
      </code></ul>
      or network connection (like via ser2net)<br>
      <ul><code>
      define Myremotetecalor  	THZ  192.168.0.244:2323 
    </code></ul>
    <br>
      <ul><code>
      define Mythz THZ /dev/ttyUSB0@115200 			<br>
      define FileLog_Mythz FileLog ./log/Mythz-%Y.log Mythz 	<br>
      attr Mythz event-min-interval s.*:4800			<br>
      attr Mythz event-on-change-reading .*			<br>
      attr Mythz interval_sDHW 400				<br>
      attr Mythz interval_sElectrDHWDay 2400			<br>
      attr Mythz interval_sElectrDHWTotal 43200			<br>
      attr Mythz interval_sGlobal 400				<br>
      attr Mythz interval_sHC1 400				<br>
      attr Mythz interval_sHeatDHWDay 2400			<br>
      attr Mythz interval_sHeatDHWTotal 43200			<br>
      attr Mythz interval_sHeatRecoveredDay 2400		<br>
      attr Mythz interval_sHeatRecoveredTotal 43200		<br>
      attr Mythz interval_sHistory 86400			<br>
      attr Mythz interval_sLast10errors 86400			<br>
      attr Mythz userReadings insideSetTemp:sHC1.* {THZ_Val("sHC1",21)}, insideTemp:sHC1.* {THZ_Val("sHC1",27)}, AussenTemp:sGlobal.* {THZ_Val("sGlobal",1)} 	<br>
      attr Mythz room pompa					<br>
      attr FileLog_Mythz  room pompa				<br>
      </code></ul>
     <br> 
   If the attributes interval_XXXX are not defined (or 0 seconds), their internal polling is disabled.
   <br>
   This module is starting to support older firmware 2.06 or newer firmware 5.39; the following attribute adapts decoding   <br>
    <br>
      <ul><code>
      attr Mythz firmware 2.06 <br>
      </code></ul>
      <ul><code>
      attr Mythz firmware 5.39 <br>
      </code></ul>
     <br>
     If no attribute firmware is set, it is assumed your firmware is compatible with 4.39.
     <br>
     A backup function has been implemented
     <ul><code>
     get Mythz zBackupParameters 
     </code></ul> 
    The command saves all pXXX in a backupfile with a special text format.
    All (or some) parameters can be easily restored with one copy&paste from the backupfile in a telnet fhem session.
  </ul>
  <br>
</ul>
 
=end html

=begin html_DE

<a name="THZ"></a>
<h3>THZ</h3>
<ul>
  THZ Modul: Kommuniziert mittels einem seriellen Interface RS232/USB (z.B. /dev/ttyxx), oder mittels ser2net (z.B. 10.0.x.x:5555) mit einer Tecalor / Stiebel  Eltron Wärmepumpe. <br>
  Getestet mit einer Tecalor THZ303/Sol (Serielle Geschwindigkeit 57600/115200@USB) und einer THZ403 (Serielle Geschwindigkeit 115200) mit identischer Firmware 4.39. <br>
  Getestet mit einer Stiebel LWZ404 (Serielle Geschwindigkeit 115200@USB) mit Firmware 5.39. <br>
  Getestet auf FritzBox, nas-qnap, Raspberry Pi and MacOS.<br>
  Dieses Modul funktioniert nicht mit älterer Firmware; Gleichwohl, das "parsing" könnte leicht angepasst werden da die Register gut beschrieben wurden.
  https://answers.launchpad.net/heatpumpmonitor/+question/100347  <br>
  Implementiert: Lesen der Statusinformation sowie Lesen und Schreiben einzelner Einstellungen.
  Genauere Beschreinung des Modules --> 00_THZ wiki http://www.fhemwiki.de/wiki/Tecalor_THZ_W%C3%A4rmepumpe
  <br><br>
  <a name="THZdefine"></a>
  <b>Define</b>
  <ul>
    <code>define &lt;name&gt; THZ &lt;device&gt;</code> <br>
    <br>
    <code>device</code> kann einige Parameter beinhalten (z.B. @baudrate, @direction,
    TCP/IP, none) wie das <a href="#CULdefine">CUL</a>, z.B. 57600 baud oder 115200.<br>
    Beispiel:<br>
    Direkte Verbindung
    <ul><code>
      define Mytecalor THZ /dev/ttyUSB0@115200<br>
      </code></ul>
      oder vir Netzwerk (via ser2net)<br>
      <ul><code>
      define Myremotetecalor THZ 192.168.0.244:2323 
    </code></ul>
    <br>
      <ul><code>
      define Mythz THZ /dev/ttyUSB0@115200 			<br>
      define FileLog_Mythz FileLog ./log/Mythz-%Y.log Mythz 	<br>
      attr Mythz event-min-interval s.*:4800			<br>
      attr Mythz event-on-change-reading .*			<br>
      attr Mythz interval_sDHW 400				<br>
      attr Mythz interval_sElectrDHWDay 2400			<br>
      attr Mythz interval_sElectrDHWTotal 43200			<br>
      attr Mythz interval_sGlobal 400				<br>
      attr Mythz interval_sHC1 400				<br>
      attr Mythz interval_sHeatDHWDay 2400			<br>
      attr Mythz interval_sHeatDHWTotal 43200			<br>
      attr Mythz interval_sHeatRecoveredDay 2400		<br>
      attr Mythz interval_sHeatRecoveredTotal 43200		<br>
      attr Mythz interval_sHistory 86400			<br>
      attr Mythz interval_sLast10errors 86400			<br>
      attr Mythz userReadings insideSetTemp:sHC1.* {THZ_Val("sHC1",21)}, insideTemp:sHC1.* {THZ_Val("sHC1",27)}, AussenTemp:sGlobal.* {THZ_Val("sGlobal",1)} 	<br>
      attr Mythz room pompa					<br>
      attr FileLog_Mythz  room pompa				<br>
      </code></ul>
     <br> 
   Wenn die Attribute interval_XXXXXXX nicht definiert sind (oder 0), ist das interne Polling deaktiviert.
      
  </ul>
  <br>
</ul>
 
=end html_DE

=for :application/json;q=META.json 00_THZ.pm
{
  "author": [
    "immi"
  ],
  "x_fhem_maintainer": [
    "immiimmi"
  ],
  "keywords": [
    "THZ",
    "Tecalor",
    "LWZ",
    "Stieben Eltron",
    "303/403/403/404/504",
    "Waermepumpe",
    "Heatpump",
    "cloudfree",
    "openapi",
    "heizungssteuerung"
  ]
}
=end :application/json;q=META.json


=cut

