| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714 |
- ###############################################################################
- # $Id: 50_HP1000.pm 17527 2018-10-14 09:38:12Z loredo $
- package main;
- use strict;
- use warnings;
- use Data::Dumper;
- use Time::Local;
- use Encode qw(encode_utf8 decode_utf8);
- use List::Util qw(sum);
- use HttpUtils;
- use Unit;
- # module hashes ###############################################################
- my %HP1000_pwsMapping = (
- # PWS => 'FHEM',
- # name translation for general readings
- light => 'luminosity',
- solarradiation => 'solarradiation',
- windcomp => 'wind_compasspoint',
- windcomp_avg2m => 'wind_compasspoint_avg2m',
- windcomp_avg10m => 'wind_compasspoint_avg10m',
- winddir => 'wind_direction',
- winddir_avg2m => 'wind_direction_avg2m',
- windgustdir => 'wind_gust_direction',
- windgustdir_10m => 'wind_gust_direction_avg10m',
- # name translation for Metric standard
- dewpoint => 'dewpoint',
- indewpoint => 'indoorDewpoint',
- humidity => 'humidity',
- outhumiabs => 'humidityAbs',
- relbaro => 'pressure',
- absbaro => 'pressureAbs',
- indoorhumidity => 'indoorHumidity',
- inhumiabs => 'indoorHumidityAbs',
- rainrate => 'rain',
- dailyrain => 'rain_day',
- weeklyrain => 'rain_week',
- monthlyrain => 'rain_month',
- yearlyrain => 'rain_year',
- outtemp => 'temperature',
- intemp => 'indoorTemperature',
- windchill => 'wind_chill',
- windgust => 'wind_gust',
- windgust_10m => 'wind_gust_max10m',
- windgustmps => 'wind_gust_mps',
- windgustmps_10m => 'wind_gust_mps_max10m',
- windspeed => 'wind_speed',
- windspeed_avg2m => 'wind_speed_avg2m',
- windspeedmps => 'wind_speed_mps',
- windspdmps_avg2m => 'wind_speed_mps_avg2m',
- # other formats
- barommm => 'pressure_mm',
- absbarommm => 'pressureAbs_mm',
- windgustbft => 'wind_gust_bft',
- windgustbft_10m => 'wind_gust_bft_max10m',
- windgustkn => 'wind_gust_kn',
- windgustkn_10m => 'wind_gust_kn_max10m',
- windspeedbft => 'wind_speed_bft',
- windspdbft_avg2m => 'wind_speed_bft_avg2m',
- windspeedkn => 'wind_speed_kn',
- windspdkn_avg2m => 'wind_speed_kn_avg2m',
- # name translation for Angloamerican standard
- dewptf => 'dewpoint_f',
- indoordewptf => 'indoorDewpoint_f',
- outhumi => 'humidity',
- outhumiabsf => 'humidityAbs_f',
- baromin => 'pressure_in',
- absbaromin => 'pressureAbs_in',
- inhumi => 'indoorHumidity',
- indoorhumidityabsf => 'indoorHumidityAbs',
- rainin => 'rain_in',
- dailyrainin => 'rain_day_in',
- weeklyrainin => 'rain_week_in',
- monthlyrainin => 'rain_month_in',
- yearlyrainin => 'rain_year_in',
- tempf => 'temperature_f',
- indoortempf => 'indoorTemperature_f',
- windchillf => 'wind_chill_f',
- windgustfts => 'wind_gust_fts',
- windgustfts_10m => 'wind_gust_fts_max10m',
- windgustmph => 'wind_gust_mph',
- windgustmph_10m => 'wind_gust_mph_max10m',
- windspeedfts => 'wind_speed_fts',
- windspdfts_avg2m => 'wind_speed_fts_avg2m',
- windspeedmph => 'wind_speed_mph',
- windspdmph_avg2m => 'wind_speed_mph_avg2m',
- );
- my %HP1000_wuParams = (
- action => 1,
- baromin => 1,
- clouds => 1,
- dailyrainin => 1,
- dateutc => 1,
- dewptf => 1,
- humidity => 1,
- ID => 1,
- indoorhumidity => 1,
- indoortempf => 1,
- lowbatt => 1,
- monthlyrainin => 1,
- PASSWORD => 1,
- rainin => 1,
- realtime => 1,
- rtfreq => 1,
- softwaretype => 1,
- solarradiation => 1,
- tempf => 1,
- UV => 1,
- weather => 1,
- weeklyrainin => 1,
- windchillf => 1,
- winddir => 1,
- winddir_avg2m => 1,
- windgustdir => 1,
- windgustdir_10m => 1,
- windgustmph => 1,
- windgustmph_10m => 1,
- windspdmph_avg2m => 1,
- windspeedmph => 1,
- yearlyrainin => 1,
- );
- # initialize ##################################################################
- sub HP1000_Initialize($) {
- my ($hash) = @_;
- Log3 $hash, 5, "HP1000_Initialize: Entering";
- if ( !$modules{dewpoint}{LOADED}
- && -f "$attr{global}{modpath}/FHEM/98_dewpoint.pm" )
- {
- my $ret = CommandReload( undef, "98_dewpoint" );
- Log3 undef, 1, $ret if ($ret);
- }
- my $webhookFWinstance =
- join( ",", devspec2array('TYPE=FHEMWEB:FILTER=TEMPORARY!=1') );
- $hash->{GetFn} = "HP1000_Get";
- $hash->{DefFn} = "HP1000_Define";
- $hash->{UndefFn} = "HP1000_Undefine";
- $hash->{DbLog_splitFn} = "Unit_DbLog_split";
- $hash->{parseParams} = 1;
- $hash->{AttrList} =
- "disable:1,0 disabledForIntervals do_not_notify:1,0 wu_push:1,0 wu_indoorValues:1,0 wu_id wu_password wu_realtime:1,0 wu_dataValues extSrvPush_Url stateReadingsLang:en,de,at,ch,nl,fr,pl webhookFWinstances:sortable-strict,$webhookFWinstance stateReadings stateReadingsFormat:0,1 "
- . $readingFnAttributes;
- my @wu;
- foreach ( keys %HP1000_wuParams ) {
- next unless ( $HP1000_pwsMapping{$_} );
- push @wu, $HP1000_pwsMapping{$_};
- }
- $hash->{AttrList} .= " wu_pushValues:multiple," . join( ",", sort @wu );
- # Unit.pm support
- $hash->{readingsDesc} = {
- 'Activity' => { rtype => 'oknok', },
- 'UV' => { rtype => 'uvi', },
- 'UVR' => { rtype => 'uwpscm', },
- 'UVcondition' => { rtype => 'condition_uvi', },
- 'UVcondition_rgb' => { rtype => 'rgbhex', },
- 'battery' => { rtype => 'oknok', },
- 'condition' => { rtype => 'condition_weather', },
- 'daylight' => { rtype => 'yesno', },
- 'dewpoint' => { rtype => 'c', formula_symbol => 'Td', },
- 'dewpoint_f' => { rtype => 'f', formula_symbol => 'Td', },
- 'extsrv_state' => { rtype => 'oknok', },
- 'humidity' => { rtype => 'pct', formula_symbol => 'H', },
- 'humidityAbs' => { rtype => 'c', formula_symbol => 'Tabs', },
- 'humidityAbs_f' => { rtype => 'f', formula_symbol => 'Tabs', },
- 'humidityCondition' => { rtype => 'condition_hum', },
- 'humidityCondition_rgb' => { rtype => 'rgbhex', },
- 'indoorDewpoint' => { rtype => 'c', formula_symbol => 'Tdi', },
- 'indoorDewpoint_f' => { rtype => 'f', formula_symbol => 'Tdi', },
- 'indoorHumidity' => { rtype => 'pct', formula_symbol => 'Hi', },
- 'indoorHumidityAbs' => { rtype => 'c', formula_symbol => 'Tabsi', },
- 'indoorHumidityAbs_f' => { rtype => 'f', formula_symbol => 'Tabsi', },
- 'indoorHumidityCondition' => { rtype => 'condition_hum', },
- 'indoorHumidityCondition_rgb' => { rtype => 'rgbhex', },
- 'indoorTemperature' => { rtype => 'c', formula_symbol => 'Ti', },
- 'indoorTemperature_f' => { rtype => 'f', formula_symbol => 'Ti', },
- #'indoorTemperatureCondition' => {},
- 'indoorTemperatureCondition_rgb' => { rtype => 'rgbhex', },
- 'israining' => { rtype => 'yesno', },
- 'luminosity' => { rtype => 'lx', },
- 'pressure' => { rtype => 'hpamb', },
- 'pressureAbs' => { rtype => 'hpamb', },
- 'pressureAbs_in' => { rtype => 'inhg', },
- 'pressureAbs_mm' => { rtype => 'mmhg', },
- 'pressure_in' => { rtype => 'inhg', },
- 'pressure_mm' => { rtype => 'mmhg', },
- 'rain' => { rtype => 'mm', },
- 'rain_day' => { rtype => 'mm', },
- 'rain_day_in' => { rtype => 'in', },
- 'rain_in' => { rtype => 'in', },
- 'rain_month' => { rtype => 'mm', },
- 'rain_month_in' => { rtype => 'in', },
- 'rain_week' => { rtype => 'mm', },
- 'rain_week_in' => { rtype => 'in', },
- 'rain_year' => { rtype => 'mm', },
- 'rain_year_in' => { rtype => 'in', },
- 'solarradiation' => { rtype => 'wpsm', },
- 'temperature' => { rtype => 'c', },
- 'temperature_f' => { rtype => 'f', },
- #'temperatureCondition' => {},
- 'temperatureCondition_rgb' => { rtype => 'rgbhex', },
- #'windCondition' => {},
- 'windCondition_rgb' => { rtype => 'rgbhex', },
- #'windWarning' => {},
- 'wind_compasspoint' => { rtype => 'compasspoint', },
- 'wind_compasspoint_avg2m' => { rtype => 'compasspoint', },
- 'wind_compasspoint_avg10m' => { rtype => 'compasspoint', },
- 'wind_chill' => { rtype => 'c', formula_symbol => 'Wc', },
- 'wind_chill_f' => { rtype => 'f', formula_symbol => 'Wc', },
- 'wind_direction' =>
- { rtype => 'compasspoint', formula_symbol => 'Wdir', },
- 'wind_direction_avg2m' =>
- { rtype => 'compasspoint', formula_symbol => 'Wdir', },
- 'wind_gust' => { rtype => 'kmph', formula_symbol => 'Wg', },
- 'wind_gust_bft' => { rtype => 'bft', formula_symbol => 'Wg', },
- 'wind_gust_direction_avg10m' =>
- { rtype => 'compasspoint', formula_symbol => 'Wdir', },
- 'wind_gust_fts' => { rtype => 'fts', formula_symbol => 'Wg', },
- 'wind_gust_kn' => { rtype => 'kn', formula_symbol => 'Wg', },
- 'wind_gust_mph' => { rtype => 'mph', formula_symbol => 'Wg', },
- 'wind_gust_mph_max10m' => { rtype => 'mph', formula_symbol => 'Wg', },
- 'wind_gust_mps' => { rtype => 'mps', formula_symbol => 'Wg', },
- 'wind_gust_max10m' => { rtype => 'kmph', formula_symbol => 'Wg', },
- 'wind_speed' => { rtype => 'kmph', formula_symbol => 'W', },
- 'wind_speed_avg2m' => { rtype => 'kmph', formula_symbol => 'W', },
- 'wind_speed_bft' => { rtype => 'bft', formula_symbol => 'W', },
- 'wind_speed_bft_avg2m' => { rtype => 'bft', formula_symbol => 'W', },
- 'wind_speed_kn' => { rtype => 'kn', formula_symbol => 'W', },
- 'wind_speed_kn_avg2m' => { rtype => 'kn', formula_symbol => 'W', },
- 'wind_speed_mph' => { rtype => 'mph', formula_symbol => 'W', },
- 'wind_speed_mph_avg2m' => { rtype => 'mph', formula_symbol => 'W', },
- 'wind_speed_mps' => { rtype => 'mps', formula_symbol => 'W', },
- 'wind_speed_mps_avg2m' => { rtype => 'mps', formula_symbol => 'W', },
- 'wu_state' => { rtype => 'oknok', },
- };
- # 98_powerMap.pm support
- $hash->{powerMap} = {
- rname_E => 'energy',
- rname_P => 'consumption',
- map => {
- Activity => {
- 'dead' => 0,
- 'alive' => 5,
- },
- state => {
- '*' => 'Activity',
- },
- },
- };
- }
- # regular Fn ##################################################################
- sub HP1000_Define($$$) {
- my ( $hash, $a, $h ) = @_;
- my $name = $hash->{NAME};
- return "Usage: define <name> HP1000 [<ID> <PASSWORD>]"
- if ( int(@$a) < 2 );
- $hash->{ID} = @$a[2] if ( defined( @$a[2] ) );
- $hash->{PASSWORD} = @$a[3] if ( defined( @$a[3] ) );
- return
- "Device already defined: "
- . $modules{HP1000}{defptr}{NAME}
- . " (there can only be one instance as per restriction of the weather station itself)"
- if ( defined( $modules{HP1000}{defptr} ) && !defined( $hash->{OLDDEF} ) );
- # check FHEMWEB instance when user first defines the device
- if ( $init_done && !defined( $hash->{OLDDEF} ) ) {
- my $FWports;
- foreach ( devspec2array('TYPE=FHEMWEB:FILTER=TEMPORARY!=1') ) {
- $hash->{FW} = $_
- if ( AttrVal( $_, "webname", "fhem" ) eq "weatherstation" );
- }
- if ( !defined( $hash->{FW} ) ) {
- $hash->{FW} = "WEBweatherstation";
- my $port = 8084;
- until ( !grep ( /^$port$/, @{$FWports} ) ) {
- $port++;
- }
- if ( !defined( $defs{ $hash->{FW} } ) ) {
- Log3 $name, 3,
- "HP1000 $name: Creating new FHEMWEB instance "
- . $hash->{FW}
- . " with webname 'weatherstation'";
- fhem "define " . $hash->{FW} . " FHEMWEB $port global";
- fhem "attr " . $hash->{FW} . " closeConn 1";
- fhem "attr " . $hash->{FW} . " webname weatherstation";
- }
- }
- fhem 'attr ' . $name . ' stateReadings temperature humidity';
- fhem 'attr ' . $name . ' stateReadingsFormat 1';
- }
- if ( HP1000_addExtension( $name, "HP1000_CGI", "updateweatherstation" ) ) {
- $hash->{fhem}{infix} = "updateweatherstation";
- }
- else {
- return "Error registering FHEMWEB infix";
- }
- # create global unique device definition
- $modules{HP1000}{defptr} = $hash;
- RemoveInternalTimer($hash);
- InternalTimer( gettimeofday() + 120, "HP1000_SetAliveState", $hash, 0 );
- return undef;
- }
- sub HP1000_Undefine($$$) {
- my ( $hash, $a, $h ) = @_;
- my $name = $hash->{NAME};
- Log3 $name, 5, "HP1000 $name: called function HP1000_Undefine()";
- HP1000_removeExtension( $hash->{fhem}{infix} );
- # release global unique device definition
- delete $modules{HP1000}{defptr};
- RemoveInternalTimer($hash);
- return undef;
- }
- sub HP1000_Get($$$) {
- my ( $hash, $a, $h ) = @_;
- my $name = $hash->{NAME};
- my $wu_id = AttrVal( $name, "wu_id", "" );
- Log3 $name, 5, "HP1000 $name: called function HP1000_Get()";
- return "Argument is missing" if ( int(@$a) < 1 );
- my $usage = "Unknown argument " . @$a[1] . ", choose one of";
- $usage .= " createWUforecast" if ( $wu_id ne "" );
- my $cmd = '';
- my $result;
- # createWUforecast
- if ( lc( @$a[1] ) eq "createwuforecast" ) {
- return "Attribute wu_id "
- . "does not contain a PWS ID to create a Wunderground device"
- if ( $wu_id eq "" );
- my @wudev = devspec2array("TYPE=Wunderground:FILTER=PWS_ID=$wu_id");
- if ( !@wudev ) {
- return "Missing WU API key" if ( !defined( @$a[2] ) );
- Log3 $name, 3, "HP1000 get $name " . @$a[1] . " " . @$a[2];
- $result =
- fhem "define $name" . "_WU Wunderground " . @$a[2] . " $wu_id";
- $result = $name . "_WU created"
- if ( !defined($result) );
- }
- else {
- $result = "Found existing WU device for this PWS ID: $wudev[0]";
- }
- }
- # return usage hint
- else {
- return $usage;
- }
- return $result;
- }
- # module Fn ####################################################################
- sub HP1000_CGI() {
- my ($request) = @_;
- my $hash;
- my $name = "";
- my $link;
- my $URI;
- my $result = "Initialized";
- my $webArgs;
- my $servertype;
- #TODO: should better be blocked in FHEMWEB already
- return ( "text/plain; charset=utf-8", "Booting up" )
- unless ($init_done);
- # data received
- if ( $request =~ /^\/updateweatherstation\.(\w{3})\?(.+=.+)/ ) {
- $servertype = lc($1);
- $URI = $2;
- # get device name
- $name = $data{FWEXT}{"/updateweatherstation"}{deviceName}
- if ( defined( $data{FWEXT}{"/updateweatherstation"} ) );
- $hash = $defs{$name};
- # return error if no such device
- return ( "text/plain; charset=utf-8",
- "No HP1000 device for webhook /updateweatherstation" )
- unless ( IsDevice( $name, 'HP1000' ) );
- # Only allow data via explicitly named FHEMWEB instances,
- # e.g. the user is taking care about correct incoming data
- # routing to that instance via reverse proxy
- $hash->{FW} = AttrVal( $name, "webhookFWinstances", "" );
- # Otherwise, only allow data via FHEMWEB instance with
- # webname 'weatherstation' as hardcoded in weatherstation firmware
- if ( $hash->{FW} eq "" ) {
- foreach ( devspec2array('TYPE=FHEMWEB:FILTER=TEMPORARY!=1') ) {
- if ( AttrVal( $_, "webname", "fhem" ) eq "weatherstation" ) {
- $hash->{FW} = $_;
- last;
- }
- }
- }
- # incorrect FHEMWEB instance used
- my @webhookFWinstances = split( ",", $hash->{FW} );
- return ( "text/plain; charset=utf-8",
- "incorrect FHEMWEB instance to receive data" )
- unless ( grep ( /^$FW_wname$/, @webhookFWinstances ) );
- # extract values from URI
- foreach my $pv ( split( "&", $URI ) ) {
- next if ( $pv eq "" );
- $pv =~ s/\+/ /g;
- $pv =~ s/%([\dA-F][\dA-F])/chr(hex($1))/ige;
- my ( $p, $v ) = split( "=", $pv, 2 );
- $webArgs->{$p} = Encode::encode_utf8($v)
- if ( $v ne "" );
- }
- if ( !defined( $webArgs->{softwaretype} )
- || !defined( $webArgs->{dateutc} )
- || !defined( $webArgs->{action} ) )
- {
- Log3 $name, 5,
- "HP1000: received insufficient data:\n" . Dumper($webArgs);
- return ( "text/plain; charset=utf-8", "Insufficient data" );
- }
- if ( $webArgs->{action} ne "updateraw" ) {
- Log3 $name, 5,
- "HP1000: action $webArgs->{action} is not implemented:\n"
- . Dumper($webArgs);
- return ( "text/plain; charset=utf-8",
- "Action $webArgs->{action} was not implemented" );
- }
- if (
- defined( $hash->{ID} )
- && defined( $hash->{PASSWORD} )
- && ( $hash->{ID} ne $webArgs->{ID}
- || $hash->{PASSWORD} ne $webArgs->{PASSWORD} )
- )
- {
- Log3 $name, 4,
- "HP1000: received data containing wrong credentials:\n"
- . Dumper($webArgs);
- return ( "text/plain; charset=utf-8", "Wrong credentials" );
- }
- }
- # no data received
- else {
- return ( "text/plain; charset=utf-8", "Missing data" );
- }
- my $uptime = time() - $fhem_started;
- delete $hash->{FORECASTDEV} if ( $hash->{FORECASTDEV} );
- my @wudev = devspec2array(
- "TYPE=Wunderground:FILTER=PWS_ID=" . AttrVal( $name, "wu_id", "-" ) );
- $hash->{FORECASTDEV} = $wudev[0]
- if ( defined( $wudev[0] ) );
- HP1000_SetAliveState( $hash, 1 );
- $hash->{IP} = $defs{$FW_cname}{PEER};
- $hash->{SERVER_TYPE} = $servertype;
- $hash->{SWVERSION} = $webArgs->{softwaretype};
- $hash->{INTERVAL} = (
- $hash->{SYSTEMTIME_UTC}
- ? time_str2num( $webArgs->{dateutc} ) -
- time_str2num( $hash->{SYSTEMTIME_UTC} )
- : 0
- );
- $hash->{SYSTEMTIME_UTC} = $webArgs->{dateutc};
- $hash->{UPLOAD_TYPE} = "default";
- $hash->{UPLOAD_TYPE} = "customize"
- if ( defined( $webArgs->{solarradiation} ) );
- Log3 $name, 5,
- "HP1000: received data (uptime=$uptime):\n" . Dumper($webArgs);
- # rename wind speed values as those are in m/sec and
- # we want km/h to be our metric default
- if ( defined( $webArgs->{windspeed} ) ) {
- $webArgs->{windspeedmps} = $webArgs->{windspeed};
- delete $webArgs->{windspeed};
- }
- if ( defined( $webArgs->{windgust} ) ) {
- $webArgs->{windgustmps} = $webArgs->{windgust};
- delete $webArgs->{windgust};
- }
- # Special handling for humidity values
- $webArgs->{inhumi} = $webArgs->{indoorhumidity}
- if ( defined( $webArgs->{indoorhumidity} )
- && !defined( $webArgs->{inhumi} ) );
- $webArgs->{indoorhumidity} = $webArgs->{inhumi}
- if ( defined( $webArgs->{inhumi} )
- && !defined( $webArgs->{indoorhumidity} ) );
- $webArgs->{outhumi} = $webArgs->{humidity}
- if ( defined( $webArgs->{humidity} )
- && !defined( $webArgs->{outhumi} ) );
- $webArgs->{humidity} = $webArgs->{outhumi}
- if ( defined( $webArgs->{outhumi} )
- && !defined( $webArgs->{humidity} ) );
- my %HP1000_pwsMappingEquivalent = (
- # Metric => 'Angloamerican',
- dewpoint => 'dewptf',
- indewpoint => 'indoordewptf',
- humidity => 'outhumi',
- outhumiabs => 'outhumiabsf',
- relbaro => 'baromin',
- absbaro => 'absbaromin',
- indoorhumidity => 'inhumi',
- inhumiabs => 'indoorhumidityabsf',
- rainrate => 'rainin',
- dailyrain => 'dailyrainin',
- weeklyrain => 'weeklyrainin',
- monthlyrain => 'monthlyrainin',
- yearlyrain => 'yearlyrainin',
- outtemp => 'tempf',
- intemp => 'indoortempf',
- windchill => 'windchillf',
- windgustmps => 'windgustmph',
- windspeedmps => 'windspeedmph',
- );
- my %HP1000_pwsMappingEquivalent_rev =
- %{ { reverse %HP1000_pwsMappingEquivalent } };
- # calculate readings for Metric standard from Angloamerican standard
- #
- # calculate Celsius based values based on Fahrenheit
- foreach ( 'dewptf', 'tempf', 'indoortempf', 'windchillf' ) {
- my $k = $HP1000_pwsMappingEquivalent_rev{$_};
- next unless ( $webArgs->{$_} && $k && !defined( $webArgs->{$k} ) );
- $webArgs->{$k} = UConv::f2c( $webArgs->{$_} );
- Log3 $name, 5,
- "HP1000: "
- . "Adding calculated value for $k=$webArgs->{$k} from $_=$webArgs->{$_}";
- }
- # calculate hPa based values based on inHg
- foreach ( 'baromin', 'absbaromin' ) {
- my $k = $HP1000_pwsMappingEquivalent_rev{$_};
- next unless ( $webArgs->{$_} && $k && !defined( $webArgs->{$k} ) );
- $webArgs->{$k} = UConv::inhg2hpa( $webArgs->{$_} );
- Log3 $name, 5,
- "HP1000: "
- . "Adding calculated value for $k=$webArgs->{$k} from $_=$webArgs->{$_}";
- }
- # calculate milimeter based values based on inch
- foreach (
- 'rainin', 'dailyrainin', 'weeklyrainin',
- 'monthlyrainin', 'yearlyrainin'
- )
- {
- my $k = $HP1000_pwsMappingEquivalent_rev{$_};
- next unless ( $webArgs->{$_} && $k && !defined( $webArgs->{$k} ) );
- $webArgs->{$k} = UConv::in2mm( $webArgs->{$_} );
- Log3 $name, 5,
- "HP1000: "
- . "Adding calculated value for $k=$webArgs->{$k} from $_=$webArgs->{$_}";
- }
- # calculate kph based values based on mph
- foreach ( 'windgustmph', 'windspeedmph' ) {
- my $k = $HP1000_pwsMappingEquivalent_rev{$_};
- next unless ( $webArgs->{$_} && $k && !defined( $webArgs->{$k} ) );
- $webArgs->{$k} = UConv::mph2mps( $webArgs->{$_} );
- Log3 $name, 5,
- "HP1000: "
- . "Adding calculated value for $k=$webArgs->{$k} from $_=$webArgs->{$_}";
- }
- # windgust in km/h (convert from windgustmps)
- if ( defined( $webArgs->{windgustmps} )
- && !defined( $webArgs->{windgust} ) )
- {
- $webArgs->{windgust} =
- UConv::mps2kph( $webArgs->{windgustmps} );
- Log3 $name, 5,
- "HP1000: "
- . "Adding calculated value for windgust=$webArgs->{windgust} from windgustmps=$webArgs->{windgustmps}";
- }
- # windspeed in km/h (convert from windspeedmps)
- if ( defined( $webArgs->{windspeedmps} )
- && !defined( $webArgs->{windspeed} ) )
- {
- Log3 $name, 5,
- "HP1000: Adding calculated value for windspeed from windspeedmps";
- $webArgs->{windspeed} =
- UConv::mps2kph( $webArgs->{windspeedmps} );
- }
- # windgust in km/h (convert from windgustmph)
- if ( defined( $webArgs->{windgustmph} )
- && !defined( $webArgs->{windgust} ) )
- {
- Log3 $name, 5,
- "HP1000: Adding calculated value for windgust from windgustmph";
- $webArgs->{windgust} =
- UConv::mph2kph( $webArgs->{windgustmph} );
- }
- # windspeed in km/h (convert from windspeedmph)
- if ( defined( $webArgs->{windspeedmph} )
- && !defined( $webArgs->{windspeed} ) )
- {
- Log3 $name, 5,
- "HP1000: Adding calculated value for windspeed from windspeedmph";
- $webArgs->{windspeed} =
- UConv::mps2kph( $webArgs->{windspeedmph} );
- }
- # calculate readings for Angloamerican standard from Metric standard
- #
- # calculate Fahrenheit based values based on Celsius
- foreach ( 'dewpoint', 'outtemp', 'intemp', 'windchill' ) {
- my $k = $HP1000_pwsMappingEquivalent{$_};
- next unless ( $webArgs->{$_} && $k && !defined( $webArgs->{$k} ) );
- Log3 $name, 5, "HP1000: Adding calculated value for $k from $_";
- $webArgs->{$k} = UConv::c2f( $webArgs->{$_} );
- }
- # calculate inHg based values based on hPa
- foreach ( 'relbaro', 'absbaro' ) {
- my $k = $HP1000_pwsMappingEquivalent{$_};
- next unless ( $webArgs->{$_} && $k && !defined( $webArgs->{$k} ) );
- Log3 $name, 5, "HP1000: Adding calculated value for $k from $_";
- $webArgs->{$k} = UConv::hpa2inhg( $webArgs->{$_}, 2 );
- }
- # calculate inch based values based on milimeter
- foreach ( 'rainrate', 'dailyrain', 'weeklyrain', 'monthlyrain',
- 'yearlyrain' )
- {
- my $k = $HP1000_pwsMappingEquivalent{$_};
- next unless ( $webArgs->{$_} && $k && !defined( $webArgs->{$k} ) );
- Log3 $name, 5, "HP1000: Adding calculated value for $k from $_";
- $webArgs->{$k} = UConv::mm2in( $webArgs->{$_}, 2 );
- }
- # calculate kph based values based on mph
- foreach ( 'windgustmps', 'windspeedmps' ) {
- my $k = $HP1000_pwsMappingEquivalent{$_};
- next unless ( $webArgs->{$_} && $k && !defined( $webArgs->{$k} ) );
- Log3 $name, 5, "HP1000: Adding calculated value for $k from $_";
- $webArgs->{$k} = UConv::mps2mph( $webArgs->{$_} );
- }
- # windgustmph in mph (convert from windgustmps)
- if ( defined( $webArgs->{windgustmps} )
- && !defined( $webArgs->{windgustmph} ) )
- {
- Log3 $name, 5,
- "HP1000: Adding calculated value for windgustmph from windgustmps";
- $webArgs->{windgustmph} =
- UConv::mps2mph( $webArgs->{windgustmps} );
- }
- # windspeedmph in mph (convert from windspeedmps)
- if ( defined( $webArgs->{windspeedmps} )
- && !defined( $webArgs->{windspeedmph} ) )
- {
- Log3 $name, 5,
- "HP1000: Adding calculated value for windspeedmph from windspeedmps";
- $webArgs->{windspeedmph} =
- UConv::mps2mph( $webArgs->{windspeedmps} );
- }
- # write general readings
- #
- readingsBeginUpdate($hash);
- while ( ( my $p, my $v ) = each %$webArgs ) {
- # ignore those values
- next
- if ( $p eq "action"
- || $p eq "dateutc"
- || $p eq "lowbatt"
- || $p eq "realtime"
- || $p eq "rtfreq"
- || $p eq "softwaretype"
- || $p eq "humidity"
- || $p eq "indoorhumidity"
- || $p eq "UV"
- || $p eq "UVR"
- || $p eq "ID"
- || $p eq "PASSWORD" );
- $p = $HP1000_pwsMapping{$p} ? $HP1000_pwsMapping{$p} : "_" . $p;
- readingsBulkUpdate( $hash, $p, $v );
- }
- # calculate additional readings
- #
- # battery
- my $battery = "ok";
- if ( defined( $webArgs->{lowbatt} ) ) {
- $battery = "low" if ( $webArgs->{lowbatt} );
- readingsBulkUpdateIfChanged( $hash, "battery", $battery );
- }
- # israining
- my $israining = 0;
- $israining = 1
- if ( defined( $webArgs->{rainrate} ) && $webArgs->{rainrate} > 0 );
- readingsBulkUpdateIfChanged( $hash, "israining", $israining );
- # solarradiation in W/m2 (convert from lux)
- if ( defined( $webArgs->{light} )
- && !defined( $webArgs->{solarradiation} ) )
- {
- $webArgs->{solarradiation} =
- UConv::lux2wpsm( $webArgs->{light}, 2 );
- readingsBulkUpdate( $hash, "solarradiation",
- $webArgs->{solarradiation} );
- }
- # luminosity in lux (convert from W/m2)
- if ( defined( $webArgs->{solarradiation} )
- && !defined( $webArgs->{light} ) )
- {
- $webArgs->{light} =
- UConv::wpsm2lux( $webArgs->{solarradiation} );
- readingsBulkUpdate( $hash, "luminosity", $webArgs->{light} );
- }
- # daylight
- my $daylight = 0;
- $daylight = 1
- if ( defined( $webArgs->{light} ) && $webArgs->{light} > 50 );
- readingsBulkUpdateIfChanged( $hash, "daylight", $daylight );
- # condition
- if ( defined( $webArgs->{light} ) ) {
- my $temp =
- ( defined( $webArgs->{outtemp} ) ? $webArgs->{outtemp} : "10" );
- my $hum = ( $webArgs->{outhumi} ? $webArgs->{outhumi} : "50" );
- readingsBulkUpdateIfChanged(
- $hash,
- "condition",
- UConv::values2weathercondition(
- $temp, $hum, $webArgs->{light}, $daylight, $israining
- )
- );
- }
- # temperatureCondition
- if ( defined( $webArgs->{windchill} ) ) {
- my $avg =
- HP1000_GetAvg( $hash, "windchill", 600, $webArgs->{windchill} );
- if ( $hash->{INTERVAL} > 0 ) {
- my ( $cond, $rgb ) = UConv::c2condition($avg);
- readingsBulkUpdateIfChanged( $hash, "temperatureCondition", $cond );
- readingsBulkUpdateIfChanged( $hash, "temperatureCondition_rgb",
- $rgb );
- }
- }
- # indoorTemperatureCondition
- if ( defined( $webArgs->{intemp} ) ) {
- my ( $v, $rgb ) = UConv::c2condition( $webArgs->{intemp}, 1 );
- readingsBulkUpdateIfChanged( $hash, "indoorTemperatureCondition", $v );
- readingsBulkUpdateIfChanged( $hash, "indoorTemperatureCondition_rgb",
- $rgb );
- }
- # humidityCondition
- if ( defined( $webArgs->{outhumi} ) ) {
- my ( $v, $rgb ) = UConv::humidity2condition( $webArgs->{outhumi} );
- readingsBulkUpdateIfChanged( $hash, "humidityCondition", $v );
- readingsBulkUpdateIfChanged( $hash, "humidityCondition_rgb", $rgb );
- }
- # indoorHumidityCondition
- if ( defined( $webArgs->{inhumi} ) ) {
- my ( $v, $rgb ) = UConv::humidity2condition( $webArgs->{inhumi}, 1 );
- readingsBulkUpdateIfChanged( $hash, "indoorHumidityCondition", $v );
- readingsBulkUpdateIfChanged( $hash, "indoorHumidityCondition_rgb",
- $rgb );
- }
- if ( defined( $webArgs->{UV} ) ) {
- # UV is already in UV-index format when upload-type
- # is set to 'customize'. Wunderground format is 'customize'.
- if ( $hash->{UPLOAD_TYPE} eq "customize" ) {
- $webArgs->{UVR} = UConv::uvi2uwpscm( $webArgs->{UV} )
- unless ( defined( $webArgs->{UVR} ) );
- }
- else {
- $webArgs->{UVR} = $webArgs->{UV};
- $webArgs->{UV} = UConv::uwpscm2uvi( $webArgs->{UVR} );
- }
- readingsBulkUpdate( $hash, "UV", $webArgs->{UV} );
- readingsBulkUpdate( $hash, "UVR", $webArgs->{UVR} );
- # UVcondition
- my ( $v, $rgb ) = UConv::uvi2condition( $webArgs->{UV} );
- readingsBulkUpdateIfChanged( $hash, "UVcondition", $v );
- readingsBulkUpdateIfChanged( $hash, "UVcondition_rgb", $rgb );
- }
- # pressure_mm in mmHg (convert from hpa)
- if ( defined( $webArgs->{relbaro} ) ) {
- $webArgs->{barommm} = UConv::hpa2mmhg( $webArgs->{relbaro} );
- readingsBulkUpdate( $hash, "pressure_mm", $webArgs->{barommm} );
- }
- # pressureAbs_mm in mmHg (convert from hpa)
- if ( defined( $webArgs->{absbaro} ) ) {
- $webArgs->{absbarommm} =
- UConv::hpa2mmhg( $webArgs->{absbaro} );
- readingsBulkUpdate( $hash, "pressureAbs_mm", $webArgs->{absbarommm} );
- }
- # indoorDewpoint in Celsius
- if ( defined( $webArgs->{intemp} )
- && defined( $webArgs->{inhumi} )
- && exists &dewpoint_dewpoint )
- {
- my $h = (
- $webArgs->{inhumi} > 110
- ? 110
- : ( $webArgs->{inhumi} <= 0 ? 0.01 : $webArgs->{inhumi} )
- );
- $webArgs->{indewpoint} =
- round( dewpoint_dewpoint( $webArgs->{intemp}, $h ), 1 );
- readingsBulkUpdate( $hash, "indoorDewpoint", $webArgs->{indewpoint} );
- }
- # indoorDewpoint in Fahrenheit
- if ( defined( $webArgs->{indoortempf} )
- && defined( $webArgs->{indoorhumidity} )
- && exists &dewpoint_dewpoint )
- {
- my $h = (
- $webArgs->{indoorhumidity} > 110 ? 110
- : (
- $webArgs->{indoorhumidity} <= 0 ? 0.01
- : $webArgs->{indoorhumidity}
- )
- );
- $webArgs->{indoordewptf} =
- round( dewpoint_dewpoint( $webArgs->{indoortempf}, $h ), 1 );
- readingsBulkUpdate( $hash, "indoorDewpoint_f",
- $webArgs->{indoordewptf} );
- }
- # humidityAbs
- if ( defined( $webArgs->{outtemp} )
- && defined( $webArgs->{outhumi} )
- && looks_like_number( $webArgs->{outtemp} )
- && looks_like_number( $webArgs->{outhumi} )
- && exists &dewpoint_absFeuchte )
- {
- my $h = (
- $webArgs->{outhumi} > 110
- ? 110
- : ( $webArgs->{outhumi} <= 0 ? 0.01 : $webArgs->{outhumi} )
- );
- $webArgs->{outhumiabs} =
- round( dewpoint_absFeuchte( $webArgs->{outtemp}, $h ), 1 );
- readingsBulkUpdate( $hash, "humidityAbs", $webArgs->{outhumiabs} );
- }
- # humidityAbs_f
- if ( defined( $webArgs->{tempf} )
- && defined( $webArgs->{outhumi} )
- && looks_like_number( $webArgs->{tempf} )
- && looks_like_number( $webArgs->{outhumi} )
- && exists &dewpoint_absFeuchte )
- {
- my $h = (
- $webArgs->{outhumi} > 110
- ? 110
- : ( $webArgs->{outhumi} <= 0 ? 0.01 : $webArgs->{outhumi} )
- );
- $webArgs->{outhumiabsf} =
- round( dewpoint_absFeuchte( $webArgs->{tempf}, $h ), 1 );
- readingsBulkUpdate( $hash, "humidityAbs_f", $webArgs->{outhumiabsf} );
- }
- # indoorHumidityAbs
- if ( defined( $webArgs->{intemp} )
- && defined( $webArgs->{inhumi} )
- && looks_like_number( $webArgs->{intemp} )
- && looks_like_number( $webArgs->{inhumi} )
- && exists &dewpoint_absFeuchte )
- {
- my $h = (
- $webArgs->{inhumi} > 110
- ? 110
- : ( $webArgs->{inhumi} <= 0 ? 0.01 : $webArgs->{inhumi} )
- );
- $webArgs->{inhumiabs} =
- round( dewpoint_absFeuchte( $webArgs->{intemp}, $h ), 1 );
- readingsBulkUpdate( $hash, "indoorHumidityAbs", $webArgs->{inhumiabs} );
- }
- # indoorHumidityAbs_f
- if ( defined( $webArgs->{indoortempf} )
- && defined( $webArgs->{indoorhumidity} )
- && looks_like_number( $webArgs->{indoortempf} )
- && looks_like_number( $webArgs->{indoorhumidity} )
- && exists &dewpoint_absFeuchte )
- {
- my $h = (
- $webArgs->{indoorhumidity} > 110 ? 110
- : (
- $webArgs->{indoorhumidity} <= 0 ? 0.01
- : $webArgs->{indoorhumidity}
- )
- );
- $webArgs->{indoorhumidityabsf} =
- round( dewpoint_absFeuchte( $webArgs->{indoortempf}, $h ), 1 );
- readingsBulkUpdate( $hash, "indoorHumidityAbs_f",
- $webArgs->{indoorhumidityabsf} );
- }
- # wind_compasspoint
- if ( defined( $webArgs->{winddir} ) ) {
- $webArgs->{windcomp} =
- UConv::direction2compasspoint( $webArgs->{winddir} );
- readingsBulkUpdate( $hash, "wind_compasspoint", $webArgs->{windcomp} );
- }
- # wind_speed_bft in Beaufort (convert from km/h)
- if ( defined( $webArgs->{windspeed} ) ) {
- $webArgs->{windspeedbft} =
- UConv::kph2bft( $webArgs->{windspeed} );
- readingsBulkUpdate( $hash, "wind_speed_bft", $webArgs->{windspeedbft} );
- }
- # wind_speed_kn in kn (convert from km/h)
- if ( defined( $webArgs->{windspeed} ) ) {
- my $v = UConv::kph2kn( $webArgs->{windspeed} );
- $webArgs->{windspeedkn} = ( $v > 0.5 ? round( $v, 1 ) : "0.0" );
- readingsBulkUpdate( $hash, "wind_speed_kn", $webArgs->{windspeedkn} );
- }
- # wind_speed_fts in ft/s (convert from mph)
- if ( defined( $webArgs->{windspeedmph} ) ) {
- my $v = UConv::mph2fts( $webArgs->{windspeedmph} );
- $webArgs->{windspeedfts} = ( $v > 0.5 ? round( $v, 1 ) : "0.0" );
- readingsBulkUpdate( $hash, "wind_speed_fts", $webArgs->{windspeedfts} );
- }
- # wind_gust_bft in Beaufort (convert from km/h)
- if ( defined( $webArgs->{windgust} ) ) {
- $webArgs->{windgustbft} =
- UConv::kph2bft( $webArgs->{windgust} );
- readingsBulkUpdate( $hash, "wind_gust_bft", $webArgs->{windgustbft} );
- }
- # wind_gust_kn in m/s (convert from km/h)
- if ( defined( $webArgs->{windgust} ) ) {
- my $v = UConv::kph2kn( $webArgs->{windgust} );
- $webArgs->{windgustkn} = ( $v > 0.5 ? round( $v, 1 ) : "0.0" );
- readingsBulkUpdate( $hash, "wind_gust_kn", $webArgs->{windgustkn} );
- }
- # wind_gust_fts ft/s (convert from mph)
- if ( defined( $webArgs->{windgustmph} ) ) {
- my $v = UConv::mph2fts( $webArgs->{windgustmph} );
- $webArgs->{windgustfts} = ( $v > 0.5 ? round( $v, 1 ) : "0.0" );
- readingsBulkUpdate( $hash, "wind_gust_fts", $webArgs->{windgustfts} );
- }
- # averages/wind_direction_avg10m
- # averages/wind_direction_avg2m
- if ( defined( $webArgs->{winddir} ) ) {
- my $v =
- round( HP1000_GetAvg( $hash, "winddir", 600, $webArgs->{winddir} ),
- 0 );
- if ( $hash->{INTERVAL} > 0 && $uptime >= 600 ) {
- $webArgs->{windgustdir_10m} = $v;
- readingsBulkUpdateIfChanged( $hash,
- "wind_gust_direction_avg10m", $webArgs->{windgustdir_10m} );
- }
- if ( $hash->{INTERVAL} > 0 && $uptime >= 120 ) {
- my $v2 = round( HP1000_GetAvg( $hash, "winddir", 120 ), 0 );
- $webArgs->{winddir_avg2m} = $v2;
- readingsBulkUpdateIfChanged( $hash, "wind_direction_avg2m",
- $webArgs->{winddir_avg2m} );
- }
- }
- # averages/wind_compasspoint_avg10m
- if ( defined( $webArgs->{windgustdir_10m} ) ) {
- $webArgs->{windcomp_avg10m} =
- UConv::direction2compasspoint( $webArgs->{windgustdir_10m} );
- readingsBulkUpdateIfChanged( $hash, "wind_compasspoint_avg10m",
- $webArgs->{windcomp_avg10m} );
- }
- # averages/wind_compasspoint_avg2m
- if ( defined( $webArgs->{winddir_avg2m} ) ) {
- $webArgs->{windcomp_avg2m} =
- UConv::direction2compasspoint( $webArgs->{winddir_avg2m} );
- readingsBulkUpdateIfChanged( $hash, "wind_compasspoint_avg2m",
- $webArgs->{windcomp_avg2m} );
- }
- # averages/wind_speed_avg2m in km/h
- if ( defined( $webArgs->{windspeed} ) ) {
- my $v = HP1000_GetAvg( $hash, "windspeed", 120, $webArgs->{windspeed} );
- if ( $hash->{INTERVAL} > 0 && $uptime >= 120 ) {
- $webArgs->{windspeed_avg2m} = $v;
- readingsBulkUpdateIfChanged( $hash, "wind_speed_avg2m",
- $webArgs->{windspeed_avg2m} );
- }
- }
- # averages/wind_speed_mph_avg2m in mph
- if ( defined( $webArgs->{windspeedmph} ) ) {
- my $v =
- HP1000_GetAvg( $hash, "windspeedmph", 120, $webArgs->{windspeedmph} );
- if ( $hash->{INTERVAL} > 0 && $uptime >= 120 ) {
- $webArgs->{windspdmph_avg2m} = $v;
- readingsBulkUpdateIfChanged( $hash, "wind_speed_mph_avg2m",
- $webArgs->{windspdmph_avg2m} );
- }
- }
- # averages/wind_speed_bft_avg2m in Beaufort (convert from km/h)
- if ( defined( $webArgs->{windspeed_avg2m} ) ) {
- $webArgs->{windspdbft_avg2m} =
- UConv::kph2bft( $webArgs->{windspeed_avg2m} );
- readingsBulkUpdateIfChanged( $hash, "wind_speed_bft_avg2m",
- $webArgs->{windspdbft_avg2m} );
- }
- # averages/wind_speed_kn_avg2m in Kn (convert from km/h)
- if ( defined( $webArgs->{windspeed_avg2m} ) ) {
- $webArgs->{windspdkn_avg2m} =
- UConv::kph2kn( $webArgs->{windspeed_avg2m} );
- readingsBulkUpdateIfChanged( $hash, "wind_speed_kn_avg2m",
- $webArgs->{windspdkn_avg2m} );
- }
- # averages/wind_speed_mps_avg2m in m/s
- if ( defined( $webArgs->{windspeed_avg2m} ) ) {
- my $v = UConv::kph2mps( $webArgs->{windspeed_avg2m} );
- $webArgs->{windspdmps_avg2m} =
- ( $v > 0.5 ? round( $v, 1 ) : "0.0" );
- readingsBulkUpdateIfChanged( $hash, "wind_speed_mps_avg2m",
- $webArgs->{windspdmps_avg2m} );
- }
- # maximum/wind_gust_max10m
- if ( defined( $webArgs->{windgust} ) ) {
- my $v = HP1000_GetMax( $hash, "windgust", 600, $webArgs->{windgust} );
- if ( $hash->{INTERVAL} > 0 && $uptime >= 600 ) {
- $webArgs->{windgust_10m} = $v;
- readingsBulkUpdateIfChanged( $hash, "wind_gust_max10m",
- $webArgs->{windgust_10m} );
- my ( $val, $rgb, $cond, $warn ) =
- UConv::kph2bft( $webArgs->{windgust_10m} );
- readingsBulkUpdateIfChanged( $hash, "windCondition", $cond );
- readingsBulkUpdateIfChanged( $hash, "windCondition_rgb", $rgb );
- readingsBulkUpdateIfChanged( $hash, "windWarning", $warn );
- }
- }
- # maximum/wind_gust_mph_max10m
- if ( defined( $webArgs->{windgustmph} ) ) {
- my $v =
- HP1000_GetMax( $hash, "windgustmph", 600, $webArgs->{windgustmph} );
- if ( $hash->{INTERVAL} > 0 && $uptime >= 600 ) {
- $webArgs->{windgustmph_10m} = $v;
- readingsBulkUpdateIfChanged( $hash, "wind_gust_mph_max10m",
- $webArgs->{windgustmph_10m} );
- }
- }
- # from WU API - can we somehow calculate these as well?
- # weather - [text] -- metar style (+RA)
- # clouds - [text] -- SKC, FEW, SCT, BKN, OVC
- # soiltempf - [F soil temperature]
- # soilmoisture - [%]
- # leafwetness - [%]
- # visibility - [nm visibility]
- # condition_forecast (based on pressure trend)
- # dayNight
- # soilTemperature
- # brightness in % ??
- # state
- my $stateReadings = AttrVal( $name, "stateReadings", "" );
- my $stateReadingsLang = AttrVal( $name, "stateReadingsLang", "en" );
- my $stateReadingsFormat = AttrVal( $name, "stateReadingsFormat", "0" );
- # $result =
- # makeSTATE( $name, $stateReadings,
- # $stateReadingsLang, $stateReadingsFormat );
- $result = makeSTATE( $name, $stateReadings, $stateReadingsFormat );
- readingsBulkUpdate( $hash, "state", $result );
- readingsEndUpdate( $hash, 1 );
- HP1000_PushWU( $hash, $webArgs )
- if AttrVal( $name, "wu_push", 0 ) eq "1";
- HP1000_PushSrv( $hash, $webArgs )
- if AttrVal( $name, "extSrvPush_Url", undef );
- return ( "text/plain; charset=utf-8", "success" );
- }
- sub HP1000_addExtension($$$) {
- my ( $name, $func, $link ) = @_;
- my $url = "/$link";
- Log3 $name, 2, "Registering HP1000 $name for URL $url...";
- $data{FWEXT}{$url}{deviceName} = $name;
- $data{FWEXT}{$url}{FUNC} = $func;
- $data{FWEXT}{$url}{LINK} = $link;
- }
- sub HP1000_removeExtension($) {
- my ($link) = @_;
- my $url = "/$link";
- my $name = $data{FWEXT}{$url}{deviceName};
- Log3 $name, 2, "Unregistering HP1000 $name for URL $url...";
- delete $data{FWEXT}{$url};
- }
- sub HP1000_SetAliveState($;$) {
- my ( $hash, $alive ) = @_;
- my $name = $hash->{NAME};
- Log3 $name, 5, "HP1000 $name: called function HP1000_SetAliveState()";
- RemoveInternalTimer($hash);
- my $activity = "dead";
- $activity = "alive" if ($alive);
- readingsBeginUpdate($hash);
- readingsBulkUpdateIfChanged( $hash, "Activity", $activity );
- readingsEndUpdate( $hash, 1 );
- InternalTimer( gettimeofday() + 120, "HP1000_SetAliveState", $hash, 0 );
- return;
- }
- sub HP1000_PushSrv($$) {
- my ( $hash, $webArgs ) = @_;
- my $name = $hash->{NAME};
- my $timeout = AttrVal( $name, "timeout", 7 );
- my $http_noshutdown = AttrVal( $name, "http-noshutdown", "1" );
- my $srv_url = AttrVal( $name, "extSrvPush_Url", "" );
- my $cmd = "";
- Log3 $name, 5, "HP1000 $name: called function HP1000_PushSrv()";
- $srv_url =~ s/\$SERVER_TYPE/$hash->{SERVER_TYPE}/g
- if ( $hash->{SERVER_TYPE} );
- if ( $srv_url !~
- m/(https?):\/\/([\w\.]+):?(\d+)?([a-zA-Z0-9\~\!\@\#\$\%\^\&\*\(\)_\-\=\+\\\/\?\.\:\;\'\,]*)?/
- )
- {
- return;
- }
- elsif ( $4 !~ /\?/ ) {
- $cmd = "?";
- }
- else {
- $cmd = "&";
- }
- $webArgs->{PASSWORD} = "";
- while ( my ( $key, $value ) = each %{$webArgs} ) {
- if ( $key eq "softwaretype" || $key eq "dateutc" ) {
- $value = urlEncode($value);
- }
- $cmd .= "$key=" . $value . "&";
- }
- Log3 $name, 4,
- "HP1000 $name: pushing data to external Server: $srv_url$cmd";
- HttpUtils_NonblockingGet(
- {
- url => $srv_url . $cmd,
- timeout => $timeout,
- noshutdown => $http_noshutdown,
- data => undef,
- hash => $hash,
- callback => \&HP1000_ReturnSrv,
- httpversion => "1.1",
- loglevel => AttrVal( $name, "httpLoglevel", 5 ),
- header => {
- Agent => 'FHEM-HP1000/1.0.0',
- 'User-Agent' => 'FHEM-HP1000/1.0.0',
- },
- sslargs => {
- SSL_verify_mode => 'SSL_VERIFY_NONE',
- },
- }
- );
- return;
- }
- sub HP1000_PushWU($$) {
- #
- # See: http://wiki.wunderground.com/index.php/PWS_-_Upload_Protocol
- #
- my ( $hash, $webArgs ) = @_;
- my $name = $hash->{NAME};
- my $timeout = AttrVal( $name, "timeout", 7 );
- my $http_noshutdown = AttrVal( $name, "http-noshutdown", "1" );
- my $wu_user = AttrVal( $name, "wu_id", "" );
- my $wu_pass = AttrVal( $name, "wu_password", "" );
- my $wu_realtime = AttrVal( $name, "wu_realtime", undef );
- my $wu_indoorValues = AttrVal( $name, "wu_indoorValues", 1 );
- my $wu_dataValues = AttrVal( $name, "wu_dataValues", undef );
- my $wu_pushValues = AttrVal( $name, "wu_pushValues", undef );
- my @whitelist =
- ( 'action', 'dateutc', 'ID', 'PASSWORD', 'rtfreq', 'softwaretype' );
- Log3 $name, 5, "HP1000 $name: called function HP1000_PushWU()";
- if ( $wu_user eq "" && $wu_pass eq "" ) {
- Log3 $name, 4,
- "HP1000 $name: "
- . "missing attributes for Weather Underground transfer: wu_user and wu_password";
- my $return = "error: missing attributes wu_user and wu_password";
- readingsBeginUpdate($hash);
- readingsBulkUpdateIfChanged( $hash, "wu_state", $return );
- readingsEndUpdate( $hash, 1 );
- return;
- }
- if ( defined($wu_realtime) && $wu_realtime eq "0" ) {
- Log3 $name, 5, "HP1000 $name: Explicitly turning off realtime";
- delete $webArgs->{realtime};
- delete $webArgs->{rtfreq};
- }
- elsif ($wu_realtime) {
- Log3 $name, 5, "HP1000 $name: Explicitly turning on realtime";
- $webArgs->{realtime} = 1;
- }
- if ( $wu_dataValues || $wu_pushValues ) {
- my %HP1000_pwsMapping_rev = %{ { reverse %HP1000_pwsMapping } };
- if ($wu_dataValues) {
- my %dummy;
- $wu_dataValues =~ s/\$name/$name/g;
- my ( $err, @a ) = ReplaceSetMagic( \%dummy, 0, ($wu_dataValues) );
- if ($err) {
- Log3 $name, 3,
- "HP1000 $name: error parsing wu_dataValues: $err";
- }
- else {
- my ( undef, $h ) = parseParams( \@a );
- foreach ( keys %$h ) {
- next unless $_ ne "";
- my $n = $_;
- if ( $HP1000_pwsMapping_rev{$_} ) {
- $n = $HP1000_pwsMapping_rev{$_};
- Log3 $name, 4,
- "HP1000 $name: Remapping reading name from $_ to $n";
- }
- Log3 $name, 4,
- "HP1000 $name: Adding new value for WU: $n=$h->{$_}"
- unless ( defined( $webArgs->{$n} ) );
- Log3 $name, 4,
- "HP1000 $name: Replacing existing value for WU: $n=$h->{$_}"
- if ( defined( $webArgs->{$n} ) );
- $webArgs->{$n} = $h->{$_};
- }
- }
- }
- if ($wu_pushValues) {
- foreach ( split( /,/, $wu_pushValues ) ) {
- if ( $HP1000_pwsMapping_rev{$_} ) {
- my $v = $HP1000_pwsMapping_rev{$_};
- $v = "humidity" if ( $v eq "outhumi" );
- $v = "indoorhumidity" if ( $v eq "inhumi" );
- push @whitelist, $v;
- }
- else {
- push @whitelist, $_;
- }
- }
- }
- }
- $webArgs->{rtfreq} = 5
- if ( defined( $webArgs->{realtime} )
- && !defined( $webArgs->{rtfreq} ) );
- my $wu_url = (
- defined( $webArgs->{realtime} )
- && $webArgs->{realtime} eq "1"
- ? "https://rtupdate.wunderground.com/weatherstation/updateweatherstation.php?"
- : "https://weatherstation.wunderground.com/weatherstation/updateweatherstation.php?"
- );
- $webArgs->{ID} = $wu_user;
- $webArgs->{PASSWORD} = $wu_pass;
- my $cmd;
- while ( my ( $key, $value ) = each %{$webArgs} ) {
- $value = urlEncode($value)
- if ( $key =~ /^(softwaretype|dateutc)$/i );
- if ( ( $wu_indoorValues || $key =~ m/^in/i )
- && ( !$wu_pushValues || grep ( $_ eq $key, @whitelist ) ) )
- {
- Log3 $name, 4, "HP1000 $name: pushing data to WU: $key=$value";
- $cmd .= "$key=" . $value . "&";
- }
- }
- HttpUtils_NonblockingGet(
- {
- url => $wu_url . $cmd,
- timeout => $timeout,
- noshutdown => $http_noshutdown,
- data => undef,
- hash => $hash,
- callback => \&HP1000_ReturnWU,
- httpversion => "1.1",
- loglevel => AttrVal( $name, "httpLoglevel", 5 ),
- header => {
- Agent => 'FHEM-HP1000/1.0.0',
- 'User-Agent' => 'FHEM-HP1000/1.0.0',
- },
- }
- );
- return;
- }
- sub HP1000_ReturnSrv($$$) {
- my ( $param, $err, $data ) = @_;
- my $hash = $param->{hash};
- my $name = $hash->{NAME};
- # device not reachable
- if ($err) {
- my $return = "error: connection timeout";
- Log3 $name, 4, "HP1000 $name: EXTSRV HTTP " . $return;
- readingsBeginUpdate($hash);
- readingsBulkUpdateIfChanged( $hash, "extsrv_state", $return );
- readingsEndUpdate( $hash, 1 );
- }
- # data received
- elsif ($data) {
- my $logprio = 5;
- my $return = "ok";
- if ( $param->{code} ne "200" ) {
- $logprio = 4;
- $return = "error " . $param->{code} . ": $data";
- }
- Log3 $name, $logprio,
- "HP1000 $name: EXTSRV HTTP return: " . $param->{code} . " - $data";
- readingsBeginUpdate($hash);
- readingsBulkUpdateIfChanged( $hash, "extsrv_state", $return );
- readingsEndUpdate( $hash, 1 );
- }
- return;
- }
- sub HP1000_ReturnWU($$$) {
- my ( $param, $err, $data ) = @_;
- my $hash = $param->{hash};
- my $name = $hash->{NAME};
- # device not reachable
- if ($err) {
- my $return = "error: connection timeout";
- Log3 $name, 4, "HP1000 $name: WU HTTP " . $return;
- readingsBeginUpdate($hash);
- readingsBulkUpdateIfChanged( $hash, "wu_state", $return );
- readingsEndUpdate( $hash, 1 );
- }
- # data received
- elsif ($data) {
- my $logprio = 5;
- my $return = "ok";
- if ( $data !~ m/^success.*/i ) {
- $logprio = 4;
- $return = "error";
- $return .= " " . $param->{code} if ( $param->{code} ne "200" );
- $return .= ": $data";
- }
- Log3 $name, $logprio,
- "HP1000 $name: WU HTTP return: " . $param->{code} . " - $data";
- readingsBeginUpdate($hash);
- readingsBulkUpdateIfChanged( $hash, "wu_state", $return );
- readingsEndUpdate( $hash, 1 );
- }
- return;
- }
- sub HP1000_GetAvg($$;$$) {
- my ( $hash, $t, $s, $v ) = @_;
- return HP1000_HistoryDb( $hash, $t, $s, $v, 1 );
- }
- sub HP1000_GetMax($$;$$) {
- my ( $hash, $t, $s, $v ) = @_;
- return HP1000_HistoryDb( $hash, $t, $s, $v, 2 );
- }
- sub HP1000_HistoryDb($$;$$$) {
- my ( $hash, $t, $s, $v, $type ) = @_;
- my $name = $hash->{NAME};
- return $v if ( $type && $type == 1 && $hash->{INTERVAL} < 1 );
- return "0" if ( $hash->{INTERVAL} < 1 );
- my $historySize = $s ? round( $s / $hash->{INTERVAL}, 0 ) : undef;
- $historySize = "1" if ( defined($historySize) && $historySize < 1 );
- if ( defined($v) && looks_like_number($v) ) {
- my $v2 = unshift @{ $hash->{helper}{history}{$t} }, $v
- unless ( !$type && $v <= 0 );
- my $v3 = splice @{ $hash->{helper}{history}{$t} }, $historySize
- if ($historySize);
- Log3 $name, 5, "HP1000 $name: Added $v to history for $t:"
- . Dumper( @{ $hash->{helper}{history}{$t} } );
- }
- my $asize = scalar @{ $hash->{helper}{history}{$t} };
- return $v unless ($asize);
- $historySize = $asize if ( !$historySize || $asize < $historySize );
- $historySize--;
- my @a =
- $historySize
- ? @{ $hash->{helper}{history}{$t} }[ 0 .. $historySize ]
- : @{ $hash->{helper}{history}{$t} }[0];
- if ( $type == 1 ) {
- return round( sum(@a) / @a, 1 );
- }
- elsif ( $type == 2 ) {
- return maxNum( 0, @a );
- }
- return undef;
- }
- 1;
- =pod
- =item device
- =item summary support for Wifi-based weather stations using PWS protocol from Wunderground
- =item summary_DE unterstützt WLAN-basierte Wetterstationen mit Wunderground PWS Protokoll
- =begin html
- <p>
- <a name="HP1000" id="HP1000"></a>
- </p>
- <h3>
- HP1000
- </h3>
- <ul>
- <div>
- <a name="HP1000define" id="HP10000define"></a> <b>Define</b>
- <div>
- <ul>
- <code>define <WeatherStation> HP1000 [<ID> <PASSWORD>]</code><br>
- <br>
- Provides webhook receiver for Wifi-based weather station which support <a href="http://wiki.wunderground.com/index.php/PWS_-_Upload_Protocol">PWS protocol</a> from Weather Underground (e.g. HP1000, WH2600, WH2601, WH2621, WH2900, WH2950 of <a href="http://www.foshk.com/Wifi_Weather_Station/">Fine Offset Electronics</a> - sometimes also known as <a href="http://www.ambientweather.com/peorhowest.html">Ambient Weather</a> WS-1001-WIFI or similar). In Germany, these devices are commonly distributed by <a href="http://www.froggit.de/">froggit</a> or by <a href="http://www.conrad.de/">Conrad</a> under it's brand name Renkforce.<br>
- There needs to be a dedicated FHEMWEB instance with attribute webname set to "weatherstation".<br>
- No other name will work as it's hardcoded in the weather station device itself!<br>
- If necessary, this module will create a matching FHEMWEB instance named WEBweatherstation during initial definition.<br>
- <br>
- As the URI has a fixed coding as well there can only be one single instance of this module FHEM installation.<br>
- <br>
- Example:<br>
- <div>
- <code># unprotected instance where ID and PASSWORD will be ignored<br>
- define WeatherStation HP1000<br>
- <br>
- # protected instance: Weather Station needs to be configured<br>
- # to send this ID and PASSWORD for data to be accepted<br>
- define WeatherStation HP1000 MyHouse SecretPassword</code>
- </div><br>
- IMPORTANT: In your hardware device, make sure you use a DNS name as most revisions cannot handle IP addresses correctly.<br>
- You might want to check to install a firmware update from <a href="http://www.foshk.com/support/">here</a>.
- </ul>
- </div><br>
- </div>
- <br>
- <a name="HP1000Attr" id="HP10000Attr"></a> <b>Attributes</b>
- <div>
- <ul>
- <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
- <br>
- <a name="webhookFWinstances"></a><li><b>webhookFWinstances</b></li>
- Explicitly specify allowed FHEMWEB instaces for data input (defaults to weatherstation)
- <a name="wu_id"></a><li><b>wu_id</b></li>
- Weather Underground (Wunderground) station ID
- <a name="wu_password"></a><li><b>wu_password</b></li>
- Weather Underground (Wunderground) password
- <a name="wu_dataValues"></a><li><b>wu_dataValues</b></li>
- Add or replace values before pushing data to Weather Underground.<br>
- Format is key=value while value may be of <a href="#set">set magic format</a>
- <a name="wu_indoorValues"></a><li><b>wu_indoorValues</b></li>
- Include indoor values for Weather Underground (defaults to 1=yes)
- <a name="wu_push"></a><li><b>wu_push</b></li>
- Enable or disable to push data forward to Weather Underground (defaults to 0=no)
- <a name="wu_pushValues"></a><li><b>wu_pushValues</b></li>
- Restrict values to be transferred to Weather Underground,
- otherwise all values will be transferred.
- <a name="wu_realtime"></a><li><b>wu_realtime</b></li>
- Send the data to the WU realtime server instead of using the standard server (defaults to 1=yes)
- </ul>
- </div>
- </ul>
- =end html
- =begin html_DE
- <p>
- <a name="HP1000" id="HP1000"></a>
- </p>
- <h3>
- HP1000
- </h3>
- <ul>
- <div>
- <a name="HP1000define" id="HP10000define"></a> <b>Define</b>
- <div>
- <ul>
- <code>define <WeatherStation> HP1000 [<ID> <PASSWORD>]</code><br>
- <br>
- Stellt einen Webhook für WLAN-basierte Wetterstationen bereit, die das <a href="http://wiki.wunderground.com/index.php/PWS_-_Upload_Protocol">PWS</a> Protokoll von Weather Underground verwenden (z.B. HP1000, WH2600, WH2601, WH2621, WH2900, WH2950 Wetterstation von <a href="http://www.foshk.com/Wifi_Weather_Station/">Fine Offset Electronics</a> - manchmal auch bekannt als <a href="http://www.ambientweather.com/peorhowest.html">Ambient Weather</a> WS-1001-WIFI oder ähnliches). In Deutschland werden die Geräte zumeist von <a href="http://www.froggit.de/">froggit</a> oder von <a href="http://www.conrad.de/">Conrad</a> unter dem Markennamen Renkforce vertrieben.<br>
- Es muss noch eine dedizierte FHEMWEB Instanz angelegt werden, wo das Attribut webname auf "weatherstation" gesetzt wurde.<br>
- Kein anderer Name funktioniert, da dieser fest in der Wetterstation hinterlegt ist!<br>
- Sofern notwendig, erstellt dieses Modul eine passende FHEMWEB Instanz namens WEBweatherstation während der initialen Definition.<br>
- <br>
- Da die URI ebenfalls fest kodiert ist, kann mit einer einzelnen FHEM Installation maximal eine Instanz dieses Moduls gleichzeitig verwendet werden.<br>
- <br>
- Beispiel:<br>
- <div>
- <code># ungeschützte Instanz bei der ID und PASSWORD ignoriert werden<br>
- define WeatherStation HP1000<br>
- <br>
- # geschützte Instanz: Die Wetterstation muss so konfiguriert sein, dass sie<br>
- # diese ID und PASSWORD sendet, damit Daten akzeptiert werden<br>
- define WeatherStation HP1000 MyHouse SecretPassword</code>
- </div><br>
- WICHTIG: Im Gerät selbst muss sichergestellt sein, dass ein DNS Name statt einer IP Adresse verwendet wird, da einige Revisionen damit nicht umgehen können.<br>
- Ggf. sollte man <a href="http://www.foshk.com/support/">hier</a> einmal nach einer neueren Firmware schauen.
- </ul>
- </div><br>
- </div>
- <a name="HP1000Attr" id="HP10000Attr"></a> <b>Attributes</b>
- <div>
- <ul>
- <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
- <br>
- <a name="webhookFWinstances"></a><li><b>webhookFWinstances</b></li>
- Explizite Angabe der FHEMWEB Instanzen, äber die Dateneingaben erlaubt sind (Standard ist weatherstation)
- <a name="wu_id"></a><li><b>wu_id</b></li>
- Weather Underground (Wunderground) Stations ID
- <a name="wu_password"></a><li><b>wu_password</b></li>
- Weather Underground (Wunderground) Passwort
- <a name="wu_dataValues"></a><li><b>wu_dataValues</b></li>
- Ersetzt Werte oder fügt neue Werte hinzu, bevor diese zu Weather Underground übertragen werden.<br>
- Das Format entspricht key=value wobei value im <a href="#set">Format set magic sein</a> kann.
- <a name="wu_indoorValues"></a><li><b>wu_indoorValues</b></li>
- Gibt an, ob die Innenraumwerte mit zu Weather Underground übertragen werden sollen (Standard ist 1=an)
- <a name="wu_push"></a><li><b>wu_push</b></li>
- Pushen der Daten zu Weather Underground aktivieren oder deaktivieren (Standard ist 0=aus)
- <a name="wu_pushValues"></a><li><b>wu_pushValues</b></li>
- Schränkt die Werte ein, die an Weather Underground übertragen werden.
- Andernfalls werden alle Werte übertragen.
- <a name="wu_realtime"></a><li><b>wu_realtime</b></li>
- Sendet die Daten an den WU Echtzeitserver statt an den Standard Server (Standard ist 1=an)
- </ul>
- </div>
- </ul>
- =end html_DE
- =cut
|