59_Twilight.pm 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019
  1. # $Id: 59_Twilight.pm 14039 2017-04-19 19:59:56Z orti-otto $
  2. ##############################################################################
  3. #
  4. # 59_Twilight.pm
  5. # Copyright by Sebastian Stuecker
  6. # erweitert von Dietmar Ortmann
  7. #
  8. # used algorithm see: http://lexikon.astronomie.info/zeitgleichung/
  9. #
  10. # Sun position computing
  11. # Copyright (C) 2013 Julian Pawlowski, julian.pawlowski AT gmail DOT com
  12. # based on Twilight.tcl http://www.homematic-wiki.info/mw/index.php/TCLScript:twilight
  13. # With contribution from http://www.ip-symcon.de/forum/threads/14925-Sonnenstand-berechnen-(Azimut-amp-Elevation)
  14. #
  15. # e-mail: omega at online dot de
  16. #
  17. # This file is part of fhem.
  18. #
  19. # Fhem is free software: you can redistribute it and/or modify
  20. # it under the terms of the GNU General Public License as published by
  21. # the Free Software Foundation, either version 2 of the License, or
  22. # (at your option) any later version.
  23. #
  24. # Fhem is distributed in the hope that it will be useful,
  25. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  27. # GNU General Public License for more details.
  28. #
  29. # You should have received a copy of the GNU General Public License
  30. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  31. #
  32. ##############################################################################
  33. package main;
  34. use strict;
  35. use warnings;
  36. use POSIX;
  37. use HttpUtils;
  38. use Math::Trig;
  39. use Time::Local 'timelocal_nocheck';
  40. sub Twilight_calc($$);
  41. sub Twilight_my_gmt_offset();
  42. sub Twilight_midnight_seconds($);
  43. ################################################################################
  44. sub Twilight_Initialize($)
  45. {
  46. my ($hash) = @_;
  47. # Consumer
  48. $hash->{DefFn} = "Twilight_Define";
  49. $hash->{UndefFn} = "Twilight_Undef";
  50. $hash->{GetFn} = "Twilight_Get";
  51. $hash->{AttrList}= "$readingFnAttributes " ."useExtWeather";
  52. return undef;
  53. }
  54. ################################################################################
  55. sub Twilight_Get($@)
  56. {
  57. my ($hash, @a) = @_;
  58. return "argument is missing" if(int(@a) != 2);
  59. my $reading= $a[1];
  60. my $value;
  61. if(defined($hash->{READINGS}{$reading})) {
  62. $value= $hash->{READINGS}{$reading}{VAL};
  63. } else {
  64. return "no such reading: $reading";
  65. }
  66. return "$a[0] $reading => $value";
  67. }
  68. ################################################################################
  69. sub Twilight_Define($$)
  70. {
  71. my ($hash, $def) = @_;
  72. my @a = split("[ \t][ \t]*", $def);
  73. return "syntax: define <name> Twilight <latitude> <longitude> [indoor_horizon [Weather]]"
  74. if(int(@a) < 4 && int(@a) > 6);
  75. $hash->{STATE} = "0";
  76. my $latitude;
  77. my $longitude;
  78. my $name = $a[0];
  79. if ($a[2] =~ /^[\+-]*[0-9]*\.*[0-9]*$/ && $a[2] !~ /^[\. ]*$/ ) {
  80. $latitude = $a[2];
  81. if($latitude > 90){$latitude = 90;}
  82. if($latitude < -90){$latitude = -90;}
  83. }else{
  84. return "Argument Latitude is not a valid number";
  85. }
  86. if ($a[3] =~ /^[\+-]*[0-9]*\.*[0-9]*$/ && $a[3] !~ /^[\. ]*$/ ) {
  87. $longitude = $a[3];
  88. if($longitude > 180){$longitude = 180;}
  89. if($longitude < -180){$longitude = -180;}
  90. }else{
  91. return "Argument Longitude is not a valid number";
  92. }
  93. my $weather = 0;
  94. my $indoor_horizon = 0;
  95. if(int(@a)>5) { $weather=$a[5] }
  96. if(int(@a)>4) { if ($a[4] =~ /^[\+-]*[0-9]*\.*[0-9]*$/ && $a[4] !~ /^[\. ]*$/ ) {
  97. $indoor_horizon = $a[4];
  98. if($indoor_horizon > 20) { $indoor_horizon=20;}
  99. # minimal indoor_horizon makes values like civil_sunset and civil_sunrise
  100. if($indoor_horizon < -6) { $indoor_horizon= -6;}
  101. }else{
  102. return "Argument Indoor_Horizon is not a valid number";}
  103. }
  104. $hash->{WEATHER_HORIZON} = 0;
  105. $hash->{INDOOR_HORIZON} = $indoor_horizon;
  106. $hash->{LATITUDE} = $latitude;
  107. $hash->{LONGITUDE} = $longitude;
  108. $hash->{WEATHER} = $weather;
  109. $hash->{VERSUCHE} = 0;
  110. $hash->{DEFINE} = 1;
  111. $hash->{CONDITION} = 50;
  112. $hash->{SUNPOS_OFFSET} = 5*60;
  113. $attr{$name}{verbose} = 4 if ($name =~ /^tst.*$/ );
  114. my $mHash = { HASH=>$hash };
  115. Twilight_sunpos($mHash);
  116. Twilight_Midnight($mHash);
  117. delete $hash->{DEFINE};
  118. return undef;
  119. }
  120. ################################################################################
  121. sub Twilight_Undef($$) {
  122. my ($hash, $arg) = @_;
  123. foreach my $key (keys %{$hash->{TW}}) {
  124. myRemoveInternalTimer($key, $hash);
  125. }
  126. myRemoveInternalTimer ("Midnight", $hash);
  127. myRemoveInternalTimer ("weather", $hash);
  128. myRemoveInternalTimer ("sunpos", $hash);
  129. return undef;
  130. }
  131. ################################################################################
  132. sub myInternalTimer($$$$$) {
  133. my ($modifier, $tim, $callback, $hash, $waitIfInitNotDone) = @_;
  134. my $timerName = "$hash->{NAME}_$modifier";
  135. my $mHash = { HASH=>$hash, NAME=>"$hash->{NAME}_$modifier", MODIFIER=>$modifier};
  136. if (defined($hash->{TIMER}{$timerName})) {
  137. Log3 $hash, 1, "[$hash->{NAME}] possible overwriting of timer $timerName - please delete first";
  138. stacktrace();
  139. } else {
  140. $hash->{TIMER}{$timerName} = $mHash;
  141. }
  142. Log3 $hash, 5, "[$hash->{NAME}] setting Timer: $timerName " . FmtDateTime($tim);
  143. InternalTimer($tim, $callback, $mHash, $waitIfInitNotDone);
  144. return $mHash;
  145. }
  146. ################################################################################
  147. sub myRemoveInternalTimer($$) {
  148. my ($modifier, $hash) = @_;
  149. my $timerName = "$hash->{NAME}_$modifier";
  150. my $myHash = $hash->{TIMER}{$timerName};
  151. if (defined($myHash)) {
  152. delete $hash->{TIMER}{$timerName};
  153. Log3 $hash, 5, "[$hash->{NAME}] removing Timer: $timerName";
  154. RemoveInternalTimer($myHash);
  155. }
  156. }
  157. ################################################################################
  158. #sub myRemoveInternalTimerByName($){
  159. # my ($name) = @_;
  160. # foreach my $a (keys %intAt) {
  161. # my $nam = "";
  162. # my $arg = $intAt{$a}{ARG};
  163. # if (ref($arg) eq "HASH" && defined($arg->{NAME}) ) {
  164. # $nam = $arg->{NAME} if (ref($arg) eq "HASH" && defined($arg->{NAME}) );
  165. # }
  166. # delete($intAt{$a}) if($nam =~ m/^$name/g);
  167. # }
  168. #}
  169. ################################################################################
  170. sub myGetHashIndirekt ($$) {
  171. my ($myHash, $function) = @_;
  172. if (!defined($myHash->{HASH})) {
  173. Log 3, "[$function] myHash not valid";
  174. return undef;
  175. };
  176. return $myHash->{HASH};
  177. }
  178. ################################################################################
  179. sub Twilight_midnight_seconds($) {
  180. my ($now) = @_;
  181. my @time = localtime($now);
  182. my $secs = ($time[2] * 3600) + ($time[1] * 60) + $time[0];
  183. return $secs;
  184. }
  185. ################################################################################
  186. #sub Twilight_ssTimeAsEpoch($) {
  187. # my ($zeit) = @_;
  188. # my ($hour, $min, $sec) = split(":",$zeit);
  189. #
  190. # my $days=0;
  191. # if ($hour>=24) {$days = 1; $hour -=24};
  192. #
  193. # my @jetzt_arr = localtime(time());
  194. # #Stunden Minuten Sekunden
  195. # $jetzt_arr[2] = $hour; $jetzt_arr[1] = $min; $jetzt_arr[0] = $sec;
  196. # $jetzt_arr[3] += $days;
  197. # my $next = timelocal_nocheck(@jetzt_arr);
  198. #
  199. # return $next;
  200. #}
  201. ################################################################################
  202. sub Twilight_calc($$) {
  203. my ($deg, $idx) = @_;
  204. my $midnight = time() - Twilight_midnight_seconds(time());
  205. my $sr = sunrise_abs("Horizon=$deg");
  206. my $ss = sunset_abs ("Horizon=$deg");
  207. my ($srhour, $srmin, $srsec) = split(":",$sr); $srhour -= 24 if($srhour>=24);
  208. my ($sshour, $ssmin, $sssec) = split(":",$ss); $sshour -= 24 if($sshour>=24);
  209. my $sr1 = $midnight + 3600*$srhour+60*$srmin+$srsec;
  210. my $ss1 = $midnight + 3600*$sshour+60*$ssmin+$sssec;
  211. return (0,0) if (abs ($sr1 - $ss1) < 30);
  212. #return Twilight_ssTimeAsEpoch($sr) + 0.01*$idx,
  213. # Twilight_ssTimeAsEpoch($ss) - 0.01*$idx;
  214. return ($sr1 + 0.01*$idx), ($ss1 - 0.01*$idx);
  215. }
  216. ################################################################################
  217. sub Twilight_TwilightTimes(@) {
  218. my ($hash, $whitchTimes, $xml) = @_;
  219. my $Name = $hash->{NAME};
  220. my $horizon = $hash->{HORIZON};
  221. my $swip = $hash->{SWIP} ;
  222. my $lat = $attr{global}{latitude}; $attr{global}{latitude} = $hash->{LATITUDE};
  223. my $long = $attr{global}{longitude}; $attr{global}{longitude} = $hash->{LONGITUDE};
  224. # ------------------------------------------------------------------------------
  225. my $idx = -1;
  226. my @horizons = ("_astro:-18", "_naut:-12", "_civil:-6",":0", "_indoor:$hash->{INDOOR_HORIZON}", "_weather:$hash->{WEATHER_HORIZON}");
  227. foreach my $horizon (@horizons) {
  228. $idx++; next if ($whitchTimes eq "weather" && !($horizon =~ m/weather/) );
  229. my ($name, $deg) = split(":", $horizon);
  230. my $sr = "sr$name"; my $ss = "ss$name";
  231. $hash->{TW}{$sr}{NAME} = $sr; $hash->{TW}{$ss}{NAME} = $ss;
  232. $hash->{TW}{$sr}{DEG} = $deg; $hash->{TW}{$ss}{DEG} = $deg;
  233. $hash->{TW}{$sr}{LIGHT} = $idx+1;$hash->{TW}{$ss}{LIGHT} = $idx;
  234. $hash->{TW}{$sr}{STATE} = $idx+1;$hash->{TW}{$ss}{STATE} = 12 - $idx;
  235. $hash->{TW}{$sr}{SWIP} = $swip; $hash->{TW}{$ss}{SWIP} = $swip;
  236. ($hash->{TW}{$sr}{TIME}, $hash->{TW}{$ss}{TIME}) = Twilight_calc ($deg, $idx);
  237. if ($hash->{TW}{$sr}{TIME} == 0) {
  238. Log3 $hash, 4, "[$Name] hint: $hash->{TW}{$sr}{NAME}, $hash->{TW}{$ss}{NAME} are not defined(HORIZON=$deg)";
  239. }
  240. }
  241. $attr{global}{latitude} = $lat;
  242. $attr{global}{longitude} = $long;
  243. # ------------------------------------------------------------------------------
  244. readingsBeginUpdate ($hash);
  245. foreach my $ereignis (keys %{$hash->{TW}}) {
  246. next if ($whitchTimes eq "weather" && !($ereignis =~ m/weather/) );
  247. readingsBulkUpdate($hash, $ereignis, $hash->{TW}{$ereignis}{TIME} == 0 ? "undefined" : FmtTime($hash->{TW}{$ereignis}{TIME}));
  248. }
  249. if ($hash->{CONDITION} != 50 ) {
  250. readingsBulkUpdate ($hash,"condition", $hash->{CONDITION});
  251. readingsBulkUpdate ($hash,"condition_txt",$hash->{CONDITION_TXT});
  252. }
  253. readingsEndUpdate ($hash, defined($hash->{LOCAL} ? 0 : 1));
  254. # ------------------------------------------------------------------------------
  255. my @horizonsOhneDeg = map {my($e, $deg)=split(":",$_); "$e"} @horizons;
  256. my @ereignisse = ((map {"sr$_"}@horizonsOhneDeg),(map {"ss$_"} reverse @horizonsOhneDeg),"sr$horizonsOhneDeg[0]");
  257. map { $hash->{TW}{$ereignisse[$_]}{NAMENEXT} = $ereignisse[$_+1] } 0..$#ereignisse-1;
  258. # ------------------------------------------------------------------------------
  259. my $myHash;
  260. my $now = time();
  261. my $secSinceMidnight = Twilight_midnight_seconds($now);
  262. my $lastMitternacht = $now-$secSinceMidnight;
  263. my $nextMitternacht = ($secSinceMidnight > 12*3600) ? $lastMitternacht+24*3600 : $lastMitternacht;
  264. my $jetztIstMitternacht = abs($now+5-$nextMitternacht)<=10;
  265. my @keyListe = qw "DEG LIGHT STATE SWIP TIME NAMENEXT";
  266. foreach my $ereignis (sort keys %{$hash->{TW}}) {
  267. next if ($whitchTimes eq "weather" && !($ereignis =~ m/weather/) );
  268. myRemoveInternalTimer($ereignis, $hash); # if(!$jetztIstMitternacht);
  269. if($hash->{TW}{$ereignis}{TIME} > 0) {
  270. $myHash = myInternalTimer($ereignis, $hash->{TW}{$ereignis}{TIME}, "Twilight_fireEvent", $hash, 0);
  271. map {$myHash->{$_} = $hash->{TW}{$ereignis}{$_} } @keyListe;
  272. }
  273. }
  274. # ------------------------------------------------------------------------------
  275. return 1;
  276. }
  277. ################################################################################
  278. sub Twilight_fireEvent($) {
  279. my ($myHash) = @_;
  280. my $hash = myGetHashIndirekt($myHash, (caller(0))[3]);
  281. return if (!defined($hash));
  282. my $name = $hash->{NAME};
  283. my $event = $myHash->{MODIFIER};
  284. my $deg = $myHash->{DEG};
  285. my $light = $myHash->{LIGHT};
  286. my $state = $myHash->{STATE};
  287. my $swip = $myHash->{SWIP};
  288. my $eventTime = $myHash->{TIME};
  289. my $nextEvent = $myHash->{NAMENEXT};
  290. my $delta = int($eventTime - time());
  291. my $oldState = ReadingsVal($name,"state","0");
  292. my $nextEventTime = ($hash->{TW}{$nextEvent}{TIME} > 0) ? FmtTime($hash->{TW}{$nextEvent}{TIME}) : "undefined";
  293. my $doTrigger = !(defined($hash->{LOCAL})) && ( abs($delta)<6 || $swip && $state gt $oldState);
  294. #Log3 $hash, 3, "[$hash->{NAME}] swip-delta-oldState-doTrigger===>$swip/$delta/$oldState/$doTrigger";
  295. Log3 $hash, 4,
  296. sprintf ("[$hash->{NAME}] %-10s %-19s ", $event, FmtDateTime($eventTime)).
  297. sprintf ("(%2d/$light/%+5.1f°/$doTrigger) ", $state, $deg).
  298. sprintf ("===> %-10s %-19s ", $nextEvent, $nextEventTime);
  299. readingsBeginUpdate($hash);
  300. readingsBulkUpdate ($hash, "state", $state);
  301. readingsBulkUpdate ($hash, "light", $light);
  302. readingsBulkUpdate ($hash, "horizon", $deg);
  303. readingsBulkUpdate ($hash, "aktEvent", $event);
  304. readingsBulkUpdate ($hash, "nextEvent", $nextEvent);
  305. readingsBulkUpdate ($hash, "nextEventTime", $nextEventTime);
  306. readingsEndUpdate ($hash, $doTrigger);
  307. }
  308. ################################################################################
  309. sub Twilight_Midnight($) {
  310. my ($myHash) = @_;
  311. my $hash = myGetHashIndirekt($myHash, (caller(0))[3]);
  312. return if (!defined($hash));
  313. $hash->{SWIP} = 0;
  314. my $param = Twilight_CreateHttpParameterAndGetData($myHash, "Mid");
  315. }
  316. ################################################################################
  317. # {Twilight_WeatherTimerUpdate( {HASH=$defs{"Twilight"}} ) }
  318. sub Twilight_WeatherTimerUpdate($) {
  319. my ($myHash) = @_;
  320. my $hash = myGetHashIndirekt($myHash, (caller(0))[3]);
  321. return if (!defined($hash));
  322. $hash->{SWIP} = 1;
  323. my $param = Twilight_CreateHttpParameterAndGetData($myHash, "weather");
  324. }
  325. ################################################################################
  326. sub Twilight_CreateHttpParameterAndGetData($$) {
  327. my ($myHash, $mode) = @_;
  328. my $hash = myGetHashIndirekt($myHash, (caller(0))[3]);
  329. return if (!defined($hash));
  330. my $location = $hash->{WEATHER};
  331. my $verbose = AttrVal($hash->{NAME}, "verbose", 3 );
  332. my $URL = "http://query.yahooapis.com/v1/public/yql?q=select%%20*%%20from%%20weather.forecast%%20where%%20woeid=%s%%20and%%20u=%%27c%%27&format=%s&env=store%%3A%%2F%%2Fdatatables.org%%2Falltableswithkeys";
  333. my $url = sprintf($URL, $location, "json");
  334. Log3 $hash, 4, "[$hash->{NAME}] url=$url";
  335. my $param = {
  336. url => $url,
  337. timeout => defined($hash->{DEFINE}) ? 10 :10,
  338. hash => $hash,
  339. method => "GET",
  340. loglevel => 4-($verbose-3),
  341. header => "User-Agent: Mozilla/5.0\r\nAccept: application/xml",
  342. callback => \&Twilight_WeatherCallback,
  343. mode => $mode };
  344. if (defined($hash->{DEFINE})) {
  345. delete $param->{callback};
  346. my ($err, $result) = HttpUtils_BlockingGet($param);
  347. Twilight_WeatherCallback($param, $err, $result);
  348. } else {
  349. HttpUtils_NonblockingGet($param);
  350. }
  351. }
  352. ################################################################################
  353. sub Twilight_WeatherCallback(@) {
  354. my ($param, $err, $result) = @_;
  355. my $hash = $param->{hash};
  356. return if (!defined($hash));
  357. if ($err) {
  358. Log3 $hash, 3, "[$hash->{NAME}] got no weather info from yahoo. Error code: $err";
  359. $result = undef;
  360. } else {
  361. Log3 $hash, 4, "[$hash->{NAME}] got weather info from yahoo for $hash->{WEATHER}";
  362. Log3 $hash, 5, "[$hash->{NAME}] answer=$result" if defined $result;
  363. }
  364. Twilight_getWeatherHorizon($hash, $result);
  365. #$hash->{CONDITION} = 50;
  366. if ($hash->{CONDITION} == 50 && $hash->{VERSUCHE} <= 10) {
  367. $hash->{VERSUCHE} += 1;
  368. Twilight_RepeatTimerSet($hash, $param->{mode});
  369. return;
  370. }
  371. Twilight_TwilightTimes ($hash, $param->{mode}, $result);
  372. Log3 $hash, 3, "[$hash->{NAME}] " . ($hash->{VERSUCHE}+1) . " attempt(s) needed to get valid weather data from yahoo" if ($hash->{CONDITION} != 50 && $hash->{VERSUCHE} > 0);
  373. Log3 $hash, 3, "[$hash->{NAME}] " . ($hash->{VERSUCHE}+1) . " attempt(s) needed got NO valid weather data from yahoo" if ($hash->{CONDITION} == 50 && $hash->{VERSUCHE} > 0);
  374. $hash->{VERSUCHE} = 0;
  375. Twilight_StandardTimerSet ($hash);
  376. }
  377. ################################################################################
  378. sub Twilight_RepeatTimerSet($$) {
  379. my ($hash, $mode) = @_;
  380. my $midnight = time() + 60;
  381. myRemoveInternalTimer("Midnight", $hash);
  382. if ($mode eq "Mid") {
  383. myInternalTimer ("Midnight", $midnight, "Twilight_Midnight", $hash, 0);
  384. } else {
  385. myInternalTimer ("Midnight", $midnight, "Twilight_WeatherTimerUpdate", $hash, 0);
  386. }
  387. }
  388. ################################################################################
  389. sub Twilight_StandardTimerSet($) {
  390. my ($hash) = @_;
  391. my $midnight = time() - Twilight_midnight_seconds(time()) + 24*3600 + 1;
  392. myRemoveInternalTimer ("Midnight", $hash);
  393. myInternalTimer ("Midnight", $midnight, "Twilight_Midnight", $hash, 0);
  394. Twilight_WeatherTimerSet ($hash);
  395. }
  396. ################################################################################
  397. sub Twilight_WeatherTimerSet($) {
  398. my ($hash) = @_;
  399. my $now = time();
  400. myRemoveInternalTimer ("weather", $hash);
  401. foreach my $key ("sr_weather", "ss_weather") {
  402. my $tim = $hash->{TW}{$key}{TIME};
  403. if ($tim-60*60>$now+60) {
  404. myInternalTimer ("weather", $tim-60*60, "Twilight_WeatherTimerUpdate", $hash, 0);
  405. last;
  406. }
  407. }
  408. }
  409. ################################################################################
  410. sub Twilight_sunposTimerSet($) {
  411. my ($hash) = @_;
  412. myRemoveInternalTimer ("sunpos", $hash);
  413. myInternalTimer ("sunpos", time()+$hash->{SUNPOS_OFFSET}, "Twilight_sunpos", $hash, 0);
  414. }
  415. ################################################################################
  416. sub Twilight_getWeatherHorizon(@)
  417. {
  418. my ($hash, $result) = @_;
  419. my $location=$hash->{WEATHER};
  420. if ($location == 0) {
  421. $hash->{WEATHER_HORIZON}="0";
  422. $hash->{CONDITION}="0";
  423. return 1;
  424. }
  425. my $mod = "[".$hash->{NAME} ."] ";
  426. my @faktor_cond_code = (10,10,10,10, 9, 7, 7, 7, 7, 7,
  427. 7, 5, 5, 5, 5, 7, 7, 5, 5, 5,
  428. 7, 5, 5, 5, 5, 5, 2, 4, 4, 2,
  429. 2, 0, 0, 0, 0, 5, 0, 8, 8, 8,
  430. 6, 8, 6, 5, 2, 5, 6, 6, 0, 0,
  431. 0);
  432. # condition codes are described in FHEM wiki and in the documentation of the yahoo weather API
  433. my ($cond_code, $cond_txt, $temperatur, $aktTemp);
  434. if (defined($result)) {
  435. # ersetze in result(json) ": durch "=>
  436. # dadurch entsteht ein Perlausdruck, der direkt geparst werden kann
  437. my $perlAusdruck = $result;
  438. #$perlAusdruck = "<h1>could";
  439. $perlAusdruck =~ s/("[\w ]+")(\s*)(:)/$1=>/g;
  440. $perlAusdruck =~ s/null/undef/g;
  441. $perlAusdruck =~ s/true/1/g;
  442. $perlAusdruck =~ s/false/0/g;
  443. $perlAusdruck = 'return ' .$perlAusdruck;
  444. my $anonymSub = eval "sub {$perlAusdruck}";
  445. Log3 $hash, 3, "[$hash->{NAME}] error $@ parsing $result" if($@);
  446. if (!$@) {
  447. my $resHash = $anonymSub->() if ($anonymSub gt "");
  448. Log3 $hash, 3, "[$hash->{NAME}] error $@ parsing $result" if($@);
  449. #Log3 $hash, 3, "jsonAsPerl". Dumper $resHash->{query}{results}{channel}{item}{condition};
  450. if (!$@) {
  451. $cond_code = $resHash->{query}{results}{channel}{item}{condition}{code};
  452. $cond_txt = $resHash->{query}{results}{channel}{item}{condition}{text};
  453. $temperatur = $resHash->{query}{results}{channel}{item}{condition}{temp};
  454. }
  455. }
  456. }
  457. # wenn kein Code ermittelt werden kann, wird ein Pseudocode gesetzt
  458. if (!defined($cond_code) ) {
  459. $cond_code = "50"; # eigener neutraler Code
  460. $cond_txt = "undefined";
  461. $temperatur = "undefined";
  462. } else {
  463. $hash->{WEATHER_CORRECTION} = $faktor_cond_code[$cond_code] / 25 * 20;
  464. $hash->{WEATHER_HORIZON} = $hash->{WEATHER_CORRECTION} + $hash->{INDOOR_HORIZON};
  465. $hash->{CONDITION} = $cond_code;
  466. $hash->{CONDITION_TXT} = $cond_txt;
  467. $hash->{TEMPERATUR} = $temperatur;
  468. Log3 $hash, 4, "[$hash->{NAME}] $cond_code=$cond_txt $temperatur, correction: $hash->{WEATHER_CORRECTION}°";
  469. }
  470. my $doy = strftime("%j",localtime);
  471. my $declination = 0.4095*sin(0.016906*($doy-80.086));
  472. if($hash->{WEATHER_HORIZON} > (89-$hash->{LATITUDE}+$declination) ){
  473. $hash->{WEATHER_HORIZON} = 89-$hash->{LATITUDE}+$declination;
  474. }
  475. return 1;
  476. }
  477. ################################################################################
  478. sub Twilight_sunpos($)
  479. {
  480. my ($myHash) = @_;
  481. my $hash = myGetHashIndirekt($myHash, (caller(0))[3]);
  482. return if (!defined($hash));
  483. my $hashName = $hash->{NAME};
  484. return "" if(AttrVal($hashName, "disable", undef));
  485. my $tn = TimeNow();
  486. my ($dSeconds,$dMinutes,$dHours,$iDay,$iMonth,$iYear,$wday,$yday,$isdst) = gmtime(time);
  487. $iMonth++;
  488. $iYear += 100;
  489. my $dLongitude = $hash->{LONGITUDE};
  490. my $dLatitude = $hash->{LATITUDE};
  491. Log3 $hash, 5, "Compute sunpos for latitude $dLatitude , longitude $dLongitude" if($dHours == 0 && $dMinutes <= 6 );
  492. my $pi=3.14159265358979323846;
  493. my $twopi=(2*$pi);
  494. my $rad=($pi/180);
  495. my $dEarthMeanRadius=6371.01; # In km
  496. my $dAstronomicalUnit=149597890; # In km
  497. # Calculate difference in days between the current Julian Day
  498. # and JD 2451545.0, which is noon 1 January 2000 Universal Time
  499. # Calculate time of the day in UT decimal hours
  500. my $dDecimalHours=$dHours + $dMinutes/60.0 + $dSeconds/3600.0;
  501. # Calculate current Julian Day
  502. my $iYfrom2000=$iYear;#expects now as YY ;
  503. my $iA=(14 - ($iMonth)) / 12;
  504. my $iM=($iMonth) + 12 * $iA -3;
  505. my $liAux3=(153 * $iM + 2)/5;
  506. my $liAux4=365 * ($iYfrom2000 - $iA);
  507. my $liAux5=( $iYfrom2000 - $iA)/4;
  508. my $dElapsedJulianDays=($iDay + $liAux3 + $liAux4 + $liAux5 + 59)+ -0.5 + $dDecimalHours/24.0;
  509. # Calculate ecliptic coordinates (ecliptic longitude and obliquity of the
  510. # ecliptic in radians but without limiting the angle to be less than 2*Pi
  511. # (i.e., the result may be greater than 2*Pi)
  512. my $dOmega = 2.1429 - 0.0010394594 * $dElapsedJulianDays;
  513. my $dMeanLongitude = 4.8950630 + 0.017202791698 * $dElapsedJulianDays; # Radians
  514. my $dMeanAnomaly = 6.2400600 + 0.0172019699 * $dElapsedJulianDays;
  515. my $dEclipticLongitude = $dMeanLongitude + 0.03341607 * sin( $dMeanAnomaly ) + 0.00034894 * sin( 2 * $dMeanAnomaly ) -0.0001134 -0.0000203 * sin($dOmega);
  516. my $dEclipticObliquity = 0.4090928 - 6.2140e-9 * $dElapsedJulianDays +0.0000396 * cos($dOmega);
  517. # Calculate celestial coordinates ( right ascension and declination ) in radians
  518. # but without limiting the angle to be less than 2*Pi (i.e., the result may be
  519. # greater than 2*Pi)
  520. my $dSin_EclipticLongitude=sin( $dEclipticLongitude );
  521. my $dY1=cos( $dEclipticObliquity ) * $dSin_EclipticLongitude;
  522. my $dX1=cos( $dEclipticLongitude );
  523. my $dRightAscension=atan2( $dY1,$dX1 );
  524. if ( $dRightAscension < 0.0 ) { $dRightAscension=$dRightAscension + $twopi };
  525. my $dDeclination=asin( sin( $dEclipticObliquity )* $dSin_EclipticLongitude );
  526. # Calculate local coordinates ( azimuth and zenith angle ) in degrees
  527. my $dGreenwichMeanSiderealTime=6.6974243242 + 0.0657098283 * $dElapsedJulianDays + $dDecimalHours;
  528. my $dLocalMeanSiderealTime=($dGreenwichMeanSiderealTime*15 + $dLongitude)* $rad;
  529. my $dHourAngle=$dLocalMeanSiderealTime - $dRightAscension;
  530. my $dLatitudeInRadians=$dLatitude * $rad;
  531. my $dCos_Latitude=cos( $dLatitudeInRadians );
  532. my $dSin_Latitude=sin( $dLatitudeInRadians );
  533. my $dCos_HourAngle=cos( $dHourAngle );
  534. my $dZenithAngle=(acos( $dCos_Latitude * $dCos_HourAngle * cos($dDeclination) + sin( $dDeclination )* $dSin_Latitude));
  535. my $dY=-sin( $dHourAngle );
  536. my $dX=tan( $dDeclination )* $dCos_Latitude - $dSin_Latitude * $dCos_HourAngle;
  537. my $dAzimuth=atan2( $dY, $dX );
  538. if ( $dAzimuth < 0.0 ) {$dAzimuth=$dAzimuth + $twopi};
  539. $dAzimuth=$dAzimuth / $rad;
  540. # Parallax Correction
  541. my $dParallax=($dEarthMeanRadius / $dAstronomicalUnit) * sin( $dZenithAngle);
  542. $dZenithAngle=($dZenithAngle + $dParallax) / $rad;
  543. my $dElevation=90 - $dZenithAngle;
  544. my $twilight = int(($dElevation+12.0)/18.0 * 1000)/10;
  545. $twilight = 100 if ($twilight>100);
  546. $twilight = 0 if ($twilight< 0);
  547. my $twilight_weather ;
  548. if( (my $ExtWeather = AttrVal($hashName, "useExtWeather", "")) eq "") {
  549. $twilight_weather = int(($dElevation-$hash->{WEATHER_HORIZON}+12.0)/18.0 * 1000)/10;
  550. Log3 $hash, 5, "[$hash->{NAME}] " . "Original weather readings";
  551. } else {
  552. my($extDev,$extReading) = split(":",$ExtWeather);
  553. my $extWeatherHorizont = ReadingsVal($extDev,$extReading,-1);
  554. if ($extWeatherHorizont >= 0){
  555. $extWeatherHorizont = 100 if ($extWeatherHorizont > 100);
  556. Log3 $hash, 5, "[$hash->{NAME}] " . "New weather readings from: ".$extDev.":".$extReading.":".$extWeatherHorizont;
  557. $twilight_weather = $twilight - int(0.007 * ($extWeatherHorizont ** 2)); ## SCM: 100% clouds => 30% light (rough estimation)
  558. } else {
  559. $twilight_weather = int(($dElevation-$hash->{WEATHER_HORIZON}+12.0)/18.0 * 1000)/10;
  560. Log3 $hash, 3, "[$hash->{NAME}] " . "Error with external readings from: ".$extDev.":".$extReading." , taking original weather readings";
  561. }
  562. }
  563. $twilight_weather = 100 if ($twilight_weather>100);
  564. $twilight_weather = 0 if ($twilight_weather< 0);
  565. # set readings
  566. $dAzimuth = int(100*$dAzimuth )/100;
  567. $dElevation = int(100*$dElevation)/100;
  568. my $compassPoint = Twilight_CompassPoint($dAzimuth);
  569. readingsBeginUpdate($hash);
  570. readingsBulkUpdate ($hash, "azimuth", $dAzimuth );
  571. readingsBulkUpdate ($hash, "elevation", $dElevation );
  572. readingsBulkUpdate ($hash, "twilight", $twilight );
  573. readingsBulkUpdate ($hash, "twilight_weather", $twilight_weather );
  574. readingsBulkUpdate ($hash, "compasspoint", $compassPoint);
  575. readingsEndUpdate ($hash, defined($hash->{LOCAL} ? 0 : 1));
  576. Twilight_sunposTimerSet($hash);
  577. return undef;
  578. }
  579. ################################################################################
  580. sub Twilight_CompassPoint($) {
  581. my ($azimuth) = @_;
  582. my $compassPoint = "unknown";
  583. if ($azimuth < 22.5) {
  584. $compassPoint = "north";
  585. } elsif ($azimuth < 45) {
  586. $compassPoint = "north-northeast";
  587. } elsif ($azimuth < 67.5) {
  588. $compassPoint = "northeast";
  589. } elsif ($azimuth < 90) {
  590. $compassPoint = "east-northeast";
  591. } elsif ($azimuth < 112.5){
  592. $compassPoint = "east";
  593. } elsif ($azimuth < 135) {
  594. $compassPoint = "east-southeast";
  595. } elsif ($azimuth < 157.5){
  596. $compassPoint = "southeast";
  597. } elsif ($azimuth < 180) {
  598. $compassPoint = "south-southeast";
  599. } elsif ($azimuth < 202.5){
  600. $compassPoint = "south";
  601. } elsif ($azimuth < 225) {
  602. $compassPoint = "south-southwest";
  603. } elsif ($azimuth < 247.5){
  604. $compassPoint = "southwest";
  605. } elsif ($azimuth < 270) {
  606. $compassPoint = "west-southwest";
  607. } elsif ($azimuth < 292.5){
  608. $compassPoint = "west";
  609. } elsif ($azimuth < 315) {
  610. $compassPoint = "west-northwest";
  611. } elsif ($azimuth < 337.5){
  612. $compassPoint = "northwest";
  613. } elsif ($azimuth <= 361) {
  614. $compassPoint = "north-northwest";
  615. }
  616. return $compassPoint;
  617. }
  618. sub twilight($$$$) {
  619. my ($twilight, $reading, $min, $max) = @_;
  620. my $t = hms2h(ReadingsVal($twilight,$reading,0));
  621. $t = hms2h($min) if(defined($min) && (hms2h($min) > $t));
  622. $t = hms2h($max) if(defined($max) && (hms2h($max) < $t));
  623. return h2hms_fmt($t);
  624. }
  625. 1;
  626. =pod
  627. =item device
  628. =item summary delivers twilight and other sun related events for use in notify
  629. =item summary_DE liefert Dämmerungs Sonnen basierte Ereignisse, für notify
  630. =begin html
  631. <a name="Twilight"></a>
  632. <h3>Twilight</h3>
  633. <ul>
  634. <br>
  635. <a name="Twilightdefine"></a>
  636. <b>Define</b>
  637. <ul>
  638. <code>define &lt;name&gt; Twilight &lt;latitude&gt; &lt;longitude&gt; [&lt;indoor_horizon&gt; [&lt;Weather_Position&gt;]]</code><br>
  639. <br>
  640. Defines a virtual device for Twilight calculations <br><br>
  641. <b>latitude, longitude</b>
  642. <br>
  643. The parameters <b>latitude</b> and <b>longitude</b> are decimal numbers which give the position on earth for which the twilight states shall be calculated.
  644. <br><br>
  645. <b>indoor_horizon</b>
  646. <br>
  647. The parameter <b>indoor_horizon</b> gives a virtual horizon, that shall be used for calculation of indoor twilight. Minimal value -6 means indoor values are the same like civil values.
  648. indoor_horizon 0 means indoor values are the same as real values. indoor_horizon > 0 means earlier indoor sunset resp. later indoor sunrise.
  649. <br><br>
  650. <b>Weather_Position</b>
  651. <br>
  652. The parameter <b>Weather_Position</b> is the yahoo weather id used for getting the weather condition. Go to http://weather.yahoo.com/ and enter a city or zip code. In the upcoming webpage, the id is a the end of the URL. Example: Munich, Germany -> 676757
  653. <br><br>
  654. A Twilight device periodically calculates the times of different twilight phases throughout the day.
  655. It calculates a virtual "light" element, that gives an indicator about the amount of the current daylight.
  656. Besides the location on earth it is influenced by a so called "indoor horizon" (e.g. if there are high buildings, mountains) as well as by weather conditions. Very bad weather conditions lead to a reduced daylight for nearly the whole day.
  657. The light calculated spans between 0 and 6, where the values mean the following:
  658. <br><br>
  659. <b>light</b>
  660. <br>
  661. <code>0 - total night, sun is at least -18 degree below horizon</code><br>
  662. <code>1 - astronomical twilight, sun is between -12 and -18 degree below horizon</code><br>
  663. <code>2 - nautical twilight, sun is between -6 and -12 degree below horizon</code><br>
  664. <code>3 - civil twilight, sun is between 0 and -6 degree below horizon</code><br>
  665. <code>4 - indoor twilight, sun is between the indoor_horizon and 0 degree below horizon (not used if indoor_horizon=0)</code><br>
  666. <code>5 - weather twilight, sun is between indoor_horizon and a virtual weather horizon (the weather horizon depends on weather conditions (optional)</code><br>
  667. <code>6 - maximum daylight</code><br>
  668. <br>
  669. <b>Azimut, Elevation, Twilight</b>
  670. <br>
  671. The module calculates additionally the <b>azimuth</b> and the <b>elevation</b> of the sun. The values can be used to control a roller shutter.
  672. <br><br>
  673. As a new (twi)light value the reading <b>Twilight</b> ist added. It is derived from the elevation of the sun with the formula: (Elevation+12)/18 * 100). The value allows a more detailed
  674. control of any lamp during the sunrise/sunset phase. The value ist betwenn 0% and 100% when the elevation is between -12&deg; and 6&deg;.
  675. <br><br>
  676. You must know, that depending on the latitude, the sun will not reach any elevation. In june/july the sun never falls in middle europe
  677. below -18&deg;. In more northern countries(norway ...) the sun may not go below 0&deg;.
  678. <br><br>
  679. Any control depending on the value of Twilight must
  680. consider these aspects.
  681. <br><br>
  682. Example:
  683. <pre>
  684. define myTwilight Twilight 49.962529 10.324845 3 676757
  685. </pre>
  686. </ul>
  687. <br>
  688. <a name="Twilightset"></a>
  689. <b>Set </b>
  690. <ul>
  691. N/A
  692. </ul>
  693. <br>
  694. <a name="Twilightget"></a>
  695. <b>Get</b>
  696. <ul>
  697. <code>get &lt;name&gt; &lt;reading&gt;</code><br><br>
  698. <table>
  699. <tr><td><b>light</b></td><td>the current virtual daylight value</td></tr>
  700. <tr><td><b>nextEvent</b></td><td>the name of the next event</td></tr>
  701. <tr><td><b>nextEventTime</b></td><td>the time when the next event will probably happen (during light phase 5 and 6 this is updated when weather conditions change</td></tr>
  702. <tr><td><b>sr_astro</b></td><td>time of astronomical sunrise</td></tr>
  703. <tr><td><b>sr_naut</b></td><td>time of nautical sunrise</td></tr>
  704. <tr><td><b>sr_civil</b></td><td>time of civil sunrise</td></tr>
  705. <tr><td><b>sr</b></td><td>time of sunrise</td></tr>
  706. <tr><td><b>sr_indoor</b></td><td>time of indoor sunrise</td></tr>
  707. <tr><td><b>sr_weather</b></td><td>time of weather sunrise</td></tr>
  708. <tr><td><b>ss_weather</b></td><td>time of weather sunset</td></tr>
  709. <tr><td><b>ss_indoor</b></td><td>time of indoor sunset</td></tr>
  710. <tr><td><b>ss</b></td><td>time of sunset</td></tr>
  711. <tr><td><b>ss_civil</b></td><td>time of civil sunset</td></tr>
  712. <tr><td><b>ss_nautic</b></td><td>time of nautic sunset</td></tr>
  713. <tr><td><b>ss_astro</b></td><td>time of astro sunset</td></tr>
  714. <tr><td><b>azimuth</b></td><td>the current azimuth of the sun 0&deg; ist north 180&deg; is south</td></tr>
  715. <tr><td><b>compasspoint</b></td><td>a textual representation of the compass point</td></tr>
  716. <tr><td><b>elevation</b></td><td>the elevaltion of the sun</td></tr>
  717. <tr><td><b>twilight</b></td><td>a percetal value of a new (twi)light value: (elevation+12)/18 * 100) </td></tr>
  718. <tr><td><b>twilight_weather</b></td><td>a percetal value of a new (twi)light value: (elevation-WEATHER_HORIZON+12)/18 * 100). So if there is weather, it
  719. is always a little bit darker than by fair weather</td></tr>
  720. <tr><td><b>condition</b></td><td>the yahoo condition weather code</td></tr>
  721. <tr><td><b>condition_txt</b></td><td>the yahoo condition weather code as textual representation</td></tr>
  722. <tr><td><b>horizon</b></td><td>value auf the actual horizon 0&deg;, -6&deg;, -12&deg;, -18&deg;</td></tr>
  723. </table>
  724. </ul>
  725. <br>
  726. <a name="Twilightattr"></a>
  727. <b>Attributes</b>
  728. <ul>
  729. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  730. <li><b>useExtWeather &lt;device&gt;:&lt;reading&gt;</b></li>
  731. use data from other devices to calculate <b>twilight_weather</b>.<br/>
  732. The reading used shoud be in the range of 0 to 100 like the reading <b>c_clouds</b> in an <b><a href="#openweathermap">openweathermap</a></b> device, where 0 is clear sky and 100 are overcast clouds.<br/>
  733. With the use of this attribute weather effects like heavy rain or thunderstorms are neglegted for the calculation of the <b>twilight_weather</b> reading.<br/>
  734. </ul>
  735. <br>
  736. <a name="Twilightfunc"></a>
  737. <b>Functions</b>
  738. <ul>
  739. <li><b>twilight</b>(<b>$twilight</b>, <b>$reading</b>, <b>$min</b>, <b>$max</b>)</li> - implements a routine to compute the twilighttimes like sunrise with min max values.<br><br>
  740. <table>
  741. <tr><td><b>$twilight</b></td><td>name of the twilight instance</td></tr>
  742. <tr><td><b>$reading</b></td><td>name of the reading to use example: ss_astro, ss_weather ...</td></tr>
  743. <tr><td><b>$min</b></td><td>parameter min time - optional</td></tr>
  744. <tr><td><b>$max</b></td><td>parameter max time - optional</td></tr>
  745. </table>
  746. </ul>
  747. <br>
  748. Example:
  749. <pre>
  750. define BlindDown at *{twilight("myTwilight","sr_indoor","7:30","9:00")} set xxxx position 100
  751. # xxxx is a defined blind
  752. </pre>
  753. </ul>
  754. =end html
  755. =begin html_DE
  756. <a name="Twilight"></a>
  757. <h3>Twilight</h3>
  758. <ul>
  759. <br>
  760. <a name="Twilightdefine"></a>
  761. <b>Define</b>
  762. <ul>
  763. <code>define &lt;name&gt; Twilight &lt;latitude&gt; &lt;longitude&gt; [&lt;indoor_horizon&gt; [&lt;Weather_Position&gt;]]</code><br>
  764. <br>
  765. Erstellt ein virtuelles Device f&uuml;r die D&auml;mmerungsberechnung (Zwielicht)<br><br>
  766. <b>latitude, longitude (geografische L&auml;nge & Breite)</b>
  767. <br>
  768. Die Parameter <b>latitude</b> und <b>longitude</b> sind Dezimalzahlen welche die Position auf der Erde bestimmen, f&uuml;r welche der Dämmerungs-Status berechnet werden soll.
  769. <br><br>
  770. <b>indoor_horizon</b>
  771. <br>
  772. Der Parameter <b>indoor_horizon</b> bestimmt einen virtuellen Horizont, der f&uuml;r die Berechnung der D&auml;mmerung innerhalb von R&auml;men genutzt werden kann. Minimalwert ist -6 (ergibt gleichen Wert wie Zivile D&auml;mmerung). Bei 0 fallen
  773. indoor- und realer D&aumlmmerungswert zusammen. Werte gr&oumlsser 0 ergeben fr&uumlhere Werte für den Abend bzw. sp&aumltere f&uumlr den Morgen.
  774. <br><br>
  775. <b>Weather_Position</b>
  776. <br>
  777. Der Parameter <b>Weather_Position</b> ist die Yahoo! Wetter-ID welche f&uuml;r den Bezug der Wetterinformationen gebraucht wird. Gehe auf http://weather.yahoo.com/ und gebe einen Ort (ggf. PLZ) ein. In der URL der daraufhin geladenen Seite ist an letzter Stelle die ID. Beispiel: München, Deutschland -> 676757
  778. <br><br>
  779. Ein Twilight-Device berechnet periodisch die D&auml;mmerungszeiten und -phasen w&auml;hrend des Tages.
  780. Es berechnet ein virtuelles "Licht"-Element das einen Indikator f&uuml;r die momentane Tageslichtmenge ist.
  781. Neben der Position auf der Erde wird es vom sog. "indoor horizon" (Beispielsweise hohe Geb&auml;de oder Berge)
  782. und dem Wetter beeinflusst. Schlechtes Wetter f&uuml;hrt zu einer Reduzierung des Tageslichts f&uuml;r den ganzen Tag.
  783. Das berechnete Licht liegt zwischen 0 und 6 wobei die Werte folgendes bedeuten:<br><br>
  784. <b>light</b>
  785. <br>
  786. <code>0 - Totale Nacht, die Sonne ist mind. -18 Grad hinter dem Horizont</code><br>
  787. <code>1 - Astronomische D&auml;mmerung, die Sonne ist zw. -12 und -18 Grad hinter dem Horizont</code><br>
  788. <code>2 - Nautische D&auml;mmerung, die Sonne ist zw. -6 and -12 Grad hinter dem Horizont</code><br>
  789. <code>3 - Zivile/B&uuml;rgerliche D&auml;mmerung, die Sonne ist zw. 0 and -6 hinter dem Horizont</code><br>
  790. <code>4 - "indoor twilight", die Sonne ist zwischen dem Wert indoor_horizon und 0 Grad hinter dem Horizont (wird nicht verwendet wenn indoor_horizon=0)</code><br>
  791. <code>5 - Wetterbedingte D&auml;mmerung, die Sonne ist zwischen indoor_horizon und einem virtuellen Wetter-Horizonz (der Wetter-Horizont ist Wetterabh&auml;ngig (optional)</code><br>
  792. <code>6 - Maximales Tageslicht</code><br>
  793. <br>
  794. <b>Azimut, Elevation, Twilight (Seitenwinkel, Höhenwinkel, D&auml;mmerung)</b>
  795. <br>
  796. Das Modul berechnet zus&auml;tzlich Azimuth und Elevation der Sonne. Diese Werte k&ouml;nnen zur Rolladensteuerung verwendet werden.<br><br>
  797. Das Reading <b>Twilight</b> wird als neuer "(twi)light" Wert hinzugef&uuml;gt. Er wird aus der Elevation der Sonne mit folgender Formel abgeleitet: (Elevation+12)/18 * 100). Das erlaubt eine detailliertere Kontrolle der Lampen w&auml;hrend Sonnenauf - und untergang. Dieser Wert ist zwischen 0% und 100% wenn die Elevation zwischen -12&deg; und 6&deg;
  798. <br><br>
  799. Wissenswert dazu ist, dass die Sonne, abh&auml;gnig vom Breitengrad, bestimmte Elevationen nicht erreicht. Im Juni und Juli liegt die Sonne in Mitteleuropa nie unter -18&deg;. In n&ouml;rdlicheren Gebieten (Norwegen, ...) kommt die Sonne beispielsweise nicht &uuml;ber 0&deg.
  800. <br><br>
  801. All diese Aspekte m&uuml;ssen ber&uuml;cksichtigt werden bei Schaltungen die auf Twilight basieren.
  802. <br><br>
  803. Beispiel:
  804. <pre>
  805. define myTwilight Twilight 49.962529 10.324845 3 676757
  806. </pre>
  807. </ul>
  808. <br>
  809. <a name="Twilightset"></a>
  810. <b>Set </b>
  811. <ul>
  812. N/A
  813. </ul>
  814. <br>
  815. <a name="Twilightget"></a>
  816. <b>Get</b>
  817. <ul>
  818. <code>get &lt;name&gt; &lt;reading&gt;</code><br><br>
  819. <table>
  820. <tr><td><b>light</b></td><td>der aktuelle virtuelle Tageslicht-Wert</td></tr>
  821. <tr><td><b>nextEvent</b></td><td>Name des n&auml;chsten Events</td></tr>
  822. <tr><td><b>nextEventTime</b></td><td>die Zeit wann das n&auml;chste Event wahrscheinlich passieren wird (w&auml;hrend Lichtphase 5 und 6 wird dieser Wert aktualisiert wenn sich das Wetter &auml;ndert)</td></tr>
  823. <tr><td><b>sr_astro</b></td><td>Zeit des astronomitschen Sonnenaufgangs</td></tr>
  824. <tr><td><b>sr_naut</b></td><td>Zeit des nautischen Sonnenaufgangs</td></tr>
  825. <tr><td><b>sr_civil</b></td><td>Zeit des zivilen/b&uuml;rgerlichen Sonnenaufgangs</td></tr>
  826. <tr><td><b>sr</b></td><td>Zeit des Sonnenaufgangs</td></tr>
  827. <tr><td><b>sr_indoor</b></td><td>Zeit des "indoor" Sonnenaufgangs</td></tr>
  828. <tr><td><b>sr_weather</b></td><td>"Wert" des Wetters beim Sonnenaufgang</td></tr>
  829. <tr><td><b>ss_weather</b></td><td>"Wert" des Wetters beim Sonnenuntergang</td></tr>
  830. <tr><td><b>ss_indoor</b></td><td>Zeit des "indoor" Sonnenuntergangs</td></tr>
  831. <tr><td><b>ss</b></td><td>Zeit des Sonnenuntergangs</td></tr>
  832. <tr><td><b>ss_civil</b></td><td>Zeit des zivilen/b&uuml;rgerlichen Sonnenuntergangs</td></tr>
  833. <tr><td><b>ss_nautic</b></td><td>Zeit des nautischen Sonnenuntergangs</td></tr>
  834. <tr><td><b>ss_astro</b></td><td>Zeit des astro. Sonnenuntergangs</td></tr>
  835. <tr><td><b>azimuth</b></td><td>aktueller Azimuth der Sonne. 0&deg; ist Norden 180&deg; ist S&uuml;den</td></tr>
  836. <tr><td><b>compasspoint</b></td><td>Ein Wortwert des Kompass-Werts</td></tr>
  837. <tr><td><b>elevation</b></td><td>the elevaltion of the sun</td></tr>
  838. <tr><td><b>twilight</b></td><td>Prozentualer Wert eines neuen "(twi)light" Wertes: (elevation+12)/18 * 100) </td></tr>
  839. <tr><td><b>twilight_weather</b></td><td>Prozentualer Wert eines neuen "(twi)light" Wertes: (elevation-WEATHER_HORIZON+12)/18 * 100). Wenn ein Wetterwert vorhanden ist, ist es immer etwas dunkler als bei klarem Wetter.</td></tr>
  840. <tr><td><b>condition</b></td><td>Yahoo! Wetter code</td></tr>
  841. <tr><td><b>condition_txt</b></td><td>Yahoo! Wetter code als Text</td></tr>
  842. <tr><td><b>horizon</b></td><td>Wert des aktuellen Horizont 0&deg;, -6&deg;, -12&deg;, -18&deg;</td></tr>
  843. </table>
  844. </ul>
  845. <br>
  846. <a name="Twilightattr"></a>
  847. <b>Attributes</b>
  848. <ul>
  849. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  850. <li><b>useExtWeather &lt;device&gt;:&lt;reading&gt;</b></li>
  851. Nutzt Daten von einem anderen Device um <b>twilight_weather</b> zu berechnen.<br/>
  852. Das Reading sollte sich im Intervall zwischen 0 und 100 bewegen, z.B. das Reading <b>c_clouds</b> in einem<b><a href="#openweathermap">openweathermap</a></b> device, bei dem 0 heiteren und 100 bedeckten Himmel bedeuten.
  853. Wird diese Attribut genutzt , werden Wettereffekte wie Starkregen oder Gewitter fuer die Berechnung von <b>twilight_weather</b> nicht mehr herangezogen.
  854. </ul>
  855. <br>
  856. <a name="Twilightfunc"></a>
  857. <b>Functions</b>
  858. <ul>
  859. <li><b>twilight</b>(<b>$twilight</b>, <b>$reading</b>, <b>$min</b>, <b>$max</b>)</li> - implementiert eine Routine um die D&auml;mmerungszeiten wie Sonnenaufgang mit min und max Werten zu berechnen.<br><br>
  860. <table>
  861. <tr><td><b>$twilight</b></td><td>Name der twiligh Instanz</td></tr>
  862. <tr><td><b>$reading</b></td><td>Name des zu verwendenden Readings. Beispiel: ss_astro, ss_weather ...</td></tr>
  863. <tr><td><b>$min</b></td><td>Parameter min time - optional</td></tr>
  864. <tr><td><b>$max</b></td><td>Parameter max time - optional</td></tr>
  865. </table>
  866. </ul>
  867. <br>
  868. Anwendungsbeispiel:
  869. <pre>
  870. define BlindDown at *{twilight("myTwilight","sr_indoor","7:30","9:00")} set xxxx position 100
  871. # xxxx ist ein definiertes Rollo
  872. </pre>
  873. </ul>
  874. =end html_DE
  875. =cut