98_weekprofile.pm 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666
  1. ##############################################
  2. # $Id: 98_weekprofile.pm 16140 2018-02-10 13:55:57Z Risiko $
  3. #
  4. # Usage
  5. #
  6. # define <name> weekprofile [device]
  7. ##############################################
  8. package main;
  9. use strict;
  10. use warnings;
  11. use JSON; #libjson-perl
  12. use Data::Dumper;
  13. use Storable qw(dclone);
  14. use vars qw(%defs);
  15. use vars qw($FW_ME);
  16. use vars qw($FW_wname);
  17. use vars qw($FW_subdir);
  18. use vars qw($init_done);
  19. my @shortDays = ("Mon","Tue","Wed","Thu","Fri","Sat","Sun");
  20. my @DEVLIST_SEND = ("MAX","CUL_HM","HMCCUDEV","weekprofile","dummy");
  21. my $CONFIG_VERSION = "1.1";
  22. my %DEV_READINGS;
  23. # MAX
  24. $DEV_READINGS{"Mon"}{"MAX"} = "weekprofile-2-Mon";
  25. $DEV_READINGS{"Tue"}{"MAX"} = "weekprofile-3-Tue";
  26. $DEV_READINGS{"Wed"}{"MAX"} = "weekprofile-4-Wed";
  27. $DEV_READINGS{"Thu"}{"MAX"} = "weekprofile-5-Thu";
  28. $DEV_READINGS{"Fri"}{"MAX"} = "weekprofile-6-Fri";
  29. $DEV_READINGS{"Sat"}{"MAX"} = "weekprofile-0-Sat";
  30. $DEV_READINGS{"Sun"}{"MAX"} = "weekprofile-1-Sun";
  31. # CUL_HM
  32. $DEV_READINGS{"Mon"}{"CUL_HM"} = "2_tempListMon";
  33. $DEV_READINGS{"Tue"}{"CUL_HM"} = "3_tempListTue";
  34. $DEV_READINGS{"Wed"}{"CUL_HM"} = "4_tempListWed";
  35. $DEV_READINGS{"Thu"}{"CUL_HM"} = "5_tempListThu";
  36. $DEV_READINGS{"Fri"}{"CUL_HM"} = "6_tempListFri";
  37. $DEV_READINGS{"Sat"}{"CUL_HM"} = "0_tempListSat";
  38. $DEV_READINGS{"Sun"}{"CUL_HM"} = "1_tempListSun";
  39. # HMCCUDEV
  40. $DEV_READINGS{"Mon"}{"HMCCUDEV"} = "MONDAY";
  41. $DEV_READINGS{"Tue"}{"HMCCUDEV"} = "TUESDAY";
  42. $DEV_READINGS{"Wed"}{"HMCCUDEV"} = "WEDNESDAY";
  43. $DEV_READINGS{"Thu"}{"HMCCUDEV"} = "THURSDAY";
  44. $DEV_READINGS{"Fri"}{"HMCCUDEV"} = "FRIDAY";
  45. $DEV_READINGS{"Sat"}{"HMCCUDEV"} = "SATURDAY";
  46. $DEV_READINGS{"Sun"}{"HMCCUDEV"} = "SUNDAY";
  47. sub weekprofile_findPRF($$$$);
  48. ##############################################
  49. sub weekprofile_minutesToTime($)
  50. {
  51. my ($minutes) = @_;
  52. my $hours = $minutes / 60;
  53. $minutes = $minutes - $hours * 60;
  54. if (length($hours) eq 1){
  55. $hours = "0$hours";
  56. }
  57. if (length($minutes) eq 1){
  58. $minutes = "0$minutes";
  59. }
  60. return "$hours:$minutes";
  61. }
  62. ##############################################
  63. sub weekprofile_timeToMinutes($)
  64. {
  65. my ($time) = @_;
  66. my ($hours, $minutes) = split(':',$time, 2);
  67. return $hours * 60 + $minutes;
  68. }
  69. ##############################################
  70. sub myAttrVal($$$)
  71. {
  72. my ($me,$name,$def) = @_;
  73. my $val = AttrVal($me, $name, $def);
  74. if (defined($val) && ($name eq 'tempON' || $name eq 'tempOFF')) {
  75. $val = sprintf("%.1f", $val);
  76. }
  77. return $val;
  78. }
  79. ##############################################
  80. sub weekprofile_getDeviceType($$;$)
  81. {
  82. my ($me,$device,$sndrcv) = @_;
  83. $sndrcv = "RCV" if (!defined($sndrcv));
  84. # determine device type
  85. my $devHash = $main::defs{$device};
  86. if (!defined($devHash)){
  87. return undef;
  88. }
  89. my $type = undef;
  90. if ($devHash->{TYPE} =~ /CUL_HM/){
  91. my $model = AttrVal($device,"model","");
  92. #models: HM-TC-IT-WM-W-EU, HM-CC-RT-DN, HM-CC-TC
  93. unless ($model =~ m/.*HM-[C|T]C-.*/) {
  94. Log3 $me, 4, "$me(getDeviceType): $devHash->{NAME}, model $model is not supported";
  95. return undef;
  96. }
  97. if (!defined($devHash->{chanNo})) { #no channel device
  98. Log3 $me, 4, "$me(getDeviceType): $devHash->{NAME}, model $model has no chanNo";
  99. return undef;
  100. }
  101. my $channel = $devHash->{chanNo};
  102. unless ($channel =~ /^\d+?$/) {
  103. Log3 $me, 4, "$me(getDeviceType): $devHash->{NAME}, model $model chanNo $channel is no number";
  104. return undef;
  105. }
  106. $channel += 0;
  107. Log3 $me, 5, "$me(getDeviceType): $devHash->{NAME}, $model, $channel";
  108. $type = "CUL_HM" if ( ($model =~ m/.*HM-CC-RT.*/) && ($channel == 4) );
  109. $type = "CUL_HM" if ( ($model =~ m/.*HM-TC.*/) && ($channel == 2) );
  110. $type = "CUL_HM" if ( ($model =~ m/.*HM-CC-TC.*/) && ($channel == 2) );
  111. }
  112. #avoid max shutter contact
  113. elsif ( ($devHash->{TYPE} =~ /MAX/) && ($devHash->{type} =~ /.*Thermostat.*/) ){
  114. $type = "MAX";
  115. }
  116. elsif ($devHash->{TYPE} =~ /dummy/){
  117. $type = "MAX" if ($device =~ m/.*MAX.*FAKE.*/); #dummy (FAKE WT) with name MAX inside for testing
  118. $type = "CUL_HM" if ($device =~ m/.*CUL_HM.*FAKE.*/); #dummy (FAKE WT) with name CUL_HM inside for testing
  119. }
  120. elsif ( $devHash->{TYPE} =~ /HMCCUDEV/){
  121. my $model = $devHash->{ccutype};
  122. $type = "HMCCUDEV" if ( $model =~ /HmIP-eTRV-2/ );
  123. }
  124. return $type if ($sndrcv eq "RCV");
  125. if ($devHash->{TYPE} =~ /weekprofile/){
  126. $type = "WEEKPROFILE";
  127. }
  128. if (defined($type)) {
  129. Log3 $me, 4, "$me(getDeviceType): $devHash->{NAME} is type $type";
  130. } else {
  131. Log3 $me, 4, "$me(getDeviceType): $devHash->{NAME} is not supported";
  132. }
  133. return $type;
  134. }
  135. ##############################################
  136. sub weekprofile_readDayProfile($@)
  137. {
  138. my ($device,$day,$type,$me) = @_;
  139. my @times;
  140. my @temps;
  141. $type = weekprofile_getDeviceType($me,$device) if (!defined($type));
  142. return if (!defined($type));
  143. my $reading = $DEV_READINGS{$day}{$type};
  144. #Log3 $me, 5, "$me(ReadDayProfile): $reading";
  145. if($type eq "MAX") {
  146. @temps = split('/',ReadingsVal($device,"$reading-temp",""));
  147. @times = split('/',ReadingsVal($device,"$reading-time",""));
  148. # only use to from interval 'from-to'
  149. for(my $i = 0; $i < scalar(@times); $i+=1){
  150. my $interval = $times[$i];
  151. my @parts = split('-',$interval);
  152. $times[$i] = ($parts[1] ne "00:00") ? $parts[1] : "24:00";
  153. }
  154. } elsif ($type eq "CUL_HM") {
  155. # get temp list for the day
  156. my $prf = ReadingsVal($device,"R_$reading","");
  157. $prf = ReadingsVal($device,"R_P1_$reading","") if (!$prf); #HM-TC-IT-WM-W-EU
  158. # split into time temp time temp etc.
  159. # 06:00 17.0 22:00 21.0 24:00 17.0
  160. my @timeTemp = split(' ', $prf);
  161. for(my $i = 0; $i < scalar(@timeTemp); $i += 2) {
  162. push(@times, $timeTemp[$i]);
  163. push(@temps, $timeTemp[$i+1]);
  164. }
  165. }
  166. elsif ($type eq "HMCCUDEV"){
  167. my $lastTime = "";
  168. for (my $i = 1; $i < 14; $i+=1){
  169. my $prfTemp = ReadingsVal($device, "R-1.P1_TEMPERATURE_" . $reading . "_$i", "");
  170. my $prfTime = ReadingsVal($device, "R-1.P1_ENDTIME_" . $reading . "_$i", "");
  171. $prfTime = weekprofile_minutesToTime($prfTime);
  172. if ($lastTime ne $prfTime){
  173. $lastTime = $prfTime;
  174. push(@temps, $prfTemp);
  175. push(@times, $prfTime);
  176. }
  177. }
  178. }
  179. for(my $i = 0; $i < scalar(@temps); $i+=1){
  180. Log3 $me, 4, "$me(ReadDayProfile): temp $i $temps[$i]";
  181. $temps[$i] =~s/[^\d.]//g; #only numbers
  182. my $tempON = myAttrVal($me, "tempON", undef);
  183. my $tempOFF = myAttrVal($me, "tempOFF", undef);
  184. $temps[$i] =~s/$tempOFF/off/g if (defined($tempOFF)); # temp off
  185. $temps[$i] =~s/$tempON/on/g if (defined($tempON)); # temp on
  186. }
  187. for(my $i = 0; $i < scalar(@times); $i+=1){
  188. $times[$i] =~ s/^\s+|\s+$//g; #trim whitespace both ends
  189. }
  190. return (\@times, \@temps);
  191. }
  192. ##############################################
  193. sub weekprofile_readDevProfile(@)
  194. {
  195. my ($device,$type,$me) = @_;
  196. $type = weekprofile_getDeviceType($me, $device) if (!defined($type));
  197. return "" if (!defined ($type));
  198. my $prf = {};
  199. my $logDaysWarning="";
  200. my $logDaysCnt=0;
  201. foreach my $day (@shortDays){
  202. my ($dayTimes, $dayTemps) = weekprofile_readDayProfile($device,$day,$type,$me);
  203. if (scalar(@{$dayTemps})==0) {
  204. push(@{$dayTimes}, "24:00");
  205. push(@{$dayTemps}, "18.0");
  206. $logDaysWarning .= "\n" if ($logDaysCnt>0);
  207. $logDaysWarning .= "WARNING master device $device has no day profile for $day - create default";
  208. $logDaysCnt++;
  209. }
  210. $prf->{$day}->{"temp"} = $dayTemps;
  211. $prf->{$day}->{"time"} = $dayTimes;
  212. }
  213. if ( ($logDaysCnt>0) && ($logDaysCnt<(@shortDays)) ) {
  214. Log3 $me, 3, $logDaysWarning;
  215. } else {
  216. if ($logDaysCnt == (@shortDays)) {
  217. Log3 $me, 3, "WARNING master device $device has no week profile - create default";
  218. }
  219. }
  220. return $prf;
  221. }
  222. ##############################################
  223. sub weekprofile_createDefaultProfile(@)
  224. {
  225. my ($hash) = @_;
  226. my $prf = {};
  227. foreach my $day (@shortDays){
  228. my @times; push(@times, "24:00");
  229. my @temps; push(@temps, "18.0");
  230. $prf->{$day}->{"temp"} = \@temps;
  231. $prf->{$day}->{"time"} = \@times;
  232. }
  233. return $prf;
  234. }
  235. ##############################################
  236. sub weekprofile_sendDevProfile(@)
  237. {
  238. my ($device,$prf,$me) = @_;
  239. my $type = weekprofile_getDeviceType($me, $device,"SND");
  240. return "Error device type not supported" if (!defined ($type));
  241. return "profile has no data" if (!defined($prf->{DATA}));
  242. if ($type eq "WEEKPROFILE") {
  243. my $json = JSON->new;
  244. my $json_text = undef;
  245. eval ( $json_text = $json->encode($prf->{DATA}) );
  246. return "Error in profile data" if (!defined($json_text));
  247. return fhem("set $device profile_data $prf->{TOPIC}:$prf->{NAME} $json_text",1);
  248. }
  249. my $devPrf = weekprofile_readDevProfile($device,$type,$me);
  250. # only send changed days
  251. my @dayToTransfer = ();
  252. foreach my $day (@shortDays){
  253. my $tmpCnt = scalar(@{$prf->{DATA}->{$day}->{"temp"}});
  254. next if ($tmpCnt <= 0);
  255. if ($tmpCnt != scalar(@{$devPrf->{$day}->{"temp"}})) {
  256. push @dayToTransfer , $day;
  257. next;
  258. }
  259. my $equal = 1;
  260. for (my $i = 0; $i < $tmpCnt; $i++) {
  261. if ( ($prf->{DATA}->{$day}->{"temp"}[$i] ne $devPrf->{$day}->{"temp"}[$i] ) ||
  262. $prf->{DATA}->{$day}->{"time"}[$i] ne $devPrf->{$day}->{"time"}[$i] ) {
  263. $equal = 0;
  264. last;
  265. }
  266. }
  267. if ($equal == 0) {
  268. push @dayToTransfer , $day;
  269. next;
  270. }
  271. }
  272. if (scalar(@dayToTransfer) <=0) {
  273. Log3 $me, 4, "$me(sendDevProfile): nothing to do";
  274. return undef;
  275. }
  276. #make a copy because of replacements
  277. my $prfData= dclone($prf->{DATA});
  278. my $tempON = myAttrVal($me, "tempON", "30.5");
  279. my $tempOFF = myAttrVal($me, "tempOFF", "4.5");
  280. #replace variables with values
  281. foreach my $day (@dayToTransfer){
  282. my $tmpCnt = scalar(@{$prfData->{$day}->{"temp"}});
  283. for (my $i = 0; $i < $tmpCnt; $i++) {
  284. $prfData->{$day}->{"temp"}[$i] = $tempON if ($prfData->{$day}->{"temp"}[$i] =~/on/i);
  285. $prfData->{$day}->{"temp"}[$i] = $tempOFF if ($prfData->{$day}->{"temp"}[$i] =~/off/i);
  286. }
  287. }
  288. my $cmd;
  289. if($type eq "MAX") {
  290. $cmd = "set $device weekProfile ";
  291. foreach my $day (@dayToTransfer){
  292. my $tmpCnt = scalar(@{$prfData->{$day}->{"temp"}});
  293. $cmd.=$day.' ';
  294. for (my $i = 0; $i < $tmpCnt; $i++) {
  295. my $endTime = $prfData->{$day}->{"time"}[$i];
  296. $endTime = ($endTime eq "24:00") ? ' ' : ','.$endTime.',';
  297. $cmd.=$prfData->{$day}->{"temp"}[$i].$endTime;
  298. }
  299. }
  300. } elsif ($type eq "CUL_HM") {
  301. my $k=0;
  302. my $dayCnt = scalar(@dayToTransfer);
  303. foreach my $day (@dayToTransfer){
  304. $cmd .= "set $device tempList";
  305. $cmd .= $day;
  306. $cmd .= ($k < $dayCnt-1) ? " prep": " exec";
  307. my $tmpCnt = scalar(@{$prfData->{$day}->{"temp"}});
  308. for (my $i = 0; $i < $tmpCnt; $i++) {
  309. $cmd .= " ".$prfData->{$day}->{"time"}[$i]." ".$prfData->{$day}->{"temp"}[$i];
  310. }
  311. $cmd .= ($k < $dayCnt-1) ? "; ": "";
  312. $k++;
  313. }
  314. } elsif ($type eq "HMCCUDEV"){
  315. my $k=0;
  316. my $dayCnt = scalar(@dayToTransfer);
  317. $cmd .= "set $device config 1";
  318. foreach my $day (@dayToTransfer){
  319. #Usage: set <device> datapoint [{channel-number}.]{datapoint} {value}
  320. my $reading = $DEV_READINGS{$day}{$type};
  321. my $dpTime = "P1_ENDTIME_$reading";
  322. my $dpTemp = "P1_TEMPERATURE_$reading";
  323. my $tmpCnt = scalar(@{$prfData->{$day}->{"temp"}});
  324. for (my $i = 0; $i < $tmpCnt; $i++) {
  325. $cmd .= " " . $dpTemp . "_" . ($i + 1) . "=" . $prfData->{$day}->{"temp"}[$i];
  326. $cmd .= " " . $dpTime . "_" . ($i + 1) . "=" . weekprofile_timeToMinutes($prfData->{$day}->{"time"}[$i]);
  327. }
  328. #$cmd .= ($k < $dayCnt-1) ? "; ": "";
  329. $k++;
  330. }
  331. }
  332. my $ret = undef;
  333. if ($cmd) {
  334. $cmd =~ s/^\s+|\s+$//g;
  335. Log3 $me, 4, "$me(sendDevProfile): $cmd";
  336. $ret = fhem($cmd,1);
  337. DoTrigger($me,"PROFILE_TRANSFERED $device",1);
  338. }
  339. return $ret;
  340. }
  341. ##############################################
  342. sub weekprofile_refreshSendDevList($)
  343. {
  344. my ($hash) = @_;
  345. my $me = $hash->{NAME};
  346. delete $hash->{SNDDEVLIST};
  347. foreach my $d (keys %defs)
  348. {
  349. next if ($defs{$d}{NAME} eq $me);
  350. my $module = $defs{$d}{TYPE};
  351. my %sndHash;
  352. @sndHash{@DEVLIST_SEND}=();
  353. next if (!exists $sndHash{$module});
  354. my $type = weekprofile_getDeviceType($me, $defs{$d}{NAME},"SND");
  355. next if (!defined($type));
  356. my $dev = {};
  357. $dev->{NAME} = $defs{$d}{NAME};
  358. $dev->{ALIAS} = AttrVal($dev->{NAME},"alias",$dev->{NAME});
  359. push @{$hash->{SNDDEVLIST}} , $dev;
  360. }
  361. return undef;
  362. }
  363. ##############################################
  364. sub weekprofile_assignDev($)
  365. {
  366. my ($hash) = @_;
  367. my $me = $hash->{NAME};
  368. my $prf = undef;
  369. if (defined($hash->{MASTERDEV})) {
  370. Log3 $me, 5, "$me(assignDev): assign to device $hash->{MASTERDEV}->{NAME}";
  371. my $type = weekprofile_getDeviceType($me, $hash->{MASTERDEV}->{NAME});
  372. if (!defined($type)) {
  373. Log3 $me, 2, "$me(assignDev): device $hash->{MASTERDEV}->{NAME} not supported or defined";
  374. } else {
  375. $hash->{MASTERDEV}->{TYPE} = $type;
  376. my $prfDev = weekprofile_readDevProfile($hash->{MASTERDEV}->{NAME},$type, $me);
  377. $prf = {};
  378. $prf->{NAME} = 'master';
  379. $prf->{TOPIC} = 'default';
  380. if(defined($prfDev)) {
  381. $prf->{DATA} = $prfDev;
  382. } else {
  383. Log3 $me, 3, "WARNING master device $hash->{MASTERDEV}->{NAME} has no week profile - create default profile";
  384. $prf->{DATA} = weekprofile_createDefaultProfile($hash);
  385. }
  386. $hash->{STATE} = "assigned";
  387. }
  388. }
  389. if (!defined($prf)) {
  390. Log3 $me, 5, "create default profile";
  391. my $prfDev = weekprofile_createDefaultProfile($hash);
  392. if(defined($prfDev)) {
  393. $prf = {};
  394. $prf->{DATA} = $prfDev;
  395. $prf->{NAME} = 'default';
  396. $prf->{TOPIC} = 'default';
  397. $hash->{STATE} = "created";
  398. }
  399. }
  400. if(defined($prf)) {
  401. push @{$hash->{PROFILES}} , $prf;
  402. }
  403. readingsBeginUpdate($hash);
  404. readingsBulkUpdate($hash,"state",$hash->{STATE});
  405. readingsEndUpdate($hash, 1);
  406. }
  407. ##############################################
  408. sub weekprofile_updateReadings($)
  409. {
  410. my ($hash) = @_;
  411. my $prfCnt = scalar(@{$hash->{PROFILES}});
  412. readingsBeginUpdate($hash);
  413. readingsBulkUpdate($hash,"profile_count",$prfCnt);
  414. #readings with profile names???
  415. #my $idx = 1;
  416. #foreach my $prf (@{$hash->{PROFILES}}){
  417. #my $str = sprintf("profile_name_%02d",$idx);
  418. #readingsBulkUpdate($hash,$str,$prf->{NAME});
  419. #$idx++;
  420. #}
  421. splice(@{$hash->{TOPICS}});
  422. foreach my $prf (@{$hash->{PROFILES}}) {
  423. if ( !grep( /^$prf->{TOPIC}$/, @{$hash->{TOPICS}}) ) {
  424. push @{$hash->{TOPICS}}, $prf->{TOPIC};
  425. }
  426. }
  427. readingsEndUpdate($hash, 1);
  428. }
  429. ##############################################
  430. sub weekprofile_Initialize($)
  431. {
  432. my ($hash) = @_;
  433. $hash->{DefFn} = "weekprofile_Define";
  434. $hash->{SetFn} = "weekprofile_Set";
  435. $hash->{GetFn} = "weekprofile_Get";
  436. $hash->{SetFn} = "weekprofile_Set";
  437. $hash->{StateFn} = "weekprofile_State";
  438. $hash->{NotifyFn} = "weekprofile_Notify";
  439. $hash->{AttrFn} = "weekprofile_Attr";
  440. $hash->{AttrList} = "useTopics:0,1 widgetTranslations widgetWeekdays widgetEditOnNewPage:0,1 widgetEditDaysInRow:1,2,3,4,5,6,7 tempON tempOFF configFile ".$readingFnAttributes;
  441. $hash->{FW_summaryFn} = "weekprofile_SummaryFn";
  442. $hash->{FW_atPageEnd} = 1;
  443. }
  444. ##############################################
  445. sub weekprofile_Define($$)
  446. {
  447. my ($hash, $def) = @_;
  448. my @a = split("[ \t][ \t]*", $def);
  449. if(@a < 1) {
  450. my $msg = "wrong syntax: define <name> weekprofile [device]";
  451. Log3 undef, 2, $msg;
  452. return $msg;
  453. }
  454. my $me = $a[0];
  455. my $devName = undef;
  456. if (@a > 1) {
  457. $devName = $a[2];
  458. $devName =~ s/(^\s+|\s+$)//g if ($devName);
  459. }
  460. $hash->{MASTERDEV}->{NAME} = $devName if (defined($devName));
  461. $hash->{STATE} = "defined";
  462. my @profiles = ();
  463. my @sendDevList = ();
  464. my @topics = ();
  465. $hash->{PROFILES} = \@profiles;
  466. $hash->{SNDDEVLIST} = \@sendDevList;
  467. $hash->{TOPICS} = \@topics;
  468. #$attr{$me}{verbose} = 5;
  469. if ($init_done) {
  470. weekprofile_refreshSendDevList($hash);
  471. weekprofile_assignDev($hash);
  472. weekprofile_updateReadings($hash);
  473. }
  474. return undef;
  475. }
  476. ##############################################
  477. sub sort_by_name
  478. {
  479. return lc("$a->{TOPIC}:$a->{NAME}") cmp lc("$b->{TOPIC}:$b->{NAME}");
  480. }
  481. ##############################################
  482. sub dumpData($$$)
  483. {
  484. my ($hash,$prefix,$data) = @_;
  485. my $me = $hash->{NAME};
  486. my $dmp = Dumper($data);
  487. $dmp =~ s/^\s+|\s+$//g; #trim whitespace both ends
  488. if (AttrVal($me,"verbose",3) < 4) {
  489. Log3 $me, 1, "$me$prefix - set verbose to 4 to see the data";
  490. } else {
  491. Log3 $me, 4, "$me$prefix $dmp";
  492. }
  493. }
  494. ##############################################
  495. sub weekprofile_Get($$@)
  496. {
  497. my ($hash, $name, $cmd, @params) = @_;
  498. my $list = '';
  499. my $prfCnt = scalar(@{$hash->{PROFILES}});
  500. my $useTopics = AttrVal($name,"useTopics",0);
  501. $list.= 'profile_data:' if ($prfCnt > 0);
  502. foreach my $prf (sort sort_by_name @{$hash->{PROFILES}}){
  503. $list.= $prf->{TOPIC}.":" if ($useTopics);
  504. $list.= $prf->{NAME}."," if ($useTopics || (!$useTopics && ($prf->{TOPIC} eq 'default')));
  505. }
  506. $list = substr($list, 0, -1) if ($prfCnt > 0);
  507. #-----------------------------------------------------------------------------
  508. if($cmd eq "profile_data") {
  509. return 'usage: profile_data <name>' if(@params < 1);
  510. return "no profile" if ($prfCnt <= 0);
  511. my ($topic, $name) = weekprofile_splitName($params[0]);
  512. my ($prf,$idx) = weekprofile_findPRF($hash,$name,$topic,1);
  513. return "profile $params[0] not found" if (!defined($prf));
  514. return "profile $params[0] has no data" if (!defined($prf->{DATA}));
  515. my $json = JSON->new;
  516. my $json_text = undef;
  517. eval { $json_text = $json->encode($prf->{DATA}) };
  518. dumpData($hash,"(Get): invalid profile data",$prf->{DATA}) if (!defined($json_text));
  519. return $json_text;
  520. }
  521. #-----------------------------------------------------------------------------
  522. $list.= ' profile_names';
  523. if($cmd eq "profile_names") {
  524. my $names = '';
  525. my $topic = 'default';
  526. $topic = $params[0] if(@params == 1);
  527. foreach my $prf (sort sort_by_name @{$hash->{PROFILES}}){
  528. $names .=$prf->{NAME}."," if ($topic eq $prf->{TOPIC});
  529. $names .="$prf->{TOPIC}:$prf->{NAME}," if ($topic eq '*');
  530. }
  531. if ($names) {
  532. $names = substr($names, 0, -1);
  533. $names =~ s/ $//;
  534. }
  535. return $names;
  536. }
  537. #-----------------------------------------------------------------------------
  538. $list.= ' profile_references' if ($useTopics);
  539. if($cmd eq "profile_references") {
  540. return 'usage: profile_references <name>' if(@params < 1);
  541. my $refs = '';
  542. my $topic = 'default';
  543. if ($params[0] eq '*') {
  544. foreach my $prf (sort sort_by_name @{$hash->{PROFILES}}){
  545. next if (!defined($prf->{REF}));
  546. $refs .= "$prf->{TOPIC}:$prf->{NAME}>$prf->{REF},";
  547. }
  548. $refs = substr($refs, 0, -1);
  549. } else {
  550. my ($topic, $name) = weekprofile_splitName($params[0]);
  551. my ($prf,$idx) = weekprofile_findPRF($hash,$name,$topic,0);
  552. return "profile $params[0] not found" unless ($prf);
  553. $refs = '0';
  554. $refs = "$prf->{REF}" if ($prf->{REF});
  555. }
  556. return $refs;
  557. }
  558. #-----------------------------------------------------------------------------
  559. $list.= ' topic_names:noArg' if ($useTopics);
  560. if($cmd eq "topic_names") {
  561. my $names = '';
  562. foreach my $topic (sort {lc($a) cmp lc($b)} @{$hash->{TOPICS}}) {
  563. $names .= "$topic,";
  564. }
  565. if ($names) {
  566. $names = substr($names, 0, -1);
  567. $names =~ s/ $//;
  568. }
  569. return $names;
  570. }
  571. if($cmd eq "sndDevList") {
  572. my $json = JSON->new;
  573. my @sortDevList = sort {lc($a->{ALIAS}) cmp lc($b->{ALIAS})} @{$hash->{SNDDEVLIST}};
  574. my $json_text = undef;
  575. eval { $json_text = $json->encode(\@sortDevList) };
  576. dumpData($hash,"(Get): invalid device list",\@sortDevList) if (!defined($json_text));
  577. return $json_text;
  578. }
  579. $list =~ s/ $//;
  580. return "Unknown argument $cmd choose one of $list";
  581. }
  582. ##############################################
  583. sub weekprofile_findPRF($$$$)
  584. {
  585. my ($hash, $name, $topic, $followRef) = @_;
  586. $topic = 'default' if (!$topic);
  587. $followRef = '0' if (!$followRef);
  588. my $found = undef;
  589. my $idx = 0;
  590. foreach my $prf (@{$hash->{PROFILES}}){
  591. if ( ($prf->{NAME} eq $name) && ($prf->{TOPIC} eq $topic) ){
  592. $found = $prf;
  593. last;
  594. }
  595. $idx++;
  596. }
  597. $idx = -1 if (!defined($found));
  598. if ($followRef == 1 && defined($found) && defined($found->{REF})) {
  599. ($topic, $name) = weekprofile_splitName($found->{REF});
  600. ($found,$idx) = weekprofile_findPRF($hash,$name,$topic,0);
  601. }
  602. return ($found,$idx);
  603. }
  604. ##############################################
  605. sub weekprofile_hasREF(@)
  606. {
  607. my ($hash, $refPrf) = @_;
  608. my $refName = "$refPrf->{TOPIC}:$refPrf->{NAME}";
  609. foreach my $prf (@{$hash->{PROFILES}}){
  610. if ( defined($prf->{REF}) && ($prf->{REF} eq $refName) ) {
  611. return "$prf->{TOPIC}:$prf->{NAME}";
  612. }
  613. }
  614. return undef;
  615. }
  616. ##############################################
  617. sub weekprofile_splitName($)
  618. {
  619. my ($in) = @_;
  620. my @parts = split(':',$in);
  621. return ($parts[0],$parts[1]) if (@parts == 2);
  622. return ('default',$in);
  623. }
  624. ##############################################
  625. sub weekprofile_Set($$@)
  626. {
  627. my ($hash, $me, $cmd, @params) = @_;
  628. my $prfCnt = scalar(@{$hash->{PROFILES}});
  629. my $list = '';
  630. my $useTopics = AttrVal($me,"useTopics",0);
  631. $list.= "profile_data";
  632. if ($cmd eq 'profile_data') {
  633. return 'usage: profile_data <name> <json data>' if(@params < 2);
  634. my ($topic, $name) = weekprofile_splitName($params[0]);
  635. return "Error topics not enabled" if (!$useTopics && ($topic ne 'default'));
  636. my $jsonData = $params[1];
  637. my $json = JSON->new;
  638. my $data = undef;
  639. eval { $data = $json->decode($jsonData); };
  640. if (!defined($data)) {
  641. Log3 $me, 1, "$me(Set): Error parsing profile data.";
  642. return "Error parsing profile data. No valid json format";
  643. };
  644. my ($found,$idx) = weekprofile_findPRF($hash,$name,$topic,1);
  645. if (defined($found)) {
  646. $found->{DATA} = $data;
  647. # automatic we send master profile to master device
  648. if ( ($name eq "master") && defined($hash->{MASTERDEV}) ){
  649. weekprofile_sendDevProfile($hash->{MASTERDEV}->{NAME},$found,$me);
  650. } else {
  651. weekprofile_writeProfilesToFile($hash);
  652. }
  653. return undef;
  654. }
  655. my $prfNew = {};
  656. $prfNew->{NAME} = $name;
  657. $prfNew->{DATA} = $data;
  658. $prfNew->{TOPIC} = $topic;
  659. push @{$hash->{PROFILES}}, $prfNew;
  660. weekprofile_writeProfilesToFile($hash);
  661. return undef;
  662. }
  663. #----------------------------------------------------------
  664. $list.= ' send_to_device' if ($prfCnt > 0);
  665. if ($cmd eq 'send_to_device') {
  666. return 'usage: send_to_device <name> [device(s)]' if(@params < 1);
  667. my ($topic, $name) = weekprofile_splitName($params[0]);
  668. return "Error topics not enabled" if (!$useTopics && ($topic ne 'default'));
  669. my @devices = ();
  670. if (@params == 2) {
  671. @devices = split(',',$params[1]);
  672. } else {
  673. push @devices, $hash->{MASTERDEV}->{NAME} if (defined($hash->{MASTERDEV}));
  674. }
  675. return "Error no devices given and no master device" if (@devices == 0);
  676. my ($found,$idx) = weekprofile_findPRF($hash,$name,$topic,1);
  677. if (!defined($found)) {
  678. Log3 $me, 1, "$me(Set): Error unknown profile $params[0]";
  679. return "Error unknown profile $params[0]";
  680. }
  681. my $err = '';
  682. foreach my $device (@devices){
  683. my $ret = weekprofile_sendDevProfile($device,$found,$me);
  684. if ($ret) {
  685. Log3 $me, 1, "$me(Set): $ret" if ($ret);
  686. $err .= $ret . "\n";
  687. }
  688. }
  689. return $err;
  690. }
  691. #----------------------------------------------------------
  692. $list.= " copy_profile";
  693. if ($cmd eq 'copy_profile') {
  694. return 'usage: copy_profile <source> <target>' if(@params < 2);
  695. my ($srcTopic, $srcName) = weekprofile_splitName($params[0]);
  696. my ($destTopic, $destName) = weekprofile_splitName($params[1]);
  697. return "Error topics not enabled" if (!$useTopics && ( ($srcTopic ne 'default') || ($destTopic ne 'default')) );
  698. my $prfSrc = undef;
  699. my $prfDest = undef;
  700. foreach my $prf (@{$hash->{PROFILES}}){
  701. $prfSrc = $prf if ( ($prf->{NAME} eq $srcName) && ($prf->{TOPIC} eq $srcTopic) );
  702. $prfDest = $prf if ( ($prf->{NAME} eq $destName) && ($prf->{TOPIC} eq $destTopic) );
  703. }
  704. return "Error unknown profile $srcName" unless($prfSrc);
  705. Log3 $me, 4, "$me(Set): override profile $destName" if ($prfDest);
  706. if ($prfDest){
  707. $prfDest->{DATA} = $prfSrc->{DATA};
  708. $prfDest->{REF} = $prfSrc->{REF};
  709. } else {
  710. $prfDest = {};
  711. $prfDest->{NAME} = $destName;
  712. $prfDest->{DATA} = $prfSrc->{DATA};
  713. $prfDest->{TOPIC} = $destTopic;
  714. $prfDest->{REF} = $prfSrc->{REF};
  715. push @{$hash->{PROFILES}}, $prfDest;
  716. }
  717. weekprofile_writeProfilesToFile($hash);
  718. return undef;
  719. }
  720. #----------------------------------------------------------
  721. $list.= " reference_profile" if ($useTopics);
  722. if ($cmd eq 'reference_profile') {
  723. return 'usage: copy_profile <source> <target>' if(@params < 2);
  724. my ($srcTopic, $srcName) = weekprofile_splitName($params[0]);
  725. my ($destTopic, $destName) = weekprofile_splitName($params[1]);
  726. return "Error topics not enabled" if (!$useTopics && ( ($srcTopic ne 'default') || ($destTopic ne 'default')) );
  727. my $prfSrc = undef;
  728. my $prfDest = undef;
  729. foreach my $prf (@{$hash->{PROFILES}}){
  730. $prfSrc = $prf if ( ($prf->{NAME} eq $srcName) && ($prf->{TOPIC} eq $srcTopic) );
  731. $prfDest = $prf if ( ($prf->{NAME} eq $destName) && ($prf->{TOPIC} eq $destTopic) );
  732. }
  733. return "Error unknown profile $srcName" unless($prfSrc);
  734. Log3 $me, 4, "$me(Set): override profile $destName" if ($prfDest);
  735. if ($prfDest){
  736. $prfDest->{DATA} = undef;
  737. $prfDest->{REF} = "$srcTopic:$srcName";
  738. } else {
  739. $prfDest = {};
  740. $prfDest->{NAME} = $destName;
  741. $prfDest->{DATA} = undef;
  742. $prfDest->{TOPIC} = $destTopic;
  743. $prfDest->{REF} = "$srcTopic:$srcName";
  744. push @{$hash->{PROFILES}}, $prfDest;
  745. }
  746. weekprofile_writeProfilesToFile($hash);
  747. return undef;
  748. }
  749. #----------------------------------------------------------
  750. $list.= " remove_profile";
  751. if ($cmd eq 'remove_profile') {
  752. return 'usage: remove_profile <name>' if(@params < 1);
  753. return 'Error master profile can not removed' if( ($params[0] eq "master") && defined($hash->{MASTERDEV}) );
  754. return 'Error Remove last profile is not allowed' if(scalar(@{$hash->{PROFILES}}) == 1);
  755. my ($topic, $name) = weekprofile_splitName($params[0]);
  756. return "Error topics not enabled" if (!$useTopics && ($topic ne 'default'));
  757. my ($delprf,$idx) = weekprofile_findPRF($hash,$name,$topic,0);
  758. return "Error unknown profile $params[0]" unless($delprf);
  759. my $ref = weekprofile_hasREF($hash,$delprf);
  760. return "Error profile $params[0] is referenced from $ref" if ($ref);
  761. splice(@{$hash->{PROFILES}},$idx, 1);
  762. weekprofile_writeProfilesToFile($hash);
  763. return undef;
  764. }
  765. #----------------------------------------------------------
  766. $list.= " restore_topic" if ($useTopics);
  767. if ($cmd eq 'restore_topic') {
  768. return 'usage: restore_topic <name>' if(@params < 1);
  769. my $topic = $params[0];
  770. my $err='';
  771. foreach my $dev (@{$hash->{SNDDEVLIST}}){
  772. my $prfName = AttrVal($dev->{NAME},"weekprofile",undef);
  773. next if (!defined($prfName));
  774. Log3 $me, 5, "$me(Set): found device $dev->{NAME}";
  775. my ($prf,$idx) = weekprofile_findPRF($hash,$prfName,$topic,1);
  776. next if (!defined($prf));
  777. Log3 $me, 4, "$me(Set): Send profile $topic:$prfName to $dev->{NAME}";
  778. my $ret = weekprofile_sendDevProfile($dev->{NAME},$prf,$me);
  779. if ($ret) {
  780. Log3 $me, 1, "$me(Set): $ret" if ($ret);
  781. $err .= $ret . "\n";
  782. }
  783. }
  784. readingsSingleUpdate($hash,"active_topic",$topic,1);
  785. return $err if ($err);
  786. return undef;
  787. }
  788. #----------------------------------------------------------
  789. $list.= " reread_master:noArg" if (defined($hash->{MASTERDEV}));
  790. if ($cmd eq 'reread_master') {
  791. return "Error no master device assigned" if (!defined($hash->{MASTERDEV}));
  792. my $devName = $hash->{MASTERDEV}->{NAME};
  793. Log3 $me, 4, "$me(Set): reread master profile from $devName";
  794. my $prfDev = weekprofile_readDevProfile($hash->{MASTERDEV}->{NAME},$hash->{MASTERDEV}->{TYPE}, $me);
  795. if(defined($prfDev)) {
  796. $hash->{PROFILES}[0]->{DATA} = $prfDev;
  797. weekprofile_updateReadings($hash);
  798. return undef;
  799. } else {
  800. return "Error reading master profile";
  801. }
  802. }
  803. $list =~ s/ $//;
  804. return "Unknown argument $cmd, choose one of $list";
  805. }
  806. ##############################################
  807. sub weekprofile_State($$$$)
  808. {
  809. my ($hash, $time, $name, $val) = @_;
  810. my $me = $hash->{NAME};
  811. #do nothing we do not restore readings from statefile
  812. return undef;
  813. }
  814. ##############################################
  815. sub weekprofile_Notify($$)
  816. {
  817. my ($own, $dev) = @_;
  818. my $me = $own->{NAME}; # own name / hash
  819. my $devName = $dev->{NAME}; # Device that created the events
  820. my $max = int(@{$dev->{CHANGED}}); # number of events / changes
  821. if ($devName eq "global"){
  822. for (my $i = 0; $i < $max; $i++) {
  823. my $s = $dev->{CHANGED}[$i];
  824. next if(!defined($s));
  825. my ($what,$who) = split(' ',$s);
  826. if ($what =~ m/^INITIALIZED$/ || $what =~ m/REREADCFG/) {
  827. delete $own->{PROFILES};
  828. weekprofile_refreshSendDevList($own);
  829. weekprofile_assignDev($own);
  830. weekprofile_readProfilesFromFile($own);
  831. weekprofile_updateReadings($own);
  832. }
  833. if ($what =~ m/DEFINED/ || $what =~ m/^DELETED/) {
  834. weekprofile_refreshSendDevList($own);
  835. }
  836. }
  837. }
  838. if ($init_done && defined($own->{MASTERDEV}) &&
  839. ($own->{MASTERDEV}->{NAME} eq $devName) &&
  840. (@{$own->{PROFILES}} > 0) ) {
  841. my $readprf=0;
  842. for (my $i = 0; $i < $max; $i++) {
  843. my $s = $dev->{CHANGED}[$i];
  844. next if(!defined($s));
  845. my ($what,$who) = split(' ',$s);
  846. Log3 $me, 5, "$me(Notify): $devName, $what";
  847. if ($own->{MASTERDEV}->{NAME} eq 'MAX') {
  848. $readprf =1 if ($what=~m/weekprofile/); #reading weekprofile
  849. } else {
  850. # toDo nur auf spezielle notify bei anderen typen reagieren!!
  851. $readprf = 1;
  852. }
  853. last if ($readprf);
  854. }
  855. if ($readprf) {
  856. Log3 $me, 4, "$me(Notify): reread master profile from $devName";
  857. my $prfDev = weekprofile_readDevProfile($own->{MASTERDEV}->{NAME},$own->{MASTERDEV}->{TYPE}, $me);
  858. if(defined($prfDev)) {
  859. $own->{PROFILES}[0]->{DATA} = $prfDev;
  860. weekprofile_updateReadings($own);
  861. }
  862. }
  863. }
  864. return undef;
  865. }
  866. ##############################################
  867. sub weekprofile_Attr($$$)
  868. {
  869. my ($cmd, $me, $attrName, $attrVal) = @_;
  870. my $hash = $defs{$me};
  871. return if (!defined($attrVal));
  872. Log3 $me, 5, "$me(weekprofile_Attr): $cmd, $attrName, $attrVal";
  873. $attr{$me}{$attrName} = $attrVal;
  874. weekprofile_writeProfilesToFile($hash) if ($attrName eq 'configFile');
  875. if ($attrName eq 'tempON') {
  876. my $tempOFF = myAttrVal($me, "tempOFF", $attrVal);
  877. if ($tempOFF > $attrVal) {
  878. Log3 $me, 2, "$me(weekprofile_Attr): warning: tempON must be bigger than tempOFF";
  879. }
  880. }
  881. if ($attrName eq 'tempOFF') {
  882. my $tempON = myAttrVal($me, "tempON", $attrVal);
  883. if ($tempON < $attrVal) {
  884. Log3 $me, 2, "$me(weekprofile_Attr): warning: tempOFF must be smaller than tempON";
  885. }
  886. }
  887. return undef;
  888. }
  889. ##############################################
  890. sub weekprofile_writeProfilesToFile(@)
  891. {
  892. my ($hash) = @_;
  893. my $me = $hash->{NAME};
  894. if (!defined($hash->{PROFILES})) {
  895. Log3 $me, 4, "$me(writeProfileToFile): no pofiles to save";
  896. return;
  897. }
  898. my $start = (defined($hash->{MASTERDEV})) ? 1:0;
  899. my $prfCnt = scalar(@{$hash->{PROFILES}});
  900. return if ($prfCnt <= $start);
  901. my $filename = "./log/weekprofile-$me.cfg";
  902. $filename = AttrVal($me,"configFile",$filename);
  903. my $ret = open(my $fh, '>', $filename);
  904. if (!$ret){
  905. Log3 $me, 1, "$me(writeProfileToFile): Could not open file '$filename' $!";
  906. return;
  907. }
  908. print $fh "__version__=".$CONFIG_VERSION."\n";
  909. Log3 $me, 5, "$me(writeProfileToFile): write profiles to $filename";
  910. my $json = JSON->new;
  911. for (my $i = $start; $i < $prfCnt; $i++) {
  912. print $fh "entry=".$json->encode($hash->{PROFILES}[$i])."\n";
  913. }
  914. close $fh;
  915. DoTrigger($me,"PROFILES_SAVED",1);
  916. weekprofile_updateReadings($hash);
  917. }
  918. ##############################################
  919. sub weekprofile_readProfilesFromFile(@)
  920. {
  921. my ($hash) = @_;
  922. my $me = $hash->{NAME};
  923. my $useTopics = AttrVal($me,"useTopics",0);
  924. my $filename = "./log/weekprofile-$me.cfg";
  925. $filename = AttrVal($me,"configFile",$filename);
  926. unless (-e $filename) {
  927. Log3 $me, 5, "$me(readProfilesFromFile): file do not exist '$filename'";
  928. return;
  929. }
  930. #my $ret = open(my $fh, '<:encoding(UTF-8)', $filename);
  931. my $ret = open(my $fh, '<', $filename);
  932. if (!$ret){
  933. Log3 $me, 1, "$me(readProfilesFromFile): Could not open file '$filename' $!";
  934. return;
  935. }
  936. Log3 $me, 5, "$me(readProfilesFromFile): read profiles from $filename";
  937. my $json = JSON->new;
  938. my $rowCnt = 0;
  939. my $version = undef;
  940. while (my $row = <$fh>) {
  941. chomp $row;
  942. Log3 $me, 5, "$me(readProfilesFromFile): data row $row";
  943. my @data = split('=',$row);
  944. if(@data<2){
  945. Log3 $me, 1, "$me(readProfilesFromFile): incorrect data row";
  946. next;
  947. }
  948. if ($rowCnt == 0 && $data[0]=~/__version__/) {
  949. $version=$data[1] * 1;
  950. Log3 $me, 5, "$me(readProfilesFromFile): detect version $version";
  951. next;
  952. }
  953. if (!$version || $version < 1.1) {
  954. my $prfData=undef;
  955. eval { $prfData = $json->decode($data[1]); };
  956. if (!defined($prfData)) {
  957. Log3 $me, 1, "$me(readProfilesFromFile): Error parsing profile data $data[1]";
  958. next;
  959. };
  960. my $prfNew = {};
  961. $prfNew->{NAME} = $data[0];
  962. $prfNew->{DATA} = $prfData;
  963. $prfNew->{TOPIC} = 'default';
  964. if (!$hash->{MASTERDEV} && $rowCnt == 0) {
  965. $hash->{PROFILES}[0] = $prfNew; # replace default
  966. } else {
  967. push @{$hash->{PROFILES}}, $prfNew;
  968. }
  969. $rowCnt++;
  970. } #----------------------------------------------------- 1.1
  971. elsif ($version = 1.1) {
  972. my $prfNew=undef;
  973. eval { $prfNew = $json->decode($data[1]); };
  974. if (!defined($prfNew)) {
  975. Log3 $me, 1, "$me(readProfilesFromFile): Error parsing profile data $data[1]";
  976. next;
  977. };
  978. next if (!$useTopics && ($prfNew->{TOPIC} ne 'default')); # remove topics!!
  979. if (!$hash->{MASTERDEV} && $rowCnt == 0) {
  980. $hash->{PROFILES}[0] = $prfNew; # replace default
  981. } else {
  982. push @{$hash->{PROFILES}}, $prfNew;
  983. }
  984. $rowCnt++;
  985. } else {
  986. Log3 $me, 1, "$me(readProfilesFromFile): Error unknown version $version";
  987. close $fh;
  988. return;
  989. }
  990. }
  991. close $fh;
  992. }
  993. ##############################################
  994. sub weekprofile_SummaryFn()
  995. {
  996. my ($FW_wname, $d, $room, $extPage) = @_;
  997. my $hash = $defs{$d};
  998. my $show_links = 1;
  999. $show_links = 0 if($FW_hiddenroom{detail});
  1000. my $html;
  1001. my $iconName = AttrVal($d, "icon", "edit_settings");
  1002. my $editNewpage = AttrVal($d, "widgetEditOnNewPage", 0);
  1003. my $useTopics = AttrVal($d, "useTopics", 0);
  1004. my $editDaysInRow = AttrVal($d, "widgetEditDaysInRow", undef);
  1005. my $tempON = AttrVal($d, "tempON", undef);
  1006. my $tempOFF = AttrVal($d, "tempOFF", undef);
  1007. my $editIcon = FW_iconName($iconName) ? FW_makeImage($iconName,$iconName,"icon") : "";
  1008. $editIcon = "<a name=\"$d.edit\" onclick=\"weekprofile_DoEditWeek('$d','$editNewpage')\" href=\"javascript:void(0)\">$editIcon</a>";
  1009. my $lnkDetails = AttrVal($d, "alias", $d);
  1010. $lnkDetails = "<a name=\"$d.detail\" href=\"$FW_ME$FW_subdir?detail=$d\">$lnkDetails</a>" if($show_links);
  1011. my $masterDev = defined($hash->{MASTERDEV}) ? $hash->{MASTERDEV}->{NAME} : undef;
  1012. my $args = "weekprofile,MODE:SHOW";
  1013. $args .= ",USETOPICS:$useTopics";
  1014. $args .= ",MASTERDEV:$masterDev" if (defined($masterDev));
  1015. $args .= ",DAYINROW:$editDaysInRow" if (defined($editDaysInRow));
  1016. $args .= ",TEMP_ON:$tempON" if (defined($tempON));
  1017. $args .= ",TEMP_OFF:$tempOFF" if (defined($tempOFF));
  1018. my $curr = "";
  1019. if (@{$hash->{PROFILES}} > 0)
  1020. {
  1021. $curr = "$hash->{PROFILES}[0]->{TOPIC}:$hash->{PROFILES}[0]->{NAME}";
  1022. my $currTopic = ReadingsVal($d, "active_topic", undef);
  1023. if ($currTopic) {
  1024. foreach my $prf (@{$hash->{PROFILES}}){
  1025. if ($prf->{TOPIC} eq $currTopic){
  1026. $curr = "$prf->{TOPIC}:$prf->{NAME}";
  1027. last;
  1028. }
  1029. }
  1030. }
  1031. }
  1032. $html .= "<table>";
  1033. $html .= "<tr><td>";
  1034. $html .= "<div class=\"devType\" id=\"weekprofile.$d.header\">";
  1035. $html .= "<table style=\"padding:0\"><tr><td style=\"padding-right:0;padding-bottom:0\"><div id=\"weekprofile.menu.base\">";
  1036. $html .= $editIcon."&nbsp;".$lnkDetails;
  1037. $html .= "</div></td></tr></table></div></td></tr>";
  1038. $html .= "<tr><td>";
  1039. $html .= "<div class=\"fhemWidget\" informId=\"$d\" cmd=\"\" arg=\"$args\" current=\"$curr\" dev=\"$d\">"; # div tag to support inform updates
  1040. $html .= "</div>";
  1041. $html .= "</td></tr>";
  1042. $html .= "</table>";
  1043. return $html;
  1044. }
  1045. ##############################################
  1046. sub weekprofile_editOnNewpage(@)
  1047. {
  1048. my ($device, $prf, $daysInRow) = @_;
  1049. my $hash = $defs{$device};
  1050. my $editDaysInRow = AttrVal($device, "widgetEditDaysInRow", undef);
  1051. $editDaysInRow = $daysInRow if (defined($daysInRow));
  1052. my $args = "weekprofile,MODE:EDIT,JMPBACK:1";
  1053. $args .= ",DAYINROW:$editDaysInRow" if (defined($editDaysInRow));
  1054. my $html;
  1055. $html .= "<html>";
  1056. $html .= "<table>";
  1057. $html .= "<tr><td>";
  1058. $html .= "<div class=\"devType\" id=\"weekprofile.$device.header\">";
  1059. $html .= "<div class=\"devType\" id=\"weekprofile.menu.base\">";
  1060. $html .= "</di></div></td></tr>";
  1061. $html .= "<tr><td>";
  1062. $html .= "<div class=\"fhemWidget\" informId=\"$device\" cmd=\"\" arg=\"$args\" current=\"$prf\" dev=\"$device\">"; # div tag to support inform updates
  1063. $html .= "</div>";
  1064. $html .= "</td></tr>";
  1065. $html .= "</table>";
  1066. $html .= "</html>";
  1067. return $html;
  1068. }
  1069. ##############################################
  1070. #search device weekprofile from a assoziated master device
  1071. sub weekprofile_findPRFDev($)
  1072. {
  1073. my ($device) = @_;
  1074. foreach my $d (keys %defs)
  1075. {
  1076. my $module = $defs{$d}{TYPE};
  1077. next if ("$module" ne "weekprofile");
  1078. next if (!defined($defs{$d}->{MASTERDEV}));
  1079. my $masterDev = $defs{$d}->{MASTERDEV}->{NAME};
  1080. next unless(defined($masterDev));
  1081. next if ($masterDev ne $device);
  1082. return $defs{$d}{NAME};
  1083. }
  1084. return undef;
  1085. }
  1086. ##############################################
  1087. # get a web link to edit a profile from weekprofile from a assoziated master device
  1088. sub weekprofile_getEditLNK_MasterDev($$)
  1089. {
  1090. my ($aszDev, $prf) = @_;
  1091. my $device = weekprofile_findPRFDev($aszDev);
  1092. return "" if (!defined($device));
  1093. my $iconName = AttrVal($device, "icon", "edit_settings");
  1094. my $editIcon = FW_iconName($iconName) ? FW_makeImage($iconName,$iconName,"icon") : "";
  1095. my $script = '<script type="text/javascript">';
  1096. $script.= "function jump_edit_weekprofile_$aszDev() {";
  1097. $script.= "window.location.assign('$FW_ME?cmd={weekprofile_editOnNewpage(";
  1098. $script.= "\"$device\",\"$prf\");;}')};";
  1099. $script.= "</script>";
  1100. my $lnk = "$script<a onclick=\"jump_edit_weekprofile_$aszDev()\" href=\"javascript:void(0)\">$editIcon</a>";
  1101. return ($lnk,0);
  1102. }
  1103. 1;
  1104. =pod
  1105. =item summary administration of weekprofiles
  1106. =item summary_DE Verwaltung von Wochenprofilen
  1107. =item helper
  1108. =begin html
  1109. <a name="weekprofile"></a>
  1110. <h3>weekprofile</h3>
  1111. <ul>
  1112. With this module you can manage and edit different weekprofiles. You can send the profiles to different devices.<br>
  1113. Currently the following devices will by supported:<br>
  1114. <li>MAX</li>
  1115. <li>other weekprofile modules</li>
  1116. <li>Homatic channel _Clima or _Climate</li>
  1117. In the normal case the module is assoziated with a master device.
  1118. So a profile 'master' will be created automatically. This profile corrensponds to the current active
  1119. profile on the master device.
  1120. You can also use this module without a master device. In this case a default profile will be created.
  1121. <br>
  1122. An other use case is the usage of categories 'Topics'.
  1123. To enable the feature the attribute 'useTopics' have to be set.
  1124. Topics are e.q. winter, summer, holidays, party, and so on.
  1125. A topic consists of different week profiles. Normally one profile for each thermostat.
  1126. The connection between the thermostats and the profile is an user attribute 'weekprofile' without the topic name.
  1127. With 'restore_topic' the defined profile in the attribute will be transfered to the thermostat.
  1128. So it is possible to change the topic easily and all thermostats will be updated with the correndponding profile.
  1129. <br><br>
  1130. <b>Attention:</b>
  1131. To transfer a profile to a device it needs a lot of Credits.
  1132. This is not taken into account from this module. So it could be happend that the profile in the module
  1133. and on the device are not equal until the whole profile is transfered completly.
  1134. <br>
  1135. If the maste device is Homatic HM-TC-IT-WM-W-EU then only the first profile (R_P1_...) will be used!
  1136. <br>
  1137. <b>For this module libjson-perl have to be installed</b>
  1138. <br><br>
  1139. <b>Events:</b><br>
  1140. Currently the following event will be created:<br>
  1141. <li>PROFILE_TRANSFERED: if a profile or a part of a profile (changes) is send to a device</li>
  1142. <li>PROFILES_SAVED: the profile are stored in the config file (also if there are no changes)</li>
  1143. <a name="weekprofiledefine"></a>
  1144. <b>Define</b>
  1145. <ul>
  1146. <code>define &lt;name&gt; weekprofile [master device]</code><br>
  1147. <br>
  1148. Activate the module with or without an assoziated master device. The master device is an optional parameter.
  1149. With a master device a spezial profile 'master' will be created.<br>
  1150. Special master' profile handling:<br>
  1151. <li>Can't be deleted</li>
  1152. <li>Will be automatically transfered to the master device if it was modified</li>
  1153. <li>Will not be saved</li>
  1154. <br>
  1155. Without a master device a 'default' profile will be created
  1156. </ul>
  1157. <a name="weekprofileset"></a>
  1158. <b>Set</b>
  1159. <ul>
  1160. <li>profile_data<br>
  1161. <code>set &lt;name&gt; profile_data &lt;profilename&gt; &lt;json data&gt; </code><br>
  1162. The profile 'profilename' will be changed. The data have to be in json format.
  1163. </li>
  1164. <li>send_to_device<br>
  1165. <code>set &lt;name&gt; send_to_device &lt;profilename&gt; [devices] </code><br>
  1166. The profile 'profilename' will be transfered to one or more the devices. Without the parameter device the profile
  1167. will be transferd to the master device. 'devices' is a comma seperated list of device names
  1168. </li>
  1169. <li>copy_profile<br>
  1170. <code>set &lt;name&gt; copy_profile &lt;source&gt; &lt;destination&gt; </code><br>
  1171. Copy from source to destination. The destination will be overwritten
  1172. </li>
  1173. <li>remove_profile<br>
  1174. <code>set &lt;name&gt; remove_profile &lt;profilename&gt; </code><br>
  1175. Delete profile 'profilename'.
  1176. </li>
  1177. <li>reference_profile<br>
  1178. <code>set &lt;name&gt; reference_profile &lt;source&gt; &lt;destination&gt; </code><br>
  1179. Create a reference from destination to source. The destination will be overwritten if it exits.
  1180. </li>
  1181. <li>restore_topic<br>
  1182. <code>set &lt;name&gt; restore_topic &lt;topic&gt;</code><br>
  1183. All weekprofiles from the topic will be transfered to the correcponding devices.
  1184. Therefore a user attribute 'weekprofile' with the weekprofile name <b>without the topic name</b> have to exist in the device.
  1185. </li>
  1186. <li>reread_master<br>
  1187. Refresh (reread) the master profile from the master device.
  1188. </li>
  1189. </ul>
  1190. <a name="weekprofileget"></a>
  1191. <b>Get</b>
  1192. <ul>
  1193. <li>profile_data<br>
  1194. <code>get &lt;name&gt; profile_data &lt;profilename&gt; </code><br>
  1195. Get the profile data from 'profilename' in json-Format
  1196. </li>
  1197. <li>profile_names<br>
  1198. <code>set &lt;name&gt; profile_names [topicname]</code><br>
  1199. Get a comma seperated list of weekprofile profile names from the topic 'topicname'
  1200. If topicname is not set, 'default' will be used
  1201. If topicname is '*', all weekprofile profile names are returned.
  1202. </li>
  1203. <li>profile_references [name]<br>
  1204. If name is '*', a comma seperated list of all references in the following syntax
  1205. <code>ref_topic:ref_profile>dest_topic:dest_profile</code>
  1206. are returned
  1207. If name is 'topicname:profilename', '0' or the reference name is returned.
  1208. </li>
  1209. <li>topic_names<br>
  1210. Return a comma seperated list of topic names.
  1211. </li>
  1212. </ul>
  1213. <a name="weekprofilereadings"></a>
  1214. <p><b>Readings</b></p>
  1215. <ul>
  1216. <li>active_topic<br>
  1217. Active\last restored topic name
  1218. </li>
  1219. <li>profile_count<br>
  1220. Count of all profiles including references.
  1221. </li>
  1222. </ul>
  1223. <a name="weekprofileattr"></a>
  1224. <b>Attributes</b>
  1225. <ul>
  1226. <li>widgetTranslations<br>
  1227. Comma seperated list of texts translations <german>:<translation>
  1228. <code>attr name widgetTranslations Abbrechen:Cancel,Speichern:Save</code>
  1229. </li>
  1230. <li>widgetWeekdays<br>
  1231. Comma seperated list of week days starting at Monday
  1232. <code>attr name widgetWeekdays Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday</code>
  1233. </li>
  1234. <li>widgetEditOnNewPage<br>
  1235. Editing the profile on a new html page if it is set to '1'
  1236. </li>
  1237. <li>widgetEditDaysInRow<br>
  1238. Count of visible days in on row during Edit. Default 2.<br>
  1239. </li>
  1240. <li>configFile<br>
  1241. Path and filename of the configuration file where the profiles will be stored
  1242. Default: ./log/weekprofile-<name>.cfg
  1243. </li>
  1244. <li>icon<br>
  1245. icon for edit<br>
  1246. Default: edit_settings
  1247. </li>
  1248. <li>useTopics<br>
  1249. Enable topics.<br>
  1250. Default: 0
  1251. </li>
  1252. <li>tempON<br>
  1253. Temperature for 'on'. e.g. 30
  1254. </li>
  1255. <li>tempOFF<br>
  1256. Temperature for 'off'. e.g. 4
  1257. </li>
  1258. </ul>
  1259. </ul>
  1260. =end html
  1261. =begin html_DE
  1262. <a name="weekprofile"></a>
  1263. <h3>weekprofile</h3>
  1264. <ul>
  1265. Beschreibung im Wiki: http://www.fhemwiki.de/wiki/Weekprofile
  1266. Mit dem Modul 'weekprofile' können mehrere Wochenprofile verwaltet und an unterschiedliche Geräte
  1267. übertragen werden. Aktuell wird folgende Hardware unterstützt:
  1268. <li>alle MAX Thermostate</li>
  1269. <li>andere weekprofile Module</li>
  1270. <li>Homatic (Kanal _Clima bzw. _Climate)</li>
  1271. Im Standardfall wird das Modul mit einem Geräte = 'Master-Gerät' assoziiert,
  1272. um das Wochenprofil vom Gerät grafisch bearbeiten zu können und andere Profile auf das Gerät zu übertragen.
  1273. Wird kein 'Master-Gerät' angegeben, wird erstmalig ein Default-Profil angelegt.
  1274. <br>
  1275. Ein weiterer Anwendungsfall ist die Verwendung von Rubriken\Kategorien 'Topics'.
  1276. Hier sollte kein 'Master-Gerät' angegeben werden. Dieses Feature muss erst über das Attribut 'useTopics' aktiviert werden.
  1277. Topics sind z.B. Winter, Sommer, Urlaub, Party, etc.
  1278. Innerhalb einer Topic kann es mehrere Wochenprofile geben. Sinnvollerweise sollten es soviele wie Thermostate sein.
  1279. Über ein Userattribut 'weekprofile' im Thermostat wird ein Wochenprofile ohne Topicname angegeben.
  1280. Mittels 'restore_topic' wird dann das angebene Wochenprofil der Topic an das Thermostat übertragen.
  1281. Somit kann man einfach zwischen den Topics wechseln und die Thermostate bekommen das passende Wochenprofil.
  1282. <br><br>
  1283. <b>Achtung:</b> Das Übertragen von Wochenprofilen erfordet eine Menge an Credits.
  1284. Dies wird vom Modul nicht berücksichtigt. So kann es sein, dass nach dem
  1285. Setzen\Aktualisieren eines Profils das Profil im Modul nicht mit dem Profil im Gerät
  1286. übereinstimmt solange das komplette Profil übertragen wurde.
  1287. <br>
  1288. Beim Homatic HM-TC-IT-WM-W-EU wird nur das 1. Profil (R_P1_...) genommen!
  1289. <br>
  1290. <b>Für das Module wird libjson-perl benötigt</b>
  1291. <br><br>
  1292. <b>Events:</b><br>
  1293. Aktuell werden folgende Events erzeugt:<br>
  1294. <li>PROFILE_TRANSFERED: wenn ein Profil oder Teile davon zu einem Gerät gesended wurden</li>
  1295. <li>PROFILES_SAVED: wenn Profile in die Konfigurationsdatei gespeichert wurden (auch wenn es keine Änderung gab!)</li>
  1296. <a name="weekprofiledefine"></a>
  1297. <b>Define</b>
  1298. <ul>
  1299. <code>define &lt;name&gt; weekprofile [master device]</code><br>
  1300. <br>
  1301. Aktiviert das Modul. Bei der Angabe eines 'Master-Gerätes' wird das Profil 'master'
  1302. entprechende dem Wochenrofil vom Gerät angelegt.
  1303. Sonderbehandlung des 'master' Profils:
  1304. <li>Kann nicht gelöscht werden</li>
  1305. <li>Bei Ändern\Setzen des Proils wird es automatisch an das 'Master-Geräte' gesendet</li>
  1306. <li>Es wird sind mit abgespeicht</li>
  1307. <br>
  1308. Wird kein 'Master-Geräte' angegeben, wird ein 'default' Profil angelegt.
  1309. </ul>
  1310. <a name="weekprofileset"></a>
  1311. <b>Set</b>
  1312. <ul>
  1313. <li>profile_data<br>
  1314. <code>set &lt;name&gt; profile_data &lt;profilname&gt; &lt;json data&gt; </code><br>
  1315. Es wird das Profil 'profilname' geändert. Die Profildaten müssen im json-Format übergeben werden.
  1316. </li>
  1317. <li>send_to_device<br>
  1318. <code>set &lt;name&gt; send_to_device &lt;profilname&gt; [devices] </code><br>
  1319. Das Profil wird an ein oder mehrere Geräte übertragen. Wird kein Gerät angegeben, wird das 'Master-Gerät' verwendet.
  1320. 'Devices' ist eine kommagetrennte Auflistung von Geräten
  1321. </li>
  1322. <li>copy_profile<br>
  1323. <code>set &lt;name&gt; copy_profile &lt;quelle&gt; &lt;ziel&gt; </code><br>
  1324. Kopiert das Profil 'quelle' auf 'ziel'. 'ziel' wird überschrieben oder neu angelegt.
  1325. </li>
  1326. <li>remove_profile<br>
  1327. <code>set &lt;name&gt; remove_profile &lt;profilname&gt; </code><br>
  1328. Das Profil 'profilname' wird gelöscht.
  1329. </li>
  1330. <li>reference_profile<br>
  1331. <code>set &lt;name&gt; reference_profile &lt;quelle&gt; &lt;ziel&gt; </code><br>
  1332. Referenziert das Profil 'ziel'auf 'quelle'. 'ziel' wird überschrieben oder neu angelegt.
  1333. </li>
  1334. <li>restore_topic<br>
  1335. <code>set &lt;name&gt; restore_topic &lt;topic&gt;</code><br>
  1336. Alle Wochenpläne in der Topic werden zu den entsprechenden Geräten übertragen.
  1337. Dazu muss im Gerät ein Userattribut 'weekprofile' mit dem Namen des Wochenplans <b>ohne</b> Topic gesetzt sein.
  1338. </li>
  1339. <li>reread_master<br>
  1340. Aktualisiert das master profile indem das 'Master-Geräte' neu ausgelesen wird.
  1341. </li>
  1342. </ul>
  1343. <a name="weekprofileget"></a>
  1344. <b>Get</b>
  1345. <ul>
  1346. <li>profile_data<br>
  1347. <code>get &lt;name&gt; profile_data &lt;profilname&gt; </code><br>
  1348. Liefert die Profildaten von 'profilname' im json-Format
  1349. </li>
  1350. <li>profile_names<br>
  1351. <code>set &lt;name&gt; profile_names [topic_name]</code><br>
  1352. Liefert alle Profilnamen getrennt durch ',' einer Topic 'topic_name'
  1353. Ist 'topic_name' gleich '*' werden alle Profilnamen zurück gegeben.
  1354. </li>
  1355. <li>profile_references [name]<br>
  1356. Liefert eine Liste von Referenzen der Form <br>
  1357. <code>
  1358. ref_topic:ref_profile>dest_topic:dest_profile
  1359. </code>
  1360. Ist name 'topicname:profilename' wird '0' der Name der Referenz zurück gegeben.
  1361. </li>
  1362. </ul>
  1363. <a name="weekprofilereadings"></a>
  1364. <p><b>Readings</b></p>
  1365. <ul>
  1366. <li>active_topic<br>
  1367. Aktive\zuletzt gesetzter Topicname.
  1368. </li>
  1369. <li>profile_count<br>
  1370. Anzahl aller Profile mit Referenzen.
  1371. </li>
  1372. </ul>
  1373. <a name="weekprofileattr"></a>
  1374. <b>Attribute</b>
  1375. <ul>
  1376. <li>widgetTranslations<br>
  1377. Liste von Übersetzungen der Form <german>:<Übersetzung> getrennt durch ',' um Texte im Widget zu übersetzen.
  1378. <code>attr name widgetTranslations Abbrechen:Abbr,Speichern:Save</code>
  1379. </li>
  1380. <li>widgetWeekdays<br>
  1381. Liste von Wochentagen getrennt durch ',' welche im Widget angzeigt werden.
  1382. Beginnend bei Montag. z.B.
  1383. <code>attr name widgetWeekdays Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag,Sonntag</code>
  1384. </li>
  1385. <li>widgetEditDaysInRow<br>
  1386. Anzahl in der in einer Reihe dargestellten Tage während der Bearbeitung. Default 2.<br>
  1387. </li>
  1388. <li>widgetEditOnNewPage<br>
  1389. Wenn gesetzt ('1'), dann wird die Bearbeitung auf einer separaten\neuen Webseite gestartet.
  1390. </li>
  1391. <li>configFile<br>
  1392. Pfad und Dateiname wo die Profile gespeichert werden sollen.
  1393. Default: ./log/weekprofile-<name>.cfg
  1394. </li>
  1395. <li>icon<br>
  1396. Änders des Icons zum Bearbeiten
  1397. Default: edit_settings
  1398. </li>
  1399. <li>useTopics<br>
  1400. Verwendung von Topic aktivieren.
  1401. </li>
  1402. <li>tempON<br>
  1403. Temperature für 'on'. z.B. 30
  1404. </li>
  1405. <li>tempOFF<br>
  1406. Temperature für 'off'. z.B. 4
  1407. </li>
  1408. </ul>
  1409. </ul>
  1410. =end html_DE
  1411. =cut