############################################## # 00_THZ # $Id$ # by immi 11/2014 my $thzversion = "0.111"; # this code is based on the hard work of Robert; I just tried to port it # http://robert.penz.name/heat-pump-lwz/ # http://heatpumpmonitor.penz.name/heatpumpmonitorwiki/ ######################################################################################## # # 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 feature ":5.10"; use SetExtensions; 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($@); ######################################################################################## # # %sets - all supported protocols are listed 59E # ######################################################################################## my %sets = ( "pOpMode" => {cmd2=>"0A0112", type => "2opmode" }, # 1 Standby bereitschaft; 11 in Automatic; 3 DAYmode; SetbackMode; DHWmode; Manual; Emergency "p01RoomTempDayHC1" => {cmd2=>"0B0005", argMin => "13", argMax => "28", type =>"5temp", unit =>" °C"}, "p02RoomTempNightHC1" => {cmd2=>"0B0008", argMin => "13", argMax => "28", type =>"5temp", unit =>" °C"}, "p03RoomTempStandbyHC1" => {cmd2=>"0B013D", argMin => "13", argMax => "28", type =>"5temp", unit =>" °C"}, "p01RoomTempDayHC1SummerMode" => {cmd2=>"0B0569", argMin => "13", argMax => "28", type =>"5temp", unit =>" °C"}, "p02RoomTempNightHC1SummerMode" => {cmd2=>"0B056B", argMin => "13", argMax => "28", type =>"5temp", unit =>" °C"}, "p03RoomTempStandbyHC1SummerMode" => {cmd2=>"0B056A", argMin => "13", argMax => "28", type =>"5temp", unit =>" °C"}, "p13GradientHC1" => {cmd2=>"0B010E", argMin => "0", argMax => "5", type =>"6gradient", unit =>""}, # 0..5 rappresentato/100 "p14LowEndHC1" => {cmd2=>"0B059E", argMin => "0", argMax => "20", 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 => "13", argMax => "28", type =>"5temp", unit =>" °C"}, "p02RoomTempNightHC2" => {cmd2=>"0C0008", argMin => "13", argMax => "28", type =>"5temp", unit =>" °C"}, "p03RoomTempStandbyHC2" => {cmd2=>"0C013D", argMin => "13", argMax => "28", type =>"5temp", unit =>" °C"}, "p01RoomTempDayHC2SummerMode" => {cmd2=>"0C0569", argMin => "13", argMax => "28", type =>"5temp", unit =>" °C"}, "p02RoomTempNightHC2SummerMode" => {cmd2=>"0C056B", argMin => "13", argMax => "28", type =>"5temp", unit =>" °C"}, "p03RoomTempStandbyHC2SummerMode" => {cmd2=>"0C056A", argMin => "13", argMax => "28", type =>"5temp", unit =>" °C"}, "p16GradientHC2" => {cmd2=>"0C010E", argMin => "0", argMax => "5", type =>"6gradient", unit =>""}, # /100 "p17LowEndHC2" => {cmd2=>"0C059E", argMin => "0", argMax => "20", type =>"5temp", unit =>" K"}, "p18RoomInfluenceHC2" => {cmd2=>"0C010F", argMin => "0", argMax => "100", type =>"1clean", unit =>" %"}, "p04DHWsetDayTemp" => {cmd2=>"0A0013", argMin => "13", argMax => "49", type =>"5temp", unit =>" °C"}, "p05DHWsetNightTemp" => {cmd2=>"0A05BF", argMin => "13", argMax => "49", type =>"5temp", unit =>" °C"}, "p83DHWsetSolarTemp" => {cmd2=>"0A05BE", argMin => "13", argMax => "75", type =>"5temp", unit =>" °C"}, "p06DHWsetStandbyTemp" => {cmd2=>"0A0581", argMin => "13", argMax => "49", type =>"5temp", unit =>" °C"}, "p11DHWsetManualTemp" => {cmd2=>"0A0580", argMin => "13", argMax => "54", type =>"5temp", unit =>" °C"}, "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 =>""}, "p75passiveCooling" => {cmd2=>"0A0575", argMin => "0", argMax => "2", type =>"1clean", unit =>""}, "p21Hist1" => {cmd2=>"0A05C0", argMin => "0", argMax => "10", type =>"5temp", unit =>" K"}, "p22Hist2" => {cmd2=>"0A05C1", argMin => "0", argMax => "10", type =>"5temp", unit =>" K"}, "p23Hist3" => {cmd2=>"0A05C2", argMin => "0", argMax => "5", type =>"5temp", unit =>" K"}, "p24Hist4" => {cmd2=>"0A05C3", argMin => "0", argMax => "5", type =>"5temp", unit =>" K"}, "p25Hist5" => {cmd2=>"0A05C4", argMin => "0", argMax => "5", type =>"5temp", unit =>" K"}, "p29HistAsymmetry" => {cmd2=>"0A05C5", argMin => "1", argMax => "5", type =>"1clean", unit =>""}, "p30integralComponent" => {cmd2=>"0A0162", argMin => "10", argMax => "999", type =>"1clean", unit =>" Kmin"}, "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 => "900", type =>"1clean", unit =>" min"}, #in min "p45UnschedVent1" => {cmd2=>"0A0572", argMin => "0", argMax => "900", type =>"1clean", unit =>" min"}, #in min "p44UnschedVent2" => {cmd2=>"0A0573", argMin => "0", argMax => "900", type =>"1clean", unit =>" min"}, #in min "p43UnschedVent3" => {cmd2=>"0A0574", argMin => "0", argMax => "900", 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 => "11", 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 => "200", type =>"1clean", unit =>""}, "p56OutTempMaxPumpCycles" => {cmd2=>"0A05B9", argMin => "1", argMax => "20", type =>"5temp", unit =>" °C"}, "p57OutTempMinPumpCycles" => {cmd2=>"0A05BA", argMin => "1", argMax => "25", type =>"5temp", unit =>" °C"}, "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 => "20", 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 => "20", 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 =>""} ); ######################################################################################## # # %gets - all supported protocols are listed without header and footer # ######################################################################################## my %getsonly = ( # "hallo" => { }, # "debug_read_raw_register_slow" => { }, "sSol" => {cmd2=>"16", type =>"16sol", unit =>""}, "sDHW" => {cmd2=>"F3", type =>"F3dhw", unit =>""}, "sHC1" => {cmd2=>"F4", type =>"F4hc1", unit =>""}, "sHC2" => {cmd2=>"F5", type =>"F5hc2", unit =>""}, "sHistory" => {cmd2=>"09", type =>"09his", unit =>""}, "sLast10errors" => {cmd2=>"D1", type =>"D1last", unit =>""}, "sGlobal" => {cmd2=>"FB", type =>"FBglob", unit =>""}, #allFB "sTimedate" => {cmd2=>"FC", type =>"FCtime", unit =>""}, "sFirmware" => {cmd2=>"FD", type =>"FDfirm", 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"}, #"sAllE8" => {cmd2=>"E8"}, "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 %gets=(%getsonly, %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 %SomWinMode = ( "01" =>"winter", "02" => "summer"); my %weekday = ( "0" =>"Monday", "1" => "Tuesday", "2" =>"Wednesday", "3" => "Thursday", "4" => "Friday", "5" =>"Saturday", "6" => "Sunday" ); my $firstLoadAll = 0; my $noanswerreceived = 0; my $internalHash; ######################################################################################## # # THZ_Initialize($) # # Parameter hash # ######################################################################################## sub THZ_Initialize($) { my ($hash) = @_; require "$attr{global}{modpath}/FHEM/DevIo.pm"; # 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->{AttrFn} = "THZ_Attr"; $hash->{AttrList}= "IODev do_not_notify:1,0 ignore:0,1 dummy:1,0 showtime:1,0 loglevel:0,1,2,3,4,5,6 " ."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_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_sBoostDHWTotal:0,3600,7200,28800,43200,86400 " . $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 THZ ". "{devicename[\@baudrate]|ip:port}" if(@a != 3); DevIo_CloseDev($hash); my $dev = $a[2]; if($dev eq "none") { Log 1, "$name device is none, commands will be echoed only"; $attr{$name}{dummy} = 1; return undef; } $hash->{DeviceName} = $dev; my $ret = DevIo_OpenDev($hash, 0, "THZ_Refresh_all_gets"); return $ret; } ######################################################################################## # # THZ_Refresh_all_gets - Called once refreshes current reading for all gets and initializes the regular interval calls # # Parameter $hash # ######################################################################################## sub THZ_Refresh_all_gets($) { my ($hash) = @_; # unlink("data.txt"); THZ_RemoveInternalTimer("THZ_GetRefresh"); my $timedelay= 5; #strart after 5 seconds foreach my $cmdhash (keys %gets) { my %par = ( hash => $hash, command => $cmdhash ); RemoveInternalTimer(\%par); InternalTimer(gettimeofday() + ($timedelay) , "THZ_GetRefresh", \%par, 0); #increment 0.6s $timedelay++ $timedelay += 0.6; } #refresh all registers; the register with interval_command ne 0 will keep on refreshing } ######################################################################################## # # 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 $interval = AttrVal($hash->{NAME}, ("interval_".$command), 0); my $replyc = ""; if ($interval) { $interval = 60 if ($interval < 60); #do not allow intervall <60 sec InternalTimer(gettimeofday()+ $interval, "THZ_GetRefresh", $par, 1) ; } if (!($hash->{STATE} eq "disconnected")) { $replyc = THZ_Get($hash, $hash->{NAME}, $command); } return ($replyc); } ##################################### # THZ_Write -- simple write # Parameter: hash and message HEX # ######################################################################################## sub THZ_Write($$) { my ($hash,$msg) = @_; my $name = $hash->{NAME}; my $ll5 = GetLogLevel($name,5); my $bstring; $bstring = $msg; Log $ll5, "$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 $ll5 = GetLogLevel($name,5); my $ll2 = GetLogLevel($name,2); my $data = $hash->{PARTIAL} . uc(unpack('H*', $buf)); Log $ll5, "$name/RAW: $data"; Log $ll2, "$name/RAW: $data"; } ##################################### # # THZ_Ready($) - Cchecks the status # # Parameter hash # ######################################################################################## sub THZ_Ready($) { my ($hash) = @_; if($hash->{STATE} eq "disconnected") { THZ_RemoveInternalTimer("THZ_GetRefresh"); select(undef, undef, undef, 0.1); #equivalent to sleep 100ms 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: and " 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)); return "\"set $name $cmd\" needs at least one further argument: " if(!defined($arg)); my $cmdHex2 = $cmdhash->{cmd2}; my $argMax = $cmdhash->{argMax}; my $argMin = $cmdhash->{argMin}; # check the parameter range given ($cmdhash->{type}) { when (["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 (($arg1 gt $argMax) or ($arg1 lt $argMin) or ($arg gt $argMax) or ($arg lt $argMin)) ) ; } when ("2opmode") { $arg1=$arg; $arg=$Rev_OpMode{$arg}; return "Unknown argument $arg1: $cmd supports " . join(" ", sort values %OpMode) if(!defined($arg)); } default { return "Argument does not match the allowed inerval Min $argMin ...... Max $argMax " if(($arg > $argMax) or ($arg < $argMin)); } } given ($cmdhash->{type}) { when ("9holy") {$arg= time2quaters($arg)} when ("8party") {$arg= time2quaters($arg1) *256 + time2quaters($arg)} #non funziona when ("7prog") {$arg= time2quaters($arg) *256 + time2quaters($arg1)} when ("6gradient") {$arg=$arg*100} when ("5temp") {$arg=$arg*10} when (["2opmode", "0clean"]) {$arg=$arg*256} #doubble shift ;;; conversion done above for opmode when ("1clean") { } default { } } Log3 $hash->{NAME}, 5, "THZ_Set: '$cmd $arg' ... Check if port is open. State = '($hash->{STATE})'"; $cmdHex2=THZ_encodecommand(($cmdHex2 . substr((sprintf("%04X", $arg)), -4)),"set"); #04X converts to hex and fills up 0s; for negative, it must be trunckated. ($err, $msg) = THZ_Get_Comunication($hash, $cmdHex2); #$err=undef; if (defined($err)) { return ($cmdHex2 . "-". $msg ."--" . $err);} else { $msg=THZ_Get($hash, $name, $cmd); #take care of program of the week given ($a[1]) { when (/Mo-So/) { $a[1] =~ s/Mo-So/Mo-Fr/; $msg.= "\n" . THZ_Set($hash, @a); select(undef, undef, undef, 0.2); $a[1] =~ s/Mo-Fr/Sa-So/; $msg.="\n" . THZ_Set($hash, @a); } when (/Mo-Fr/) { $a[1] =~ s/_Mo-Fr_/_Mo_/; $msg.="\n" . THZ_Set($hash, @a); $a[1] =~ s/_Mo_/_Tu_/ ; $msg.="\n" . THZ_Set($hash, @a); $a[1] =~ s/_Tu_/_We_/ ; $msg.="\n" . THZ_Set($hash, @a); $a[1] =~ s/_We_/_Th_/ ; $msg.="\n" . THZ_Set($hash, @a); $a[1] =~ s/_Th_/_Fr_/ ; $msg.="\n" . THZ_Set($hash, @a); } when (/Sa-So/) { $a[1] =~ s/_Sa-So_/_Sa_/; $msg.="\n" . THZ_Set($hash, @a); $a[1] =~ s/_Sa_/_So_/ ; $msg.="\n" . THZ_Set($hash, @a); } default {} } #split _ mo-fr when [3] undefined do nothing, when mo-fr chiama gli altri return ($msg); } } ##################################### # # 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}; my $ll5 = GetLogLevel($name,5); my $ll2 = GetLogLevel($name,2); 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"); } my $cmdhash = $gets{$cmd}; return "Unknown argument $cmd, choose one of " . join(" ", sort keys %gets) if(!defined($cmdhash)); Log3 $hash->{NAME}, 5, "THZ_Get: Try to get '$cmd'"; my $cmdHex2 = $cmdhash->{cmd2}; if(defined($cmdHex2) ) { $cmdHex2=THZ_encodecommand($cmdHex2,"get"); ($err, $msg2) = THZ_Get_Comunication($hash, $cmdHex2); if (defined($err)) { Log3 $hash->{NAME}, 5, "THZ_Get: Error msg2: '$err'"; return ($msg2 ."\n msg2 " . $err); } $msg2 = THZ_Parse1($hash,$msg2); } my $cmdHex3 = $cmdhash->{cmd3}; if(defined($cmdHex3)) { my $msg3= " "; $cmdHex3=THZ_encodecommand($cmdHex3,"get"); ($err, $msg3) = THZ_Get_Comunication($hash, $cmdHex3); if (defined($err)) { Log3 $hash->{NAME}, 5, "THZ_Get: Error msg3: '$err'"; 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); #open (MYFILE, '>>data.txt'); #print MYFILE ($cmd . "-" . $msg2 . "\n"); #close (MYFILE); return ($msg2); } ##################################### # # THZ_Get_Comunication- provides a method for reading comunication called from THZ_Get # # 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", "");} THZ_Write($hash, "02"); # STX start of text ($err, $msg) = THZ_ReadAnswer($hash); #Expectedanswer1 is "10" DLE data link escape if ($msg eq "10") { THZ_Write($hash, $cmdHex); # send request SOH start of heading -- Null -- ?? -- DLE data link escape -- EOT End of Text ($err, $msg) = THZ_ReadAnswer($hash); #Expectedanswer2 is "1002", DLE data link escape -- STX start of text } if ($msg eq "10") { ($err, $msg) = THZ_ReadAnswer($hash); } if($msg eq "1002" || $msg eq "02") { THZ_Write($hash, "10"); # DLE data link escape // ack datatranfer #select(undef,undef,undef,0.010); ($err, $msg) = THZ_ReadAnswer($hash); # Expectedanswer3 // read from the heatpump THZ_Write($hash, "10"); } if (!(defined($err))) {($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, "$hash->{NAME} start Funktion THZ_ReadAnswer"; my $buf = DevIo_SimpleReadWithTimeout($hash, 0.7); if(!defined($buf)) { Log3 $hash->{NAME}, 3, "$hash->{NAME} THZ_ReadAnswer got no answer from DevIo_SimpleRead. Maybe too slow?"; return ("InterfaceNotRespondig", ""); } my $data = uc(unpack('H*', $buf)); my $count =1; while ((length($data) > 4) and ($data !~ m/1003$/m ) and ($count <= 10)) { my $buf1 = DevIo_SimpleReadWithTimeout($hash, 0.05); Log3($hash->{NAME}, 5, "double read $count activated $data"); if(defined($buf1)) { $buf = ($buf . $buf1) ; $data = uc(unpack('H*', $buf)); Log3($hash->{NAME}, 5, "double read $count result with buf1 $data"); } $count ++; } return ("WInterface max repeat limited to 10" , $data) if ($count == 11); 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); } #################################### # # 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); } #################################### # # 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: # - each sequece 2B 18 must be replaced with single byte 2B # - each sequece 10 10 must be replaced with single byte 10 my $find = "1010"; my $replace = "10"; $message_orig=THZ_replacebytes($message_orig, $find, $replace); $find = "2B18"; $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("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) . "crc_error in answer", $message_orig)}; } if ($header eq "0103") { return ("command not known", $message_orig); } if ($header eq "0102") { return ("CRC error in request", $message_orig); } if ($header eq "0104") { return ("UNKNOWN REQUEST", $message_orig); } if ($header eq "0180") { return (undef, $message_orig); } return ("new unknown answer " , $message_orig); } ############################### #added by jakob do not know if needed # ############################### local $SIG{__WARN__} = sub { my $message = shift; if (!defined($internalHash)) { Log 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("","F70B000500E6")} ####################################### sub THZ_Parse1($$) { my %parsinghash = ( #msgtype => parsingrule "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] ], "16sol" => [["collector_temp: ", 4, 4, "hex2int", 10], [" dhw_temp: ", 8, 4, "hex2int", 10], [" flow_temp: ", 12, 4, "hex2int", 10], [" ed_sol_pump_temp: ", 16, 4, "hex2int", 10], [" x20: ", 20, 4, "hex2int", 1], [" x24: ", 24, 4, "hex2int", 1], [" x28: ", 28, 4, "hex2int", 1], [" x32: ", 32, 2, "hex2int", 1] ], "D1last" => [["number_of_faults: ", 4, 2, "hex", 1], [" fault0CODE: ", 8, 2, "hex", 1], [" fault0TIME: ", 12, 4, "turnhex2time", 1], [" fault0DATE: ", 16, 4, "turnhex", 100], [" fault1CODE: ", 20, 2, "hex", 1], [" fault1TIME: ", 24, 4, "turnhex2time", 1], [" fault1DATE: ", 28, 4, "turnhex", 100], [" fault2CODE: ", 32, 2, "hex", 1], [" fault2TIME: ", 36, 4, "turnhex2time", 1], [" fault2DATE: ", 40, 4, "turnhex", 100], [" fault3CODE: ", 44, 2, "hex", 1], [" fault3TIME: ", 48, 4, "turnhex2time", 1], [" fault3DATE: ", 52, 4, "turnhex", 100] ], "F3dhw" => [["dhw_temp: ", 4, 4, "hex2int", 10], [" outside_temp: ", 8, 4, "hex2int", 10], [" dhw_set_temp: ", 12, 4, "hex2int", 10], [" comp_block_time: ", 16, 4, "hex2int", 1], [" x20: ", 20, 4, "hex2int", 1], [" heat_block_time: ", 24, 4, "hex2int", 1], [" x28: ", 28, 4, "hex2int", 1], [" x32: ", 32, 4, "hex2int", 1], [" x36: ", 36, 4, "hex", 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], #[" x32: ", 32, 4, "hex2int", 1], [" seasonMode: ", 38, 2, "somwinmode", 1],#[" x40: ", 40, 4, "hex2int", 1], [" integralSwitch: ", 44, 4, "hex2int", 1], [" opMode: ", 48, 2, "opmodehc", 1], #[" x52: ", 52, 4, "hex2int", 1], [" roomSetTemp: ", 56, 4, "hex2int", 10] ], "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],[" opMode: ", 36, 2, "opmodehc", 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], [" rvuRelease: ", 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, "hex2int", 1], [" actualPower_Pel: ", 102, 8, "hex2int", 1], [" collectorTemp: ", 4, 4, "hex2int", 10], [" insideTemp: ", 32, 4, "hex2int", 10] #, [" x84: ", 84, 4, "donottouch", 1] ], "FCtime" => [["Weekday: ", 4, 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] ], "FDfirm" => [["version: ", 4, 4, "hex", 100] ], "0clean" => [["", 8, 2, "hex", 1] ], "1clean" => [["", 8, 4, "hex", 1] ], "2opmode" => [["", 8, 2, "opmode", 1] ], "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] ] ); 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)/) ; my $msgtype; my $parsingrule; my $parsingelement; # search for the type in %gets foreach my $cmdhash (values %gets) { if ($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 $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}, 3, "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 #Log3 $hash->{NAME},3, "after: '$message'"; } my $value = substr($message, $positionInMsg, $lengthInMsg); given ($Type) { when ("hex") {$value= hex($value);} when ("year") {$value= hex($value)+2000;} when ("hex2int") {$value= hex2int($value);} when ("turnhex") {$value= hex(substr($value, 2,2) . substr($value, 0,2));} when ("turnhex2time") {$value= sprintf(join(':', split("\\.", hex(substr($value, 2,2) . substr($value, 0,2))/100))) ;} when ("opmode") {$value= $OpMode{hex($value)};} when ("opmodehc") {$value= $OpModeHC{hex($value)};} when ("somwinmode") {$value= $SomWinMode{($value)};} when ("weekday") {$value= $weekday{($value)};} when ("quater") {$value= quaters2time($value);} when ("bit0") {$value= (hex($value) & 0b0001) / 0b0001;} when ("bit1") {$value= (hex($value) & 0b0010) / 0b0010;} when ("bit2") {$value= (hex($value) & 0b0100) / 0b0100;} when ("bit3") {$value= (hex($value) & 0b1000) / 0b1000;} when ("nbit0") {$value= 1-((hex($value) & 0b0001) / 0b0001);} when ("nbit1") {$value= 1-((hex($value) & 0b0010) / 0b0010);} } $value = $value/$divisor if ($divisor != 1); $ParsedMsg = $ParsedMsg . $parsingtitle . $value; } } return (undef, $ParsedMsg); } ######################################################################################## # only for debug # ######################################################################################## sub THZ_debugread($){ my ($hash) = @_; my ($err, $msg) =("", " "); # my @numbers=('01', '09', '16', 'D1', 'D2', 'E8', 'E9', 'F2', 'F3', 'F4', 'F5', 'F6', 'FB', 'FC', 'FD', 'FE'); #my @numbers=('0A0597','0A0598', '0A0599', '0A059A', '0A059B', '0A059C',); #my @numbers = (1..255); #my @numbers = (1..65535); my @numbers = (1..3179); my $indice= "FF"; unlink("data.txt"); #delete debuglog foreach $indice(@numbers) { #my $cmd = sprintf("%02X", $indice); my $cmd = "0A" . sprintf("%04X", $indice); # my $cmd = $indice; my $cmdHex2 = THZ_encodecommand($cmd,"get"); #($err, $msg) = THZ_Get_Comunication($hash, $cmdHex2); #STX start of text THZ_Write($hash, "02"); ($err, $msg) = THZ_ReadAnswer($hash); # send request THZ_Write($hash, $cmdHex2); select(undef, undef, undef, 0.01); ($err, $msg) = THZ_ReadAnswer($hash); # ack datatranfer and read from the heatpump THZ_Write($hash, "10"); select(undef, undef, undef, 0.05); ($err, $msg) = THZ_ReadAnswer($hash); THZ_Write($hash, "10"); #my $activatetrigger =1; # readingsSingleUpdate($hash, $cmd, $msg, $activatetrigger); # open (MYFILE, '>>data.txt'); # print MYFILE ($cmdHex2 . "-" . $msg . "\n"); # close (MYFILE); if (defined($err)) {return ($msg ."\n" . $err);} else { #clean up and remove footer and header ($err, $msg) = THZ_decode($msg); if (defined($err)) {$msg=$cmdHex2 ."-". $msg ."-". $err;} my $activatetrigger =1; # readingsSingleUpdate($hash, $cmd, $msg, $activatetrigger); open (MYFILE, '>>data.txt'); print MYFILE ($cmd . "-" . $msg . "\n"); close (MYFILE); } select(undef, undef, undef, 0.05); #equivalent to sleep 50ms } } ####################################### #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}; if( $attrName =~ /^interval_/ ) { #DevIo_CloseDev($hash); THZ_RemoveInternalTimer("THZ_GetRefresh"); #sleep 1; #DevIo_OpenDev($hash, 1, "THZ_Refresh_all_gets"); THZ_Refresh_all_gets($hash); } return undef; } ##################################### sub THZ_Undef($$) { my ($hash, $arg) = @_; my $name = $hash->{NAME}; THZ_RemoveInternalTimer("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}; } } DevIo_CloseDev($hash); return undef; } ########################################## # 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 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
wl_hr2
##################################### sub THZ_PrintcurveSVG { my $ret = <<'END'; HC1 heat SetTemp °C outside temperature filtered °C -15 -9 -3 3 9 15 21 10 20 30 40 50 10 20 30 40 50 END my $insideTemp=(split ' ',ReadingsVal("Mythz","sGlobal",14))[81]; $insideTemp="n.a." if ($insideTemp eq "-60"); #in case internal room sensor not connected my $roomSetTemp =(split ' ',ReadingsVal("Mythz","sHC1",0))[21]; $roomSetTemp ="1" if ($roomSetTemp == 0); #division by 0 is bad my $p13GradientHC1 = ReadingsVal("Mythz","p13GradientHC1",0.4); my $heatSetTemp =(split ' ',ReadingsVal("Mythz","sHC1",17))[11]; my $p15RoomInfluenceHC1 = (split ' ',ReadingsVal("Mythz","p15RoomInfluenceHC1",0))[0]; my $outside_tempFiltered =(split ' ',ReadingsVal("Mythz","sGlobal",0))[65]; my $p14LowEndHC1 =(split ' ',ReadingsVal("Mythz","p14LowEndHC1",0))[0]; ############willi data #$insideTemp=24.6; #$roomSetTemp = 21; #$p13GradientHC1 = 0.26; #$heatSetTemp = 21.3; #$p15RoomInfluenceHC1 = 50; #$outside_tempFiltered = 13.1; #$p14LowEndHC1 =1.5; #labels ###################### $ret .= ' --- heat curve' ; $ret .= ' --- working point: outside_tempFiltered='; $ret .= $outside_tempFiltered . ' heatSetTemp=' . $heatSetTemp . ''; #title ###################### $ret .= ''; $ret .= 'roomSetTemp=' . $roomSetTemp . ' p13GradientHC1=' . $p13GradientHC1 . ' p14LowEndHC1=' . $p14LowEndHC1 . ' p15RoomInfluenceHC1=' . $p15RoomInfluenceHC1 . " insideTemp=" . $insideTemp .' '; #equation#################### $insideTemp=$roomSetTemp if ($insideTemp eq "n.a."); my $a= 1 + ($roomSetTemp * (1 + $p13GradientHC1 * 0.87)) + $p14LowEndHC1 + ($p15RoomInfluenceHC1 * $p13GradientHC1 * ($roomSetTemp - $insideTemp) /10); my $b= -14 * $p13GradientHC1 / $roomSetTemp; my $c= -1 * $p13GradientHC1 /75; my $Simul_heatSetTemp; #point ###################### $ret .=''; #curve ###################### $ret .='

