70_Pushover.pm 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833
  1. # $Id: 70_Pushover.pm 13388 2017-02-11 13:34:30Z loredo $
  2. ###############################################################################
  3. #
  4. # A module to send notifications to Pushover.
  5. #
  6. # written 2013 by Johannes B <johannes_b at icloud.com>
  7. # modified 24.02.2014 by Benjamin Battran <fhem.contrib at benni.achalmblick.de>
  8. # -> Added title, device, priority and sound attributes (see documentation below)
  9. # modified 09.08.2015 by Julian Pawlowski <julian.pawlowski@gmail.com>
  10. # -> Rewrite for Non-Blocking HttpUtils
  11. # -> much more readings
  12. # -> Support for emergency callback via push (see documentation below)
  13. # -> Support for supplementary URLs incl. push callback (e.g. for priority < 2)
  14. # -> Added readingFnAttributes to AttrList
  15. # -> Added support for HTML formatted text
  16. # -> Added user/group token validation
  17. #
  18. ###############################################################################
  19. #
  20. # Also see API documentation:
  21. # https://pushover.net/api
  22. package main;
  23. use HttpUtils;
  24. use utf8;
  25. use Data::Dumper;
  26. use HttpUtils;
  27. use SetExtensions;
  28. use Encode;
  29. no if $] >= 5.017011, warnings => 'experimental';
  30. my %sets = ( "msg" => 1, "glance" => 1 );
  31. #------------------------------------------------------------------------------
  32. sub Pushover_Initialize($$) {
  33. my ($hash) = @_;
  34. $hash->{DefFn} = "Pushover_Define";
  35. $hash->{UndefFn} = "Pushover_Undefine";
  36. $hash->{SetFn} = "Pushover_Set";
  37. $hash->{AttrList} =
  38. "disable:0,1 disabledForIntervals do_not_notify:0,1 timestamp:0,1 title sound:pushover,bike,bugle,cashregister,classical,cosmic,falling,gamelan,incoming,intermission,magic,mechanical,pianobar,siren,spacealarm,tugboat,alien,climb,persistent,echo,updown,none device priority:0,1,-1,-2 callbackUrl "
  39. . $readingFnAttributes;
  40. # a priority value of 2 is not predifined as for this also a value for
  41. # retry and expire must be set which will most likely not be used with
  42. # default values.
  43. }
  44. #------------------------------------------------------------------------------
  45. sub Pushover_addExtension($$$) {
  46. my ( $name, $func, $link ) = @_;
  47. my $url = "/$link";
  48. return 0
  49. if ( defined( $data{FWEXT}{$url} )
  50. && $data{FWEXT}{$url}{deviceName} ne $name );
  51. Log3 $name, 2,
  52. "Pushover $name: Registering Pushover for webhook URI $url ...";
  53. $data{FWEXT}{$url}{deviceName} = $name;
  54. $data{FWEXT}{$url}{FUNC} = $func;
  55. $data{FWEXT}{$url}{LINK} = $link;
  56. $name->{HASH}{FHEMWEB_URI} = $url;
  57. return 1;
  58. }
  59. #------------------------------------------------------------------------------
  60. sub Pushover_removeExtension($) {
  61. my ($link) = @_;
  62. my $url = "/$link";
  63. my $name = $data{FWEXT}{$url}{deviceName};
  64. Log3 $name, 2,
  65. "Pushover $name: Unregistering Pushover for webhook URL $url...";
  66. delete $data{FWEXT}{$url};
  67. delete $name->{HASH}{FHEMWEB_URI};
  68. }
  69. #------------------------------------------------------------------------------
  70. sub Pushover_Define($$) {
  71. my ( $hash, $def ) = @_;
  72. my @args = split( "[ \t]+", $def );
  73. return
  74. "Invalid number of arguments: define <name> Pushover <token> <user> [<infix>]"
  75. if ( int(@args) < 2 );
  76. my ( $name, $type, $token, $user, $infix ) = @args;
  77. return "$user does not seem to be a valid user or group token"
  78. if ( $user !~ /^([a-zA-Z0-9]{30})$/ );
  79. if ( defined($token) && defined($user) ) {
  80. $hash->{APP_TOKEN} = $token;
  81. $hash->{USER_KEY} = $user;
  82. if ( defined($infix) && $infix ne "" ) {
  83. $hash->{fhem}{infix} = $infix;
  84. return "Could not register infix, seems to be existing"
  85. if ( !Pushover_addExtension( $name, "Pushover_CGI", $infix ) );
  86. }
  87. # start Validation Timer
  88. RemoveInternalTimer($hash);
  89. if ( ReadingsVal( $name, "tokenState", "invalid" ) ne "valid"
  90. || ReadingsVal( $name, "userState", "invalid" ) ne "valid"
  91. || $init_done )
  92. {
  93. InternalTimer( gettimeofday() + 5,
  94. "Pushover_ValidateUser", $hash, 0 );
  95. }
  96. else {
  97. InternalTimer( gettimeofday() + 21600,
  98. "Pushover_ValidateUser", $hash, 0 );
  99. }
  100. return undef;
  101. }
  102. else {
  103. return "App or user/group token missing.";
  104. }
  105. }
  106. #------------------------------------------------------------------------------
  107. sub Pushover_Undefine($$) {
  108. my ( $hash, $name ) = @_;
  109. if ( defined( $hash->{fhem}{infix} ) ) {
  110. Pushover_removeExtension( $hash->{fhem}{infix} );
  111. }
  112. RemoveInternalTimer($hash);
  113. return undef;
  114. }
  115. #------------------------------------------------------------------------------
  116. sub Pushover_Set($@) {
  117. my ( $hash, $name, $cmd, @args ) = @_;
  118. my ( $a, $h ) = parseParams( join " ", @args );
  119. if ( !defined( $sets{$cmd} ) ) {
  120. return
  121. "Unknown argument "
  122. . $cmd
  123. . ", choose one of "
  124. . join( " ", sort keys %sets );
  125. }
  126. return "Unable to send message: Device is disabled"
  127. if ( IsDisabled($name) );
  128. return "Unable to send message: User key is invalid"
  129. if ( ReadingsVal( $name, "userState", "valid" ) eq "invalid" );
  130. return "Unable to send message: App token is invalid"
  131. if ( ReadingsVal( $name, "tokenState", "valid" ) eq "invalid" );
  132. return Pushover_SetMessage2( $hash, $cmd, $a, $h )
  133. if (
  134. $cmd eq 'glance'
  135. || (
  136. $cmd eq 'msg'
  137. && ( join( " ", @args ) !~ m/^(".*"|'.*').*$/
  138. || ( defined($h) && keys %{$h} > 0 ) )
  139. )
  140. );
  141. return Pushover_SetMessage( $hash, @args )
  142. if ( $cmd eq 'msg' );
  143. }
  144. #------------------------------------------------------------------------------
  145. sub Pushover_SendCommand($$;$\%) {
  146. my ( $hash, $service, $cmd, $type ) = @_;
  147. my $name = $hash->{NAME};
  148. my $address = "api.pushover.net";
  149. my $port = "443";
  150. my $apiVersion = "1";
  151. my $http_method = "POST";
  152. my $http_noshutdown = ( defined( $attr{$name}{"http-noshutdown"} )
  153. && $attr{$name}{"http-noshutdown"} eq "0" ) ? 0 : 1;
  154. my $timeout;
  155. $cmd = ( defined($cmd) ) ? $cmd : "";
  156. Log3 $name, 5, "Pushover $name: called function Pushover_SendCommand()";
  157. my $http_proto;
  158. if ( $port eq "443" ) {
  159. $http_proto = "https";
  160. }
  161. elsif ( defined( $attr{$name}{https} ) && $attr{$name}{https} eq "1" ) {
  162. $http_proto = "https";
  163. $port = "443" if ( $port eq "80" );
  164. }
  165. else {
  166. $http_proto = "http";
  167. }
  168. $cmd .= "&" if ( $cmd ne "" );
  169. $cmd .= "token=" . $hash->{APP_TOKEN};
  170. if ( !defined( $type->{USER_KEY} ) ) {
  171. $cmd .= "&user=" . $hash->{USER_KEY};
  172. }
  173. else {
  174. Log3 $name, 4,
  175. "Pushover $name: USER_KEY found in device name: " . $type->{USER_KEY};
  176. $cmd .= "&user=" . $type->{USER_KEY};
  177. }
  178. my $URL;
  179. my $response;
  180. my $return;
  181. if ( !defined($cmd) || $cmd eq "" ) {
  182. Log3 $name, 4, "Pushover $name: REQ $service";
  183. }
  184. else {
  185. $cmd = "?" . $cmd . "&"
  186. if ( $http_method eq "GET" || $http_method eq "" );
  187. Log3 $name, 4, "Pushover $name: REQ $service/" . urlDecode($cmd);
  188. }
  189. $URL =
  190. $http_proto . "://"
  191. . $address . ":"
  192. . $port . "/"
  193. . $apiVersion . "/"
  194. . $service;
  195. $URL .= $cmd if ( $http_method eq "GET" || $http_method eq "" );
  196. if ( defined( $attr{$name}{timeout} )
  197. && $attr{$name}{timeout} =~ /^\d+$/ )
  198. {
  199. $timeout = $attr{$name}{timeout};
  200. }
  201. else {
  202. $timeout = 3;
  203. }
  204. # send request via HTTP-GET method
  205. if ( $http_method eq "GET" || $http_method eq "" || $cmd eq "" ) {
  206. Log3 $name, 5,
  207. "Pushover $name: GET "
  208. . urlDecode($URL)
  209. . " (noshutdown="
  210. . $http_noshutdown . ")";
  211. HttpUtils_NonblockingGet(
  212. {
  213. url => $URL,
  214. timeout => $timeout,
  215. noshutdown => $http_noshutdown,
  216. data => undef,
  217. hash => $hash,
  218. service => $service,
  219. cmd => $cmd,
  220. type => $type,
  221. httpversion => "1.1",
  222. callback => \&Pushover_ReceiveCommand,
  223. }
  224. );
  225. }
  226. # send request via HTTP-POST method
  227. elsif ( $http_method eq "POST" ) {
  228. Log3 $name, 5,
  229. "Pushover $name: GET "
  230. . $URL
  231. . " (POST DATA: "
  232. . urlDecode($cmd)
  233. . ", noshutdown="
  234. . $http_noshutdown . ")";
  235. HttpUtils_NonblockingGet(
  236. {
  237. url => $URL,
  238. timeout => $timeout,
  239. noshutdown => $http_noshutdown,
  240. data => $cmd,
  241. hash => $hash,
  242. service => $service,
  243. cmd => $cmd,
  244. type => $type,
  245. callback => \&Pushover_ReceiveCommand,
  246. }
  247. );
  248. }
  249. # other HTTP methods are not supported
  250. else {
  251. Log3 $name, 1,
  252. "Pushover $name: ERROR: HTTP method "
  253. . $http_method
  254. . " is not supported.";
  255. }
  256. return;
  257. }
  258. #------------------------------------------------------------------------------
  259. sub Pushover_ReceiveCommand($$$) {
  260. my ( $param, $err, $data ) = @_;
  261. my $hash = $param->{hash};
  262. my $name = $hash->{NAME};
  263. my $service = $param->{service};
  264. my $cmd = $param->{cmd};
  265. my $state = ReadingsVal( $name, "state", "initialized" );
  266. my $values = $param->{type};
  267. my $return;
  268. Log3 $name, 5,
  269. "Pushover $name: Received HttpUtils callback:\n\nPARAM:\n"
  270. . Dumper($param)
  271. . "\n\nERROR:\n"
  272. . Dumper($err)
  273. . "\n\nDATA:\n"
  274. . Dumper($data);
  275. readingsBeginUpdate($hash);
  276. # service not reachable
  277. if ($err) {
  278. $state = "disconnected";
  279. if ( !defined($cmd) || $cmd eq "" ) {
  280. Log3 $name, 4, "Pushover $name: RCV TIMEOUT $service";
  281. }
  282. else {
  283. Log3 $name, 4,
  284. "Pushover $name: RCV TIMEOUT $service/" . urlDecode($cmd);
  285. }
  286. }
  287. # data received
  288. elsif ($data) {
  289. $state = "connected";
  290. if ( !defined($cmd) || $cmd eq "" ) {
  291. Log3 $name, 4, "Pushover $name: RCV $service";
  292. }
  293. else {
  294. Log3 $name, 4, "Pushover $name: RCV $service/" . urlDecode($cmd);
  295. }
  296. if ( $data ne "" ) {
  297. if ( $data =~ /^{/ || $data =~ /^\[/ ) {
  298. if ( !defined($cmd) || ref($cmd) eq "HASH" || $cmd eq "" ) {
  299. Log3 $name, 5, "Pushover $name: RES $service\n" . $data;
  300. }
  301. else {
  302. Log3 $name, 5,
  303. "Pushover $name: RES $service/"
  304. . urlDecode($cmd) . "\n"
  305. . $data;
  306. }
  307. # Use JSON module if possible
  308. eval {
  309. require JSON;
  310. import JSON qw( decode_json );
  311. };
  312. $return = decode_json( Encode::encode_utf8($data) )
  313. if ( !$@ );
  314. }
  315. else {
  316. if ( !defined($cmd) || ref($cmd) eq "HASH" || $cmd eq "" ) {
  317. Log3 $name, 5,
  318. "Pushover $name: RES ERROR $service\n" . $data;
  319. }
  320. else {
  321. Log3 $name, 5,
  322. "Pushover $name: RES ERROR $service/"
  323. . urlDecode($cmd) . "\n"
  324. . $data;
  325. }
  326. return undef;
  327. }
  328. }
  329. $return = Encode::encode_utf8($data) if ( ref($return) ne "HASH" );
  330. #######################
  331. # process return data
  332. #
  333. $values{result} = "ok";
  334. # extract API stats
  335. my $apiLimit = 7500;
  336. my $apiRemaining = 1;
  337. my $apiReset;
  338. if ( $param->{httpheader} =~ m/X-Limit-App-Limit:[\s\t]*(.*)[\s\t\n]*/ )
  339. {
  340. $apiLimit = $1;
  341. readingsBulkUpdateIfChanged( $hash, "apiLimit", $1 ),;
  342. }
  343. if ( $param->{httpheader} =~
  344. m/X-Limit-App-Remaining:[\s\t]*(.*)[\s\t\n]*/ )
  345. {
  346. $apiRemaining = $1;
  347. readingsBulkUpdateIfChanged( $hash, "apiRemaining", $1 );
  348. }
  349. if ( $param->{httpheader} =~ m/X-Limit-App-Reset:[\s\t]*(.*)[\s\t\n]*/ )
  350. {
  351. $apiReset = $1;
  352. readingsBulkUpdateIfChanged( $hash, "apiReset", $1 );
  353. }
  354. # Server error
  355. if ( $param->{code} >= 500 ) {
  356. $state = "error";
  357. $values{result} = "Server Error " . $param->{code};
  358. }
  359. # error handling
  360. elsif (
  361. ( $param->{code} == 200 || $param->{code} >= 400 )
  362. && ( ( ref($return) eq "HASH" && $return->{status} ne "1" )
  363. || ( ref($return) ne "HASH" && $return !~ m/"status":1,/ ) )
  364. )
  365. {
  366. $values{result} =
  367. "Error " . $param->{code} . ": Unspecified error occured";
  368. if ( ref($return) eq "HASH" && defined $return->{errors} ) {
  369. $values{result} =
  370. "Error "
  371. . $param->{code} . ": "
  372. . join( ". ", @{ $return->{errors} } );
  373. }
  374. elsif ( ref($return) ne "HASH" && $return =~ m/"errors":\[(.*)\]/ )
  375. {
  376. $values{result} = "Error " . $param->{code} . ": " . $1;
  377. }
  378. $state = "error";
  379. if ( ref($return) eq "HASH" && defined( $return->{token} ) ) {
  380. $state = "unauthorized";
  381. readingsBulkUpdate( $hash, "tokenState", $return->{token} );
  382. }
  383. elsif ( ref($return) ne "HASH" && $return =~ m/"token":"invalid"/ )
  384. {
  385. $state = "unauthorized";
  386. readingsBulkUpdate( $hash, "tokenState", "invalid" );
  387. }
  388. else {
  389. readingsBulkUpdateIfChanged( $hash, "tokenState", "valid" );
  390. }
  391. if ( ref($return) eq "HASH" && defined( $return->{user} ) ) {
  392. $state = "unauthorized" if ( !defined( $values->{USER_KEY} ) );
  393. readingsBulkUpdate( $hash, "userState", $return->{user} )
  394. if ( !defined( $values->{USER_KEY} ) );
  395. $hash->{helper}{FAILED_USERKEYS}{ $values->{USER_KEY} } =
  396. "USERKEY "
  397. . $values->{USER_KEY} . " "
  398. . $return->{user} . " - "
  399. . $values{result}
  400. if ( defined( $values->{USER_KEY} ) );
  401. }
  402. elsif ( ref($return) ne "HASH" && $return =~ m/"user":"invalid"/ ) {
  403. $state = "unauthorized" if ( !defined( $values->{USER_KEY} ) );
  404. readingsBulkUpdate( $hash, "userState", "invalid" )
  405. if ( !defined( $values->{USER_KEY} ) );
  406. $hash->{helper}{FAILED_USERKEYS}{ $values->{USER_KEY} } =
  407. "USERKEY "
  408. . $values->{USER_KEY}
  409. . " invalid - "
  410. . $values{result}
  411. if ( defined( $values->{USER_KEY} ) );
  412. }
  413. else {
  414. readingsBulkUpdateIfChanged( $hash, "userState", "valid" )
  415. if ( !defined( $values->{USER_KEY} ) );
  416. delete $hash->{helper}{FAILED_USERKEYS}{ $values->{USER_KEY} }
  417. if (
  418. !defined( $values->{USER_KEY} )
  419. && defined(
  420. $hash->{helper}{FAILED_USERKEYS}{ $values->{USER_KEY} }
  421. )
  422. );
  423. }
  424. }
  425. else {
  426. $state = "limited" if ( $apiRemaining < 1 );
  427. readingsBulkUpdateIfChanged( $hash, "tokenState", "valid" )
  428. if ( !defined( $values->{USER_KEY} ) );
  429. readingsBulkUpdateIfChanged( $hash, "userState", "valid" )
  430. if ( !defined( $values->{USER_KEY} ) );
  431. }
  432. # messages.json
  433. if ( $service eq "messages.json" ) {
  434. readingsBulkUpdate( $hash, "lastTitle", $values->{title} );
  435. readingsBulkUpdate( $hash, "lastMessage",
  436. urlDecode( $values->{message} ) );
  437. readingsBulkUpdate( $hash, "lastPriority", $values->{priority} );
  438. readingsBulkUpdate( $hash, "lastAction", $values->{action} )
  439. if ( $values->{action} ne "" );
  440. readingsBulkUpdate( $hash, "lastAction", "-" )
  441. if ( $values->{action} eq "" );
  442. readingsBulkUpdate( $hash, "lastDevice", $values->{device} )
  443. if ( $values->{device} ne "" );
  444. readingsBulkUpdate( $hash, "lastDevice",
  445. ReadingsVal( $name, "devices", "all" ) )
  446. if ( $values->{device} eq "" );
  447. if ( ref($return) eq "HASH" ) {
  448. readingsBulkUpdate( $hash, "lastRequest", $return->{request} )
  449. if ( defined $return->{request} );
  450. if ( $values->{expire} ne "" ) {
  451. readingsBulkUpdate( $hash, "cbTitle_" . $values->{cbNr},
  452. $values->{title} );
  453. readingsBulkUpdate(
  454. $hash,
  455. "cbMsg_" . $values->{cbNr},
  456. urlDecode( $values->{message} )
  457. );
  458. readingsBulkUpdate( $hash, "cbPrio_" . $values->{cbNr},
  459. $values->{priority} );
  460. readingsBulkUpdate( $hash, "cbAck_" . $values->{cbNr},
  461. "0" );
  462. if ( $values->{device} ne "" ) {
  463. readingsBulkUpdate( $hash, "cbDev_" . $values->{cbNr},
  464. $values->{device} );
  465. }
  466. else {
  467. readingsBulkUpdate(
  468. $hash,
  469. "cbDev_" . $values->{cbNr},
  470. ReadingsVal( $name, "devices", "all" )
  471. );
  472. }
  473. if ( defined $return->{receipt} ) {
  474. readingsBulkUpdate( $hash, "cb_" . $values->{cbNr},
  475. $return->{receipt} );
  476. }
  477. else {
  478. readingsBulkUpdate( $hash, "cb_" . $values->{cbNr},
  479. $values->{cbNr} );
  480. }
  481. if ( $values->{action} ne "" ) {
  482. readingsBulkUpdate( $hash, "cbAct_" . $values->{cbNr},
  483. $values->{action} );
  484. }
  485. }
  486. }
  487. elsif ( $values{expire} ne "" ) {
  488. $values{result} =
  489. "SoftFail: Callback not supported. Please install Perl::JSON";
  490. }
  491. }
  492. # glances.json
  493. elsif ( $service eq "glances.json" ) {
  494. readingsBulkUpdate( $hash, "lastTitle", $values->{title} );
  495. readingsBulkUpdate( $hash, "lastText",
  496. urlDecode( $values->{text} ) )
  497. if ( $values->{text} ne "" );
  498. readingsBulkUpdate( $hash, "lastSubtext",
  499. urlDecode( $values->{subtext} ) )
  500. if ( $values->{subtext} ne "" );
  501. readingsBulkUpdate( $hash, "lastCount", $values->{count} )
  502. if ( $values->{count} ne "" );
  503. readingsBulkUpdate( $hash, "lastPercent", $values->{percent} )
  504. if ( $values->{percent} ne "" );
  505. readingsBulkUpdate( $hash, "lastDevice", $values->{device} )
  506. if ( $values->{device} ne "" );
  507. readingsBulkUpdate( $hash, "lastDevice",
  508. ReadingsVal( $name, "devices", "all" ) )
  509. if ( $values->{device} eq "" );
  510. if ( ref($return) eq "HASH" ) {
  511. readingsBulkUpdate( $hash, "lastRequest", $return->{request} )
  512. if ( defined $return->{request} );
  513. }
  514. }
  515. # users/validate.json
  516. elsif ( $service eq "users/validate.json" ) {
  517. if ( ref($return) eq "HASH" ) {
  518. my $devices = "-";
  519. my $group = "0";
  520. $devices = join( ",", @{ $return->{devices} } )
  521. if ( defined( $return->{devices} ) );
  522. $group = $return->{group} if ( defined( $return->{group} ) );
  523. readingsBulkUpdateIfChanged( $hash, "devices", $devices );
  524. readingsBulkUpdateIfChanged( $hash, "group", $group );
  525. }
  526. }
  527. readingsBulkUpdate( $hash, "lastResult", $values{result} );
  528. }
  529. # Set reading for availability
  530. #
  531. my $available = 0;
  532. $available = 1
  533. if ( $param->{code} ne "429"
  534. && ( $state eq "connected" || $state eq "error" ) );
  535. readingsBulkUpdateIfChanged( $hash, "available", $available );
  536. # Set reading for state
  537. #
  538. readingsBulkUpdateIfChanged( $hash, "state", $state );
  539. # credentials validation loop
  540. #
  541. my $nextTimer = "none";
  542. # if we could not connect, try again in 5 minutes
  543. if ( $state eq "disconnected" ) {
  544. $nextTimer = gettimeofday() + 300;
  545. }
  546. # re-validate every 6 hours if there was no message sent during
  547. # that time
  548. elsif ( $available eq "1" ) {
  549. $nextTimer = gettimeofday() + 21600;
  550. }
  551. # re-validate after API limit was reset
  552. elsif ( $state eq "limited" || $param->{code} == 429 ) {
  553. $nextTimer =
  554. ReadingsVal( $name, "apiReset", gettimeofday() + 21277 ) + 323;
  555. }
  556. RemoveInternalTimer($hash);
  557. $hash->{VALIDATION_TIMER} = $nextTimer;
  558. InternalTimer( $nextTimer, "Pushover_ValidateUser", $hash, 0 )
  559. if ( $nextTimer ne "none" );
  560. readingsEndUpdate( $hash, 1 );
  561. return;
  562. }
  563. #------------------------------------------------------------------------------
  564. sub Pushover_ValidateUser ($;$) {
  565. my ( $hash, $update ) = @_;
  566. my $name = $hash->{NAME};
  567. my $device = AttrVal( $name, "device", "" );
  568. Log3 $name, 5, "Pushover $name: called function Pushover_ValidateUser()";
  569. RemoveInternalTimer($hash);
  570. if ( AttrVal( $name, "disable", 0 ) == 1 ) {
  571. $hash->{VALIDATION_TIMER} = "disabled";
  572. RemoveInternalTimer($hash);
  573. InternalTimer( gettimeofday() + 900, "Pushover_ValidateUser", $hash,
  574. 0 );
  575. return;
  576. }
  577. elsif ( $device ne "" ) {
  578. Pushover_SendCommand( $hash, "users/validate.json", "device=$device" );
  579. }
  580. else {
  581. Pushover_SendCommand( $hash, "users/validate.json" );
  582. }
  583. return;
  584. }
  585. #------------------------------------------------------------------------------
  586. sub Pushover_SetMessage {
  587. my $hash = shift;
  588. my $name = $hash->{NAME};
  589. my %values = ();
  590. Log3 $name, 5, "Pushover $name: called function Pushover_SetMessage()";
  591. #Set defaults
  592. $values{title} = AttrVal( $hash->{NAME}, "title", "" );
  593. $values{message} = "";
  594. $values{device} = AttrVal( $hash->{NAME}, "device", "" );
  595. $values{priority} = AttrVal( $hash->{NAME}, "priority", 0 );
  596. $values{sound} = AttrVal( $hash->{NAME}, "sound", "" );
  597. $values{retry} = "";
  598. $values{expire} = "";
  599. $values{url_title} = "";
  600. $values{action} = "";
  601. my $callback = (
  602. defined( $attr{$name}{callbackUrl} )
  603. && defined( $hash->{fhem}{infix} )
  604. ? $attr{$name}{callbackUrl}
  605. : ""
  606. );
  607. #Split parameters
  608. my $param = join( " ", @_ );
  609. my $argc = 0;
  610. if ( $param =~
  611. /(".*"|'.*')\s*(".*"|'.*')\s*(".*"|'.*')\s*(-?\d+)\s*(".*"|'.*')\s*(\d+)\s*(\d+)\s*(".*"|'.*')\s*(".*"|'.*')\s*$/s
  612. )
  613. {
  614. $argc = 9;
  615. }
  616. elsif ( $param =~
  617. /(".*"|'.*')\s*(".*"|'.*')\s*(".*"|'.*')\s*(-?\d+)\s*(".*"|'.*')\s*(\d+)\s*(\d+)\s*$/s
  618. )
  619. {
  620. $argc = 7;
  621. }
  622. elsif ( $param =~
  623. /(".*"|'.*')\s*(".*"|'.*')\s*(".*"|'.*')\s*(-?\d+)\s*(".*"|'.*')\s*$/s )
  624. {
  625. $argc = 5;
  626. }
  627. elsif ( $param =~ /(".*"|'.*')\s*(".*"|'.*')\s*$/s ) {
  628. $argc = 2;
  629. }
  630. elsif ( $param =~ /(".*"|'.*')\s*$/s ) {
  631. $argc = 1;
  632. }
  633. Log3 $name, 4, "Pushover $name: Found $argc argument(s)";
  634. if ( $argc > 1 ) {
  635. $values{title} = $1;
  636. $values{message} = $2;
  637. Log3 $name, 4,
  638. "Pushover $name: title=$values{title} message=$values{message}";
  639. if ( $argc > 2 ) {
  640. $values{device} = $3;
  641. $values{priority} = $4;
  642. $values{sound} = $5;
  643. Log3 $name, 4,
  644. "Pushover $name: device=$values{device} priority=$values{priority} sound=$values{sound}";
  645. if ( $argc > 5 ) {
  646. $values{retry} = $6;
  647. $values{expire} = $7;
  648. Log3 $name, 4,
  649. "Pushover $name: retry=$values{retry} expire=$values{expire}";
  650. if ( $argc > 7 ) {
  651. $values{url_title} = $8;
  652. $values{action} = $9;
  653. Log3 $name, 4,
  654. "Pushover $name: url_title=$values{url_title} action=$values{action}";
  655. }
  656. }
  657. }
  658. }
  659. elsif ( $argc == 1 ) {
  660. $values{message} = $1;
  661. Log3 $name, 4, "Pushover $name: message=$values{message}";
  662. }
  663. #Remove quotation marks
  664. if ( $values{title} =~ /^['"](.*)['"]$/s ) {
  665. $values{title} = $1;
  666. }
  667. if ( $values{message} =~ /^['"](.*)['"]$/s ) {
  668. $values{message} = $1;
  669. }
  670. if ( $values{device} =~ /^['"](.*)['"]$/s ) {
  671. $values{device} = $1;
  672. }
  673. if ( $values{priority} =~ /^['"](.*)['"]$/s ) {
  674. $values{priority} = $1;
  675. }
  676. if ( $values{sound} =~ /^['"](.*)['"]$/s ) {
  677. $values{sound} = $1;
  678. }
  679. if ( $values{retry} =~ /^['"](.*)['"]$/s ) {
  680. $values{retry} = $1;
  681. }
  682. if ( $values{expire} =~ /^['"](.*)['"]$/s ) {
  683. $values{expire} = $1;
  684. }
  685. if ( $values{url_title} =~ /^['"](.*)['"]$/s ) {
  686. $values{url_title} = $1;
  687. }
  688. if ( $values{action} =~ /^['"](.*)['"]$/s ) {
  689. $values{action} = $1;
  690. }
  691. # check if we got a user or group key as device and use it as
  692. # user-key instead of hash->USER_KEY
  693. if ( $values{device} =~ /^(([A-Za-z0-9]{30}):)?([A-Za-z0-9,_-]*)(.*)$/ ) {
  694. $values{USER_KEY} = $2 if ( $2 ne "" );
  695. $values{device} = $3;
  696. return $hash->{helper}{FAILED_USERKEYS}{ $values{USER_KEY} }
  697. if ( $values{USER_KEY}
  698. && defined( $hash->{helper}{FAILED_USERKEYS}{ $values{USER_KEY} } )
  699. );
  700. }
  701. # Check if all mandatory arguments are filled:
  702. # "message" can not be empty and if "priority" is set to "2" "retry" and
  703. # "expire" must also be set.
  704. # "url_title" and "action" need to be set together and require "expire"
  705. # to be set as well.
  706. if (
  707. $values{message} ne ""
  708. && ( ( $values{retry} ne "" && $values{expire} ne "" )
  709. || $values{priority} < 2 )
  710. && (
  711. (
  712. $values{url_title} ne ""
  713. && $values{action} ne ""
  714. && $values{expire} ne ""
  715. )
  716. || ( $values{url_title} eq "" && $values{action} eq "" )
  717. )
  718. )
  719. {
  720. my $body;
  721. $body = "title=" . urlEncode( $values{title} )
  722. if ( $values{title} ne "" );
  723. if ( $values{message} =~
  724. /\<(\/|)[biu]\>|\<(\/|)font(.+)\>|\<(\/|)a(.*)\>|\<br\s?\/?\>/
  725. && $values{message} !~ /^nohtml:.*/ )
  726. {
  727. Log3 $name, 4, "Pushover $name: handling message with HTML content";
  728. $body .= "&html=1";
  729. # replace \n by <br /> but ignore \\n
  730. $values{message} =~ s/(?<!\\)(\\n)/<br \/>/g;
  731. }
  732. elsif ( $values{message} =~ /^nohtml:.*/ ) {
  733. Log3 $name, 4,
  734. "Pushover $name: explicitly ignoring HTML tags in message";
  735. $values{message} =~ s/^(nohtml:).*//;
  736. }
  737. # HttpUtil's urlEncode() does not handle \n but would escape %
  738. # so we encode first
  739. $values{message} = urlEncode( $values{message} );
  740. # replace any URL-encoded \n with their hex equivalent but ignore \\n
  741. $values{message} =~ s/(?<!%5c)(%5cn)/%0a/g;
  742. # replace any URL-encoded \\n by \n
  743. $values{message} =~ s/%5c%5cn/%5cn/g;
  744. $body .= "&message=" . $values{message};
  745. if ( $values{device} ne "" ) {
  746. $body .= "&device=" . $values{device};
  747. }
  748. if ( $values{priority} ne "" ) {
  749. $values{priority} = 2 if ( $values{priority} > 2 );
  750. $values{priority} = -2 if ( $values{priority} < -2 );
  751. $body .= "&priority=" . $values{priority};
  752. }
  753. if ( $values{sound} ne "" ) {
  754. $body .= "&sound=" . $values{sound};
  755. }
  756. if ( $values{retry} ne "" ) {
  757. $body .= "&retry=" . $values{retry};
  758. }
  759. if ( $values{expire} ne "" ) {
  760. $body .= "&expire=" . $values{expire};
  761. $values{cbNr} = int( time() ) + $values{expire};
  762. my $cbReading = "cb_" . $values{cbNr};
  763. until ( ReadingsVal( $name, $cbReading, "" ) eq "" ) {
  764. $values{cbNr}++;
  765. $cbReading = "cb_" . $values{cbNr};
  766. }
  767. }
  768. if ( 1 == AttrVal( $hash->{NAME}, "timestamp", 0 ) ) {
  769. $body .= "&timestamp=" . int( time() );
  770. }
  771. if ( $callback ne "" && $values{priority} > 1 ) {
  772. Log3 $name, 5,
  773. "Pushover $name: Adding emergency callback URL $callback";
  774. $body .= "&callback=" . $callback;
  775. }
  776. if ( $values{url_title} ne ""
  777. && $values{action} ne ""
  778. && $values{expire} ne "" )
  779. {
  780. my $url;
  781. if (
  782. $callback eq ""
  783. || ( $values{action} !~ /^http[s]?:\/\/.*$/
  784. && $values{action} =~ /^[\w-]+:\/\/.*$/ )
  785. )
  786. {
  787. $url = $values{action};
  788. $values{expire} = "";
  789. }
  790. else {
  791. $url =
  792. $callback
  793. . "?acknowledged=1&acknowledged_by="
  794. . $hash->{USER_KEY}
  795. . "&FhemCallbackId="
  796. . $values{cbNr};
  797. }
  798. Log3 $name, 5,
  799. "Pushover $name: Adding supplementary URL '$values{url_title}' ($url) with "
  800. . "action '$values{action}' (expires after $values{expire} => "
  801. . "$values{cbNr})";
  802. $body =
  803. $body
  804. . "&url_title="
  805. . urlEncode( $values{url_title} ) . "&url="
  806. . urlEncode($url);
  807. }
  808. # cleanup callback readings
  809. my $revReadings;
  810. while ( ( $key, $value ) = each %{ $hash->{READINGS} } ) {
  811. if ( $key =~ /^cb_\d+$/ ) {
  812. my @rBase = split( "_", $key );
  813. my $rTit = "cbTitle_" . $rBase[1];
  814. my $rMsg = "cbMsg_" . $rBase[1];
  815. my $rPrio = "cbPrio_" . $rBase[1];
  816. my $rAct = "cbAct_" . $rBase[1];
  817. my $rAck = "cbAck_" . $rBase[1];
  818. my $rAckAt = "cbAckAt_" . $rBase[1];
  819. my $rAckBy = "cbAckBy_" . $rBase[1];
  820. my $rDev = "cbDev_" . $rBase[1];
  821. Log3 $name, 5,
  822. "Pushover $name: checking to clean up "
  823. . $hash->{NAME}
  824. . " $key: time="
  825. . $rBase[1] . " ack="
  826. . ReadingsVal( $name, $rAck, "-" )
  827. . " curTime="
  828. . int( time() );
  829. if ( ReadingsVal( $name, $rAck, 0 ) == 1
  830. || $rBase[1] <= int( time() ) )
  831. {
  832. delete $hash->{READINGS}{$key};
  833. delete $hash->{READINGS}{$rTit};
  834. delete $hash->{READINGS}{$rMsg};
  835. delete $hash->{READINGS}{$rPrio};
  836. delete $hash->{READINGS}{$rAck};
  837. delete $hash->{READINGS}{$rDev};
  838. if ( defined( $hash->{READINGS}{$rAct} ) ) {
  839. delete $hash->{READINGS}{$rAct};
  840. }
  841. if ( defined( $hash->{READINGS}{$rAckAt} ) ) {
  842. delete $hash->{READINGS}{$rAckAt};
  843. }
  844. if ( defined( $hash->{READINGS}{$rAckBy} ) ) {
  845. delete $hash->{READINGS}{$rAckBy};
  846. }
  847. Log3 $name, 4,
  848. "Pushover $name: cleaned up expired receipt " . $rBase[1];
  849. }
  850. }
  851. }
  852. Pushover_SendCommand( $hash, "messages.json", $body, %values );
  853. return;
  854. }
  855. else {
  856. # There was a problem with the arguments, so tell the user the
  857. # correct usage of the 'set msg' command
  858. if ( 1 == $argc && $values{title} eq "" ) {
  859. return
  860. "Please define the default title in the pushover device arguments.";
  861. }
  862. else {
  863. return
  864. "Syntax: $name msg ['<title>'] '<text>' ['<device>' <priority> '<sound>' "
  865. . "[<retry> <expire> ['<url_title>' '<action>']]]";
  866. }
  867. }
  868. }
  869. #------------------------------------------------------------------------------
  870. sub Pushover_SetMessage2 ($$$$) {
  871. my ( $hash, $cmd, $a, $h ) = @_;
  872. my $name = $hash->{NAME};
  873. my %values = ();
  874. Log3 $name, 5, "Pushover $name: called function Pushover_SetMessage2()";
  875. # general values
  876. $values{title} =
  877. $h->{title} ? $h->{title} : AttrVal( $hash->{NAME}, "title", "" );
  878. $values{device} =
  879. $h->{device} ? $h->{device} : AttrVal( $hash->{NAME}, "device", "" );
  880. # message only
  881. if ( $cmd eq "msg" ) {
  882. if ( defined( $h->{message} ) ) {
  883. $values{message} = $h->{message};
  884. }
  885. elsif ( defined( $h->{text} ) ) {
  886. $values{message} = $h->{text};
  887. }
  888. else {
  889. $values{message} = join ' ', @$a;
  890. }
  891. }
  892. $values{priority} =
  893. $h->{priority} ? $h->{priority} : AttrVal( $hash->{NAME}, "priority", 0 );
  894. $values{sound} =
  895. $h->{sound} ? $h->{sound} : AttrVal( $hash->{NAME}, "sound", "" );
  896. $values{timestamp} = $h->{timestamp} ? $h->{timestamp} : undef;
  897. $values{retry} = $h->{retry} ? $h->{retry} : "";
  898. $values{expire} = $h->{expire} ? $h->{expire} : "";
  899. $values{url_title} = $h->{url_title} ? $h->{url_title} : "";
  900. $values{action} =
  901. $h->{action} ? $h->{action} : ( $h->{url} ? $h->{url} : "" );
  902. # glances only
  903. if ( $cmd eq "glance" ) {
  904. if ( defined( $h->{text} ) ) {
  905. $values{text} = $h->{text};
  906. }
  907. elsif ( defined( $h->{message} ) ) {
  908. $values{text} = $h->{message};
  909. }
  910. else {
  911. $values{text} = join ' ', @$a;
  912. }
  913. }
  914. $values{subtext} = $h->{subtext} ? $h->{subtext} : undef;
  915. $values{count} = $h->{count} ? $h->{count} : undef;
  916. $values{percent} = $h->{percent} ? $h->{percent} : undef;
  917. my $callback = (
  918. defined( $attr{$name}{callbackUrl} )
  919. && defined( $hash->{fhem}{infix} )
  920. ? $attr{$name}{callbackUrl}
  921. : ""
  922. );
  923. # check if we got a user or group key as device and use it as
  924. # user-key instead of hash->USER_KEY
  925. if ( $values{device} =~ /^(([A-Za-z0-9]{30}):)?([A-Za-z0-9,_-]*)(.*)$/ ) {
  926. $values{USER_KEY} = $2 if ( $2 ne "" );
  927. $values{device} = $3;
  928. return $hash->{helper}{FAILED_USERKEYS}{ $values{USER_KEY} }
  929. if ( $values{USER_KEY}
  930. && defined( $hash->{helper}{FAILED_USERKEYS}{ $values{USER_KEY} } )
  931. );
  932. }
  933. # Check if all mandatory arguments are filled:
  934. # "message" can not be empty and if "priority" is set to "2" "retry" and
  935. # "expire" must also be set.
  936. # "url_title" and "action" need to be set together and require "expire"
  937. # to be set as well.
  938. if (
  939. (
  940. defined( $values{message} )
  941. || defined( $values{text} )
  942. || defined( $values{subtext} )
  943. || defined( $values{count} )
  944. || defined( $values{percent} )
  945. )
  946. && ( ( $values{retry} ne "" && $values{expire} ne "" )
  947. || $values{priority} < 2 )
  948. && (
  949. (
  950. $values{url_title} ne ""
  951. && $values{action} ne ""
  952. && $values{expire} ne ""
  953. )
  954. || ( $values{url_title} eq "" && $values{action} eq "" )
  955. )
  956. )
  957. {
  958. my $body;
  959. $body = "title=" . urlEncode( $values{title} )
  960. if ( $values{title} ne "" );
  961. if ( $values{message}
  962. && $values{message} =~
  963. /\<(\/|)[biu]\>|\<(\/|)font(.+)\>|\<(\/|)a(.*)\>|\<br\s?\/?\>/
  964. && $values{message} !~ /^nohtml:.*/ )
  965. {
  966. Log3 $name, 4, "Pushover $name: handling message with HTML content";
  967. $body .= "&html=1";
  968. # replace \n by <br /> but ignore \\n
  969. $values{message} =~ s/(?<!\\)(\\n)/<br \/>/g;
  970. }
  971. elsif ( $values{message} && $values{message} =~ /^nohtml:.*/ ) {
  972. Log3 $name, 4,
  973. "Pushover $name: explicitly ignoring HTML tags in message";
  974. $values{message} =~ s/^(nohtml:).*//;
  975. }
  976. if ( $values{message} ) {
  977. # HttpUtil's urlEncode() does not handle \n but would escape %
  978. # so we encode first
  979. $values{message} = urlEncode( $values{message} );
  980. # replace any URL-encoded \n with their hex equivalent but ignore \\n
  981. $values{message} =~ s/(?<!%5c)(%5cn)/%0a/g;
  982. # replace any URL-encoded \\n by \n
  983. $values{message} =~ s/%5c%5cn/%5cn/g;
  984. $body .= "&message=" . $values{message};
  985. }
  986. elsif ( $values{text} ) {
  987. # HttpUtil's urlEncode() does not handle \n but would escape %
  988. # so we encode first
  989. $values{text} = urlEncode( $values{text} );
  990. # replace any URL-encoded \n with their hex equivalent but ignore \\n
  991. $values{text} =~ s/(?<!%5c)(%5cn)/%0a/g;
  992. # replace any URL-encoded \\n by \n
  993. $values{text} =~ s/%5c%5cn/%5cn/g;
  994. $body .= "&text=" . $values{text};
  995. }
  996. if ( $values{subtext} ) {
  997. # HttpUtil's urlEncode() does not handle \n but would escape %
  998. # so we encode first
  999. $values{subtext} = urlEncode( $values{subtext} );
  1000. # replace any URL-encoded \n with their hex equivalent but ignore \\n
  1001. $values{subtext} =~ s/(?<!%5c)(%5cn)/%0a/g;
  1002. # replace any URL-encoded \\n by \n
  1003. $values{subtext} =~ s/%5c%5cn/%5cn/g;
  1004. $body .= "&subtext=" . $values{subtext};
  1005. }
  1006. if ( defined( $values{count} )
  1007. && looks_like_number( $values{count} ) )
  1008. {
  1009. $body .= "&count=" . $values{count};
  1010. }
  1011. if ( defined( $values{percent} )
  1012. && looks_like_number( $values{percent} )
  1013. && $values{percent} >= 0
  1014. && $values{percent} <= 100 )
  1015. {
  1016. $body .= "&percent=" . $values{percent};
  1017. }
  1018. if ( $values{device} ne "" ) {
  1019. $body .= "&device=" . $values{device};
  1020. }
  1021. if ( $values{priority} ne "" ) {
  1022. $values{priority} = 2 if ( $values{priority} > 2 );
  1023. $values{priority} = -2 if ( $values{priority} < -2 );
  1024. $body .= "&priority=" . $values{priority};
  1025. }
  1026. if ( $values{sound} ne "" ) {
  1027. $body .= "&sound=" . $values{sound};
  1028. }
  1029. if ( $values{retry} ne "" ) {
  1030. $body .= "&retry=" . $values{retry};
  1031. }
  1032. if ( $values{expire} ne "" ) {
  1033. $body .= "&expire=" . $values{expire};
  1034. $values{cbNr} = int( time() ) + $values{expire};
  1035. my $cbReading = "cb_" . $values{cbNr};
  1036. until ( ReadingsVal( $name, $cbReading, "" ) eq "" ) {
  1037. $values{cbNr}++;
  1038. $cbReading = "cb_" . $values{cbNr};
  1039. }
  1040. }
  1041. if ( $values{timestamp} ne "" ) {
  1042. $body .= "&timestamp=" . $values{timestamp};
  1043. }
  1044. elsif ( 1 == AttrVal( $hash->{NAME}, "timestamp", 0 ) ) {
  1045. $body .= "&timestamp=" . int( time() );
  1046. }
  1047. if ( $callback ne "" && $values{priority} > 1 ) {
  1048. Log3 $name, 5,
  1049. "Pushover $name: Adding emergency callback URL $callback";
  1050. $body .= "&callback=" . $callback;
  1051. }
  1052. if ( $values{url_title} ne ""
  1053. && $values{action} ne ""
  1054. && $values{expire} ne "" )
  1055. {
  1056. my $url;
  1057. if (
  1058. $callback eq ""
  1059. || ( $values{action} !~ /^http[s]?:\/\/.*$/
  1060. && $values{action} =~ /^[\w-]+:\/\/.*$/ )
  1061. )
  1062. {
  1063. $url = $values{action};
  1064. $values{expire} = "";
  1065. }
  1066. else {
  1067. $url =
  1068. $callback
  1069. . "?acknowledged=1&acknowledged_by="
  1070. . $hash->{USER_KEY}
  1071. . "&FhemCallbackId="
  1072. . $values{cbNr};
  1073. }
  1074. Log3 $name, 5,
  1075. "Pushover $name: Adding supplementary URL '$values{url_title}' ($url) with "
  1076. . "action '$values{action}' (expires after $values{expire} => "
  1077. . "$values{cbNr})";
  1078. $body =
  1079. $body
  1080. . "&url_title="
  1081. . urlEncode( $values{url_title} ) . "&url="
  1082. . urlEncode($url);
  1083. }
  1084. # cleanup callback readings
  1085. my $revReadings;
  1086. while ( ( $key, $value ) = each %{ $hash->{READINGS} } ) {
  1087. if ( $key =~ /^cb_\d+$/ ) {
  1088. my @rBase = split( "_", $key );
  1089. my $rTit = "cbTitle_" . $rBase[1];
  1090. my $rMsg = "cbMsg_" . $rBase[1];
  1091. my $rPrio = "cbPrio_" . $rBase[1];
  1092. my $rAct = "cbAct_" . $rBase[1];
  1093. my $rAck = "cbAck_" . $rBase[1];
  1094. my $rAckAt = "cbAckAt_" . $rBase[1];
  1095. my $rAckBy = "cbAckBy_" . $rBase[1];
  1096. my $rDev = "cbDev_" . $rBase[1];
  1097. Log3 $name, 5,
  1098. "Pushover $name: checking to clean up "
  1099. . $hash->{NAME}
  1100. . " $key: time="
  1101. . $rBase[1] . " ack="
  1102. . ReadingsVal( $name, $rAck, "-" )
  1103. . " curTime="
  1104. . int( time() );
  1105. if ( ReadingsVal( $name, $rAck, 0 ) == 1
  1106. || $rBase[1] <= int( time() ) )
  1107. {
  1108. delete $hash->{READINGS}{$key};
  1109. delete $hash->{READINGS}{$rTit};
  1110. delete $hash->{READINGS}{$rMsg};
  1111. delete $hash->{READINGS}{$rPrio};
  1112. delete $hash->{READINGS}{$rAck};
  1113. delete $hash->{READINGS}{$rDev};
  1114. if ( defined( $hash->{READINGS}{$rAct} ) ) {
  1115. delete $hash->{READINGS}{$rAct};
  1116. }
  1117. if ( defined( $hash->{READINGS}{$rAckAt} ) ) {
  1118. delete $hash->{READINGS}{$rAckAt};
  1119. }
  1120. if ( defined( $hash->{READINGS}{$rAckBy} ) ) {
  1121. delete $hash->{READINGS}{$rAckBy};
  1122. }
  1123. Log3 $name, 4,
  1124. "Pushover $name: cleaned up expired receipt " . $rBase[1];
  1125. }
  1126. }
  1127. }
  1128. Pushover_SendCommand( $hash, "messages.json", $body, %values )
  1129. if ( $cmd eq "msg" );
  1130. Pushover_SendCommand( $hash, "glances.json", $body, %values )
  1131. if ( $cmd eq "glance" );
  1132. return;
  1133. }
  1134. else {
  1135. # There was a problem with the arguments, so tell the user the
  1136. # correct usage of the 'set msg' command
  1137. return
  1138. "Syntax: $name msg <text> [ option1=<value> option2='<value with space>' ... ]";
  1139. }
  1140. }
  1141. #------------------------------------------------------------------------------
  1142. sub Pushover_CGI() {
  1143. my ($request) = @_;
  1144. my $hash;
  1145. my $name = "";
  1146. my $link = "";
  1147. my $URI = "";
  1148. # data received
  1149. if ( $request =~ m,^(/[^/]+?)(?:\&|\?)(.*)?$, ) {
  1150. $link = $1;
  1151. $URI = $2;
  1152. # get device name
  1153. $name = $data{FWEXT}{$link}{deviceName} if ( $data{FWEXT}{$link} );
  1154. $hash = $defs{$name};
  1155. # return error if no such device
  1156. return ( "text/plain; charset=utf-8",
  1157. "NOK No Pushover device for callback $link" )
  1158. unless ($name);
  1159. Log3 $name, 4, "Pushover $name callback: link='$link' URI='$URI'";
  1160. my $webArgs;
  1161. my $receipt = "";
  1162. my $revReadings;
  1163. # extract values from URI
  1164. foreach my $pv ( split( "&", $URI ) ) {
  1165. next if ( $pv eq "" );
  1166. $pv =~ s/\+/ /g;
  1167. $pv =~ s/%([\dA-F][\dA-F])/chr(hex($1))/ige;
  1168. my ( $p, $v ) = split( "=", $pv, 2 );
  1169. $webArgs->{$p} = $v;
  1170. }
  1171. if ( defined( $webArgs->{receipt} ) ) {
  1172. $receipt = $webArgs->{receipt};
  1173. }
  1174. elsif ( defined( $webArgs->{FhemCallbackId} ) ) {
  1175. $receipt = $webArgs->{FhemCallbackId};
  1176. }
  1177. else {
  1178. return ( "text/plain; charset=utf-8",
  1179. "NOK missing argument receipt or FhemCallbackId" );
  1180. }
  1181. # search for existing receipt
  1182. while ( ( $key, $value ) = each %{ $hash->{READINGS} } ) {
  1183. if ( $key =~ /^cb_\d+$/ ) {
  1184. my $val = $value->{VAL};
  1185. $revReadings{$val} = $key;
  1186. }
  1187. }
  1188. if ( defined( $revReadings{$receipt} ) ) {
  1189. my $r = $revReadings{$receipt};
  1190. my @rBase = split( "_", $r );
  1191. my $rAct = "cbAct_" . $rBase[1];
  1192. my $rAck = "cbAck_" . $rBase[1];
  1193. my $rAckAt = "cbAckAt_" . $rBase[1];
  1194. my $rAckBy = "cbAckBy_" . $rBase[1];
  1195. my $rDev = "cbDev_" . $rBase[1];
  1196. return ( "text/plain; charset=utf-8",
  1197. "NOK " . $receipt . ": invalid argument 'acknowledged'" )
  1198. if ( !defined( $webArgs->{acknowledged} )
  1199. || $webArgs->{acknowledged} ne "1" );
  1200. return ( "text/plain; charset=utf-8",
  1201. "NOK " . $receipt . ": invalid argument 'acknowledged_by'" )
  1202. if ( !defined( $webArgs->{acknowledged_by} )
  1203. || $webArgs->{acknowledged_by} ne $hash->{USER_KEY} );
  1204. if ( ReadingsVal( $name, $rAck, 1 ) == 0
  1205. && $rBase[1] > int( time() ) )
  1206. {
  1207. readingsBeginUpdate($hash);
  1208. readingsBulkUpdate( $hash, $rAck, "1" );
  1209. readingsBulkUpdate( $hash, $rAckBy,
  1210. $webArgs->{acknowledged_by} );
  1211. if ( defined( $webArgs->{acknowledged_at} )
  1212. && $webArgs->{acknowledged_at} ne "" )
  1213. {
  1214. readingsBulkUpdate( $hash, $rAckAt,
  1215. $webArgs->{acknowledged_at} );
  1216. }
  1217. else {
  1218. readingsBulkUpdate( $hash, $rAckAt, int( time() ) );
  1219. }
  1220. my $redirect = "";
  1221. # run FHEM command if desired
  1222. if ( ReadingsVal( $name, $rAct, "pushover://" ) !~
  1223. /^[\w-]+:\/\/.*$/ )
  1224. {
  1225. $redirect = "pushover://";
  1226. fhem ReadingsVal( $name, $rAct, "" );
  1227. readingsBulkUpdate( $hash, $rAct,
  1228. "executed: " . ReadingsVal( $name, $rAct, "" ) );
  1229. }
  1230. # redirect to presented URL
  1231. if ( ReadingsVal( $name, $rAct, "none" ) =~ /^[\w-]+:\/\/.*$/ )
  1232. {
  1233. $redirect = ReadingsVal( $name, $rAct, "" );
  1234. }
  1235. readingsEndUpdate( $hash, 1 );
  1236. return (
  1237. "text/html; charset=utf-8",
  1238. "<html><head><meta http-equiv=\"refresh\" content=\"0;url="
  1239. . $redirect
  1240. . "\"></head><body><a href=\""
  1241. . $redirect
  1242. . "\">Click here to get redirected to your destination"
  1243. . "</a></body></html>"
  1244. ) if ( $redirect ne "" );
  1245. }
  1246. else {
  1247. Log3 $name, 4,
  1248. "Pushover $name callback: " . $receipt . " has expired";
  1249. return (
  1250. "text/plain; charset=utf-8",
  1251. "NOK " . $receipt . " has expired"
  1252. );
  1253. }
  1254. }
  1255. else {
  1256. Log3 $name, 4,
  1257. "Pushover $name callback: unable to find existing receipt "
  1258. . $receipt;
  1259. return ( "text/plain; charset=utf-8",
  1260. "NOK unable to find existing receipt " . $receipt );
  1261. }
  1262. }
  1263. # no data received
  1264. else {
  1265. Log3 $name, 5,
  1266. "Pushover $name callback: received malformed request\n$request";
  1267. return ( "text/plain; charset=utf-8", "NOK malformed request" );
  1268. }
  1269. return ( "text/plain; charset=utf-8", "OK" );
  1270. }
  1271. 1;
  1272. ###############################################################################
  1273. =pod
  1274. =item device
  1275. =item summary text message push functionality using the Pushover smartphone app
  1276. =item summary_DE Push Funktion f&uuml;r Textnachrichten &uuml;ber die Pushover Smartphone App
  1277. =begin html
  1278. <a name="Pushover"></a>
  1279. <h3>Pushover</h3>
  1280. <ul>
  1281. Pushover is a service to receive instant push notifications on your
  1282. phone or tablet from a variety of sources.<br>
  1283. You need an account to use this module.<br>
  1284. For further information about the service see <a href="https://pushover.net">pushover.net</a>.<br>
  1285. <br>
  1286. Installation of Perl module IO::Socket::SSL is mandatory to use this module (i.e. via 'cpan -i IO::Socket::SSL').<br>
  1287. It is recommended to install Perl-JSON to make use of advanced functions like supplementary URLs.<br>
  1288. <br>
  1289. Discuss the module <a href="http://forum.fhem.de/index.php/topic,16215.0.html">here</a>.<br>
  1290. <br>
  1291. <br>
  1292. <a name="PushoverDefine"></a>
  1293. <b>Define</b>
  1294. <ul>
  1295. <code>define &lt;name&gt; Pushover &lt;token&gt; &lt;user&gt; [&lt;infix&gt;]</code><br>
  1296. <br>
  1297. You have to <a href="https://pushover.net/login">create an account</a> to get the user key.<br>
  1298. And you have to <a href="https://pushover.net/apps/build">create an application</a> to get the API token.<br>
  1299. <br>
  1300. Attribute infix is optional to define FHEMWEB uri name for Pushover API callback function.<br>
  1301. Callback URL may be set using attribute callbackUrl (see below).<br>
  1302. Note: A uri name can only be used once within each FHEM instance!<br>
  1303. <br>
  1304. Example:
  1305. <ul>
  1306. <code>define Pushover1 Pushover 01234 56789</code>
  1307. </ul>
  1308. <ul>
  1309. <code>define Pushover1 Pushover 01234 56789 pushCallback1</code>
  1310. </ul>
  1311. </ul>
  1312. <br>
  1313. <a name="PushoverSet"></a>
  1314. <b>Set</b>
  1315. <ul><b>msg</b><ul>
  1316. <code>set &lt;Pushover_device&gt; msg &lt;text&gt; [&lt;option1&gt;=&lt;value&gt; &lt;option2&gt;="&lt;value with space in it&gt;" ...]</code>
  1317. <br>
  1318. <br>
  1319. The following options may be used to adjust message content and delivery behavior:<br>
  1320. <br>
  1321. <code><b>message</b>&nbsp;&nbsp;&nbsp;</code> - type: text - Your message text. Using this option takes precedence; non-option text content will be discarded.<br>
  1322. <code><b>device</b>&nbsp;&nbsp;&nbsp;&nbsp;</code> - type: text - Your user's device name to send the message directly to that device, rather than all of the user's devices (multiple devices may be separated by a comma). May also be set to a specific User or Group Key. To address a specific device for a specific User/Group, use User/Group Key first and add device name separated by colon.<br>
  1323. <code><b>title</b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code> - type: text - Your message's title, otherwise your Pushover API app's name is used.<br>
  1324. <code><b>action</b>&nbsp;&nbsp;&nbsp;&nbsp;</code> - type: text - Either a FHEM command to run when user taps link or a <a href="https://pushover.net/api#urls">supplementary URL</a> to show with your message.<br>
  1325. <code><b>url_title</b>&nbsp;</code> - type: text - A title for your FHEM command or supplementary URL, otherwise just the URL is shown.<br>
  1326. <code><b>priority</b>&nbsp;&nbsp;</code> - type: integer - Send as -2 to generate no notification/alert, -1 to always send as a quiet notification, 1 to display as <a href="https://pushover.net/api#priority">high-priority</a> and bypass the user's quiet hours, or 2 to also require confirmation from the user.<br>
  1327. <code><b>retry</b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code> - type: integer - Mandatory in combination with message priority >= 2.<br>
  1328. <code><b>expire</b>&nbsp;&nbsp;&nbsp;&nbsp;</code> - type: integer - Mandatory in combination with message priority >= 2.<br>
  1329. <code><b>timestamp</b>&nbsp;</code> - type: integer - A Unix timestamp of your message's date and time to display to the user, rather than the time your message is received by the Pushover servers. Takes precendence over attribute timestamp=1.<br>
  1330. <code><b>sound</b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code> - type: text - The name of one of the <a href="https://pushover.net/api#sounds">sounds</a> supported by device clients to override the user's default sound choice.<br>
  1331. <br>
  1332. Examples:
  1333. <ul>
  1334. <code>set Pushover1 msg My first Pushover message.</code><br>
  1335. <code>set Pushover1 msg My second Pushover message.\nThis time with two lines.</code><br>
  1336. <code>set Pushover1 msg "Another Pushover message in double quotes."</code><br>
  1337. <code>set Pushover1 msg 'Another Pushover message in single quotes.'</code><br>
  1338. <code>set Pushover1 msg message="Pushover message using explicit option for text content." This part of the text will be ignored.</code><br>
  1339. <code>set Pushover1 msg This is a message with a title. title="This is a subject"</code><br>
  1340. <code>set Pushover1 msg title="This is a subject, too!" This is another message with a title set at the beginning of the command.</code><br>
  1341. <code>set Pushover1 msg title=Emergency priority=2 retry=30 expire=3600 Security issue in living room.</code><br>
  1342. <code>set Pushover1 msg title=Link Have a look to this website: url_title="Open" action="http://fhem.de/" expire=3600</code><br>
  1343. <code>set Pushover1 msg title=Hint expire=3600 This is a reminder to do something. Action will expire in 1h. url_title="Click here for action" action="set device something"</code><br>
  1344. <code>set Pushover1 msg title=Emergency priority=2 retry=30 expire=3600 Security issue in living room. sound=siren url_title="Click here for action" action="set device something"</code><br>
  1345. </ul>
  1346. <br>
  1347. </ul></ul>
  1348. <br>
  1349. <br>
  1350. <ul><b>msg</b> <u>(deprecated format)</u><ul>
  1351. <code>set &lt;Pushover_device&gt; msg [title] &lt;msg&gt; [&lt;device&gt; &lt;priority&gt; &lt;sound&gt; [&lt;retry&gt; &lt;expire&gt; [&lt;url_title&gt; &lt;action&gt;]]]</code>
  1352. <br>
  1353. <br>
  1354. Examples:
  1355. <ul>
  1356. <code>set Pushover1 msg 'This is a text.'</code><br>
  1357. <code>set Pushover1 msg 'Title' 'This is a text.'</code><br>
  1358. <code>set Pushover1 msg 'Title' 'This is a text.' '' 0 ''</code><br>
  1359. <code>set Pushover1 msg 'Emergency' 'Security issue in living room.' '' 2 'siren' 30 3600</code><br>
  1360. <code>set Pushover1 msg 'Hint' 'This is a reminder to do something' '' 0 '' 0 3600 'Click here for action' 'set device something'</code><br>
  1361. <code>set Pushover1 msg 'Emergency' 'Security issue in living room.' '' 2 'siren' 30 3600 'Click here for action' 'set device something'</code><br>
  1362. </ul>
  1363. <br>
  1364. Notes:
  1365. <ul>
  1366. <li>For the first and the second example the corresponding default attributes for the missing arguments must be defined for the device (see attributes section)
  1367. </li>
  1368. <li>If device is empty, the message will be sent to all devices.
  1369. </li>
  1370. <li>If device has a User or Group Key, the message will be sent to this recipient instead. Should you wish to address a specific device here, add it at the end separated by colon.
  1371. </li>
  1372. <li>If sound is empty, the default setting in the app will be used.
  1373. </li>
  1374. <li>If priority is higher or equal 2, retry and expire must be defined.
  1375. </li>
  1376. </ul>
  1377. </ul></ul>
  1378. <br>
  1379. <br>
  1380. <ul><b>glance</b><ul>
  1381. <code>set &lt;Pushover_device&gt; glance [&lt;text&gt;] [&lt;option1&gt;=&lt;value&gt; &lt;option2&gt;="&lt;value with space in it&gt;" ...]</code>
  1382. <br>
  1383. <br>
  1384. Update <a href="https://pushover.net/api/glances">Pushover's glances</a> on Apple Watch.<br>
  1385. The following options may be used to adjust message content and delivery behavior:<br>
  1386. <br>
  1387. <code><b>title</b>&nbsp;&nbsp;&nbsp;</code> - type: text(100 characters) - A description of the data being shown, such as "Widgets Sold".<br>
  1388. <code><b>text</b>&nbsp;&nbsp;&nbsp;&nbsp;</code> - type: text(100 characters) - The main line of data, used on most screens. Using this option takes precedence; non-option text content will be discarded.<br>
  1389. <code><b>subtext</b>&nbsp;</code> - type: text(100 characters) - A second line of data.<br>
  1390. <code><b>count</b>&nbsp;&nbsp;&nbsp;</code> - type: integer(may be negative) - Shown on smaller screens; useful for simple counts.<br>
  1391. <code><b>percent</b>&nbsp;</code> - type: integer(0-100) - Shown on some screens as a progress bar/circle.<br>
  1392. <code><b>device</b>&nbsp;&nbsp;</code> - type: text - Your user's device name to send the message directly to that device, rather than all of the user's devices (multiple devices may be separated by a comma). May also be set to a specific User or Group Key. To address a specific device for a specific User/Group, use User/Group Key first and add device name separated by colon.<br>
  1393. <br>
  1394. </ul></ul>
  1395. <br>
  1396. <b>Get</b> <ul>N/A</ul><br>
  1397. <a name="PushoverAttr"></a>
  1398. <b>Attributes</b>
  1399. <ul>
  1400. <li>
  1401. <a href="#do_not_notify">do_not_notify</a>
  1402. </li>
  1403. <li>
  1404. <a href="#disabledForIntervals">disabledForIntervals</a>
  1405. </li>
  1406. <li>
  1407. <a href="#readingFnAttributes">readingFnAttributes</a>
  1408. </li>
  1409. <li>
  1410. <a name="PushoverAttrcallbackUrl"></a><code>callbackUrl</code><br>
  1411. Set the callback URL to be used to acknowledge messages with emergency priority or supplementary URLs.
  1412. </li>
  1413. <li><a name="PushoverAttrtimestamp"></a><code>timestamp</code><br>
  1414. Send the unix timestamp with each message.
  1415. </li>
  1416. <li><a name="PushoverAttrtitle"></a><code>title</code><br>
  1417. Will be used as title if title is not specified as an argument.
  1418. </li>
  1419. <li><a name="PushoverAttrdevice"></a><code>device</code><br>
  1420. Will be used for the device name if device is not specified as an argument. If left blank, the message will be sent to all devices.
  1421. </li>
  1422. <li><a name="PushoverAttrpriority"></a><code>priority</code><br>
  1423. Will be used as priority value if priority is not specified as an argument. Valid values are -1 = silent / 0 = normal priority / 1 = high priority
  1424. </li>
  1425. <li><a name="PushoverAttrsound"></a><code>sound</code><br>
  1426. Will be used as the default sound if sound argument is missing. If left blank the adjusted sound of the app will be used.
  1427. </li>
  1428. </ul>
  1429. <br>
  1430. <a name="PushoverEvents"></a>
  1431. <b>Generated events:</b>
  1432. <ul>
  1433. N/A
  1434. </ul>
  1435. </ul>
  1436. =end html
  1437. =begin html_DE
  1438. <a name="Pushover"></a>
  1439. <h3>Pushover</h3>
  1440. <ul>
  1441. Pushover ist ein Dienst, um Benachrichtigungen von einer vielzahl
  1442. von Quellen auf Deinem Smartphone oder Tablet zu empfangen.<br>
  1443. Du brauchst einen Account um dieses Modul zu verwenden.<br>
  1444. F&uuml;r weitere Informationen &uuml;ber den Dienst besuche <a href="https://pushover.net">pushover.net</a>.<br>
  1445. <br>
  1446. Die Installation des Perl Moduls IO::Socket::SSL ist Voraussetzung zur Nutzung dieses Moduls (z.B. via 'cpan -i IO::Socket::SSL').<br>
  1447. Es wird empfohlen Perl-JSON zu installieren, um erweiterte Funktion wie Supplementary URLs nutzen zu k&ouml;nnen.<br>
  1448. <br>
  1449. Diskutiere das Modul <a href="http://forum.fhem.de/index.php/topic,16215.0.html">hier</a>.<br>
  1450. <br>
  1451. <br>
  1452. <a name="PushoverDefine"></a>
  1453. <b>Define</b>
  1454. <ul>
  1455. <code>define &lt;name&gt; Pushover &lt;token&gt; &lt;user&gt; [&lt;infix&gt;]</code><br>
  1456. <br>
  1457. Du musst einen <a href="https://pushover.net/login">Account erstellen</a>, um den User Key zu bekommen.<br>
  1458. Und du musst <a href="https://pushover.net/apps/build">eine Anwendung erstellen</a>, um einen API APP_TOKEN zu bekommen.<br>
  1459. <br>
  1460. Das Attribut infix ist optional, um einen FHEMWEB uri Namen f&uuml;r die Pushover API Callback Funktion zu definieren.<br>
  1461. Die Callback URL Callback URL kann dann mit dem Attribut callbackUrl gesetzt werden (siehe unten).<br>
  1462. Hinweis: Eine infix uri can innerhalb einer FHEM Instanz nur einmal verwendet werden!<br>
  1463. <br>
  1464. Beispiel:
  1465. <ul>
  1466. <code>define Pushover1 Pushover 01234 56789</code>
  1467. </ul>
  1468. <ul>
  1469. <code>define Pushover1 Pushover 01234 56789 pushCallback1</code>
  1470. </ul>
  1471. </ul>
  1472. <br>
  1473. <a name="PushoverSet"></a>
  1474. <b>Set</b>
  1475. <ul><b>msg</b><ul>
  1476. <code>set &lt;Pushover_device&gt; msg &lt;text&gt; [&lt;option1&gt;=&lt;value&gt; &lt;option2&gt;="&lt;value with space in it&gt;" ...]</code>
  1477. <br>
  1478. <br>
  1479. Die folgenden Optionen k&ouml;nnen genutzt werden, um den Nachrichteninhalt und die Zustellung zu beeinflussen::<br>
  1480. <br>
  1481. <code><b>message</b>&nbsp;&nbsp;&nbsp;</code> - Typ: Text - Dein Nachrichtentext. Die Nutzung dieser Option hat Vorrang; Text au&szlig;erhalb wird verworfen.<br>
  1482. <code><b>device</b>&nbsp;&nbsp;&nbsp;&nbsp;</code> - Typ: Text - Dein selbst vergebener Ger&auml;tename, um die Nachricht direkt an dieses Ger&auml;t zu senden anstatt an alle Ger&auml;te gleichzeitig (mehrere Ger&auml;te k&ouml;nnen mit Komma getrennt angegeben werden). Hier kann auch explizit ein User oder Group Key angegeben werden. Um gezielt ein Ger&auml;t einer/s speziellen User/Group anzusprechen, zuerst den User/Group Key angeben, gefolgt vom Ger&auml;tenamen und einem Doppelpunkt als Trennzeichen.<br>
  1483. <code><b>title</b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code> - Typ: Text - Dein Nachrichten Titel, andernfalls wird der App Name wie in der Pushover API festgelegt verwendet.<br>
  1484. <code><b>action</b>&nbsp;&nbsp;&nbsp;&nbsp;</code> - Typ: Text - Entweder ein auszuf&uuml;hrendes FHEM Kommando, wenn der Empf&auml;nger den Link anklickt oder eine <a href="https://pushover.net/api#urls">supplementary URL</a>, die mit der Nachricht zusammen angezeigt werden soll.<br>
  1485. <code><b>url_title</b>&nbsp;</code> - Typ: Text - Ein Titel f&uuml;r das FHEM Kommando oder die supplementary URL, andernfalls wird die URL direkt angezeigt.<br>
  1486. <code><b>priority</b>&nbsp;&nbsp;</code> - Type: Integer - Sende mit -2, um keine/n Benachrichtigung/Alarm zu generieren. Sende mit -1, um immer eine lautlose Benachrichtigung zu senden. Sende mit 1, um die Nachricht mit <a href="https://pushover.net/api#priority">hoher Priorit&auml;t</a> anzuzeigen und die Ruhezeiten des Empf&auml;ngers zu umgehen. Oder sende mit 2, um zus&auml;tzlich eine Best&auml;tigung des Empf&auml;ngers anzufordern.<br>
  1487. <code><b>retry</b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code> - Type: Integer - Verpflichtend bei einer Nachrichten Priorit&auml;t >= 2.<br>
  1488. <code><b>expire</b>&nbsp;&nbsp;&nbsp;&nbsp;</code> - Type: Integer - Verpflichtend bei einer Nachrichten Priorit&auml;t >= 2.<br>
  1489. <code><b>timestamp</b>&nbsp;</code> - Type: Integer - Ein Unix Zeitstempfel mit Datum und Uhrzeit deiner Nachricht, die dem Empf&auml;nger statt der Uhrzeit des Einganges auf den Pushover Servern angezeigt wird. Hat Vorrang bei gesetztem Attribut timestamp=1.<br>
  1490. <code><b>sound</b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code> - Typ: Text - Der Name eines vom Empf&auml;ngerger&auml;t unterst&uuml;tzten <a href="https://pushover.net/api#sounds">Klangs</a>, um den vom Empf&auml;nger ausgew&auml;hlten Klang zu &uuml;berschreiben.<br>
  1491. <br>
  1492. Beispiele:
  1493. <ul>
  1494. <code>set Pushover1 msg Meine erste Pushover Nachricht.</code><br>
  1495. <code>set Pushover1 msg Meine zweite Pushover Nachricht.\nDiesmal mit zwei Zeilen.</code><br>
  1496. <code>set Pushover1 msg "Eine andere Pushover Nachricht in doppelten Anf&auml;hrungszeichen."</code><br>
  1497. <code>set Pushover1 msg 'Eine andere Pushover Nachricht in einfachen Anf&auml;hrungszeichen.'</code><br>
  1498. <code>set Pushover1 msg message="Pushover Nachricht, die die explizite Nachrichten Option f&uuml;r den Textinhalt verwendet." Dieser Teil des Textes wird ignoriert.</code><br>
  1499. <code>set Pushover1 msg Dies ist eine Nachricht mit einem Titel. title="Dies ist ein Betreff"</code><br>
  1500. <code>set Pushover1 msg title="Dies ist auch ein Betreff!" Dies ist eine weitere Nachricht mit einem Titel, der am Anfang des Kommandos gesetzt ist.</code><br>
  1501. <code>set Pushover1 msg title=Notfall priority=2 retry=30 expire=3600 Sicherheits-Alarm im Wohnzimmer.</code><br>
  1502. <code>set Pushover1 msg title=Link Schau dir mal diese Website an: url_title="&Ouml;ffnen" action="http://fhem.de/" expire=3600</code><br>
  1503. <code>set Pushover1 msg title=Hinweis expire=3600 Dies ist eine Erinnerung, um etwas zu tun. Der Link verliert in 1h seine G&uuml;ltigkeit. url_title="Hier klicken, um den Befehl auszuf&uuml;hren" action="set device something"</code><br>
  1504. <code>set Pushover1 msg title=Notfall priority=2 retry=30 expire=3600 Sicherheits-Alarm im Wohnzimmer. sound=siren url_title="Hier klicken, um den Befehl auszuf&uuml;hren" action="set device something"</code><br>
  1505. </ul>
  1506. <br>
  1507. </ul></ul>
  1508. <br>
  1509. <br>
  1510. <ul><b>msg</b> <u>(veraltetes Format)</u><ul>
  1511. <code>set &lt;Pushover_device&gt; msg [title] &lt;msg&gt; [&lt;device&gt; &lt;priority&gt; &lt;sound&gt; [&lt;retry&gt; &lt;expire&gt; [&lt;url_title&gt; &lt;action&gt;]]]</code>
  1512. <br>
  1513. <br>
  1514. Beispiele:
  1515. <ul>
  1516. <code>set Pushover1 msg 'Dies ist ein Text.'</code><br>
  1517. <code>set Pushover1 msg 'Titel' 'Dies ist ein Text.'</code><br>
  1518. <code>set Pushover1 msg 'Titel' 'Dies ist ein Text.' '' 0 ''</code><br>
  1519. <code>set Pushover1 msg 'Notfall' 'Sicherheitsproblem im Wohnzimmer.' '' 2 'siren' 30 3600</code><br>
  1520. <code>set Pushover1 msg 'Erinnerung' 'Dies ist eine Erinnerung an etwas' '' 0 '' 0 3600 'Hier klicken, um Aktion auszuf&uuml;hren' 'set device irgendwas'</code><br>
  1521. <code>set Pushover1 msg 'Notfall' 'Sicherheitsproblem im Wohnzimmer.' '' 2 'siren' 30 3600 'Hier klicken, um Aktion auszuf&uuml;hren' 'set device something'</code><br>
  1522. </ul>
  1523. <br>
  1524. Anmerkungen:
  1525. <ul>
  1526. <li>Bei der Verwendung der ersten beiden Beispiele m&uuml;ssen die entsprechenden Attribute als Ersatz f&uuml;r die fehlenden Parameter belegt sein (s. Attribute)
  1527. </li>
  1528. <li>Wenn device leer ist, wird die Nachricht an alle Ger&auml;te geschickt.
  1529. </li>
  1530. <li>Wenn device ein User oder Group Key ist, wird die Nachricht stattdessen hierhin verschickt. M&ouml;chte man trotzdem ein dediziertes Device angeben, trennt man den Namen mit einem Doppelpunkt ab.
  1531. </li>
  1532. <li>Wenn sound leer ist, dann wird die Standardeinstellung in der App verwendet.
  1533. </li>
  1534. <li>Wenn die Priorit&auml;t h&ouml;her oder gleich 2 ist m&uuml;ssen retry und expire definiert sein.
  1535. </li>
  1536. </ul>
  1537. </ul></ul>
  1538. <br>
  1539. <br>
  1540. <ul><b>glance</b><ul>
  1541. <code>set &lt;Pushover_device&gt; glance [&lt;text&gt;] [&lt;option1&gt;=&lt;value&gt; &lt;option2&gt;="&lt;value with space in it&gt;" ...]</code>
  1542. <br>
  1543. <br>
  1544. Aktualisiert die <a href="https://pushover.net/api/glances">Pushover glances</a> auf einer Apple Watch.<br>
  1545. Die folgenden Optionen k&ouml;nnen genutzt werden, um den Nachrichteninhalt und die Zustellung zu beeinflussen::<br>
  1546. <br>
  1547. <code><b>title</b>&nbsp;&nbsp;&nbsp;</code> - type: text(100 characters) - Eine Beschreibung der Daten, die angezeigt werden, beispielsweise "Verkaufte Dinge".<br>
  1548. <code><b>text</b>&nbsp;&nbsp;&nbsp;&nbsp;</code> - type: text(100 characters) - Textzeile, die in den meisten Ansichten verwendet wird. Die Nutzung dieser Option hat Vorrang; Text au&szlig;erhalb wird verworfen.<br>
  1549. <code><b>subtext</b>&nbsp;</code> - type: text(100 characters) - Eine zweite Zeile mit Text.<br>
  1550. <code><b>count</b>&nbsp;&nbsp;&nbsp;</code> - type: integer(may be negative) - Wird auf kleineren Ansichten dargestellt; n&uuml;tzlich f&uuml;r einfache Z&auml;hlerst&auml;nde.<br>
  1551. <code><b>percent</b>&nbsp;</code> - type: integer(0-100) - Wird bei einigen Ansichten als Fortschrittsbalken/-kreis angezeigt.<br>
  1552. <code><b>device</b>&nbsp;&nbsp;</code> - Typ: Text - Dein selbst vergebener Ger&auml;tename, um die Nachricht direkt an dieses Ger&auml;t zu senden anstatt an alle Ger&auml;te gleichzeitig (mehrere Ger&auml;te k&ouml;nnen mit Komma getrennt angegeben werden). Hier kann auch explizit ein User oder Group Key angegeben werden. Um gezielt ein Ger&auml;t einer/s speziellen User/Group anzusprechen, zuerst den User/Group Key angeben, gefolgt vom Ger&auml;tenamen und einem Doppelpunkt als Trennzeichen.<br>
  1553. <br>
  1554. </ul></ul>
  1555. <br>
  1556. <b>Get</b> <ul>N/A</ul><br>
  1557. <a name="PushoverAttr"></a>
  1558. <b>Attributes</b>
  1559. <ul>
  1560. <li>
  1561. <a href="#do_not_notify">do_not_notify</a>
  1562. </li>
  1563. <li>
  1564. <a href="#disabledForIntervals">disabledForIntervals</a>
  1565. </li>
  1566. <li>
  1567. <a href="#readingFnAttributes">readingFnAttributes</a>
  1568. </li>
  1569. <li><a name="PushoverAttrcallbackUrl"></a><code>callbackUrl</code><br>
  1570. Setzt die Callback URL, um Nachrichten mit Emergency Priorit&auml;t zu best&auml;tigen.
  1571. </li>
  1572. <li><a name="PushoverAttrtimestamp"></a><code>timestamp</code><br>
  1573. Sende den Unix-Zeitstempel mit jeder Nachricht.
  1574. </li>
  1575. <li><a name="title"></a><code>title</code><br>
  1576. Wird beim Senden als Titel verwendet, sofern dieser nicht als Aufrufargument angegeben wurde.
  1577. </li>
  1578. <li><a name="PushoverAttrdevice"></a><code>device</code><br>
  1579. Wird beim Senden als Ger&auml;tename verwendet, sofern dieser nicht als Aufrufargument angegeben wurde. Kann auch generell entfallen, bzw. leer sein, dann wird an alle Ger&auml;te gesendet.
  1580. </li>
  1581. <li><a name="PushoverAttrpriority"></a><code>priority</code><br>
  1582. Wird beim Senden als Priorit&auml;t verwendet, sofern diese nicht als Aufrufargument angegeben wurde. Zul&auml;ssige Werte sind -1 = leise / 0 = normale Priorit&auml;t / 1 = hohe Priorit&auml;t
  1583. </li>
  1584. <li><a name="PushoverAttrsound"></a><code>sound</code><br>
  1585. Wird beim Senden als Titel verwendet, sofern dieser nicht als Aufrufargument angegeben wurde. Kann auch generell entfallen, dann wird der eingestellte Ton der App verwendet.
  1586. </li>
  1587. </ul>
  1588. <br>
  1589. <a name="PushoverEvents"></a>
  1590. <b>Generated events:</b>
  1591. <ul>
  1592. N/A
  1593. </ul>
  1594. </ul>
  1595. =end html_DE
  1596. =cut