THZ

    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.
    Tested on a THZ303/Sol (with serial speed 57600/115200@USB) and a THZ403 (with serial speed 115200) with the same Firmware 4.39.
    Tested on a LWZ404 (with serial speed 115200) with Firmware 5.39.
    Tested on fritzbox, nas-qnap, raspi and macos.
    This module is not working if you have an older firmware; Nevertheless, "parsing" could be easily updated, because now the registers are well described. https://answers.launchpad.net/heatpumpmonitor/+question/100347
    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

    Define
      define <name> THZ <device>

      device can take the same parameters (@baudrate, @directio, TCP/IP, none) like the CUL, e.g 57600 baud or 115200.
      Example: direct connection
        define Mytecalor THZ /dev/ttyUSB0@115200
      or network connection (like via ser2net)
        define Myremotetecalor THZ 192.168.0.244:2323

        define Mythz THZ /dev/ttyUSB0@115200
        attr Mythz interval_sGlobal 300 # internal polling interval 5min
        attr Mythz interval_sHistory 28800 # internal polling interval 8h
        attr Mythz interval_sLast10errors 86400 # internal polling interval 24h
        attr Mythz interval_sSol 86400 # internal polling interval 24h
        attr Mythz interval_sDHW 86400 # internal polling interval 24h
        attr Mythz interval_sHC1 86400 # internal polling interval 24h
        attr Mythz interval_sHC2 86400 # internal polling interval 24h
        define FileLog_Mythz FileLog ./log/Mythz-%Y.log Mythz

      If the attributes interval_allFB and interval_history are not defined (or 0), their internal polling is disabled. Clearly you can also define the polling interval outside the module with the "at" command.
        define Mythz THZ /dev/ttyUSB0@115200
        define atMythzFB at +*00:05:00 {fhem "get Mythz sGlobal","1";;return()}
        define atMythz09 at +*08:00:00 {fhem "get Mythz sHistory","1";;return()}
        define FileLog_Mythz FileLog ./log/Mythz-%Y.log Mythz

=end html =begin html_DE

THZ

    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.
    Getestet mit einer Tecalor THZ303/Sol (Serielle Geschwindigkeit 57600/115200@USB) und einer THZ403 (Serielle Geschwindigkeit 115200) mit identischer Firmware 4.39.
    Getestet mit einer Stiebel LWZ404 (Serielle Geschwindigkeit 115200@USB) mit Firmware 5.39.
    Getestet auf FritzBox, nas-qnap, Raspberry Pi and MacOS.
    Dieses Modul funktioniert nicht mit äterer Firmware; Gleichwohl, das "parsing" könnte leicht angepasst werden da die Register gut beschrieben wurden. https://answers.launchpad.net/heatpumpmonitor/+question/100347
    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

    Define
      define <name> THZ <device>

      device kann einige Parameter beinhalten (z.B. @baudrate, @direction, TCP/IP, none) wie das CUL, z.B. 57600 baud oder 115200.
      Beispiel:
      Direkte Verbindung
        define Mytecalor THZ /dev/ttyUSB0@115200
      oder vir Netzwerk (via ser2net)
        define Myremotetecalor THZ 192.168.0.244:2323

        define Mythz THZ /dev/ttyUSB0@115200
        attr Mythz interval_sGlobal 300 # Internes Polling Intervall 5min
        attr Mythz interval_sHistory 28800 # Internes Polling Intervall 8h
        attr Mythz interval_sLast10errors 86400 # Internes Polling Intervall 24h
        attr Mythz interval_sSol 86400 # Internes Polling Intervall 24h
        attr Mythz interval_sDHW 86400 # Internes Polling Intervall 24h
        attr Mythz interval_sHC1 86400 # Internes Polling Intervall 24h
        attr Mythz interval_sHC2 86400 # Internes Polling Intervall 24h
        define FileLog_Mythz FileLog ./log/Mythz-%Y.log Mythz

      Wenn die Attribute interval_allFB und interval_history nicht definiert sind (oder 0), ist das interne Polling deaktiviert. Natürlich kann das Polling auch mit dem "at" Befehl ausserhalb des Moduls definiert werden.
        define Mythz THZ /dev/ttyUSB0@115200
        define atMythzFB at +*00:05:00 {fhem "get Mythz sGlobal","1";;return()}
        define atMythz09 at +*08:00:00 {fhem "get Mythz sHistory","1";;return()}
        define FileLog_Mythz FileLog ./log/Mythz-%Y.log Mythz

=end html_DE =cut