#################################################################
# $Id: 14_FLAMINGO.pm 27979 2023-09-18 18:00:56Z HomeAuto_User $
#################################################################
# The module was taken over by an unknown maintainer!
# It is part of the SIGNALduinos project.
# https://github.com/RFD-FHEM/RFFHEM
# 
# 2018      - takeover from unknown maintainer
# 2018-2022 - HomeAuto_User, elektron-bbs
#################################################################
# FLAMINGO FA20RF
# get sduino_dummy raw MU;;P0=-1384;;P1=815;;P2=-2725;;P3=-20001;;P4=8159;;P5=-891;;D=01010121212121010101210101345101210101210101212101010101012121212101010121010134510121010121010121210101010101212121210101012101013451012101012101012121010101010121212121010101210101345101210101210101212101010101012121212101010121010134510121010121010121;;CP=1;;O;;
# FLAMINGO FA21RF
# get sduino_dummy raw MS;;P0=-1413;;P1=757;;P2=-2779;;P3=-16079;;P4=8093;;P5=-954;;D=1345121210101212101210101012121012121210121210101010;;CP=1;;SP=3;;R=33;;O;;
# FLAMINGO FA22RF
# get sduino_dummy raw MU;;P0=-5684;;P1=8149;;P2=-887;;P3=798;;P4=-1393;;P5=-2746;;P6=-19956;;D=0123434353534353434343434343435343534343534353534353612343435353435343434343434343534353434353435353435361234343535343534343434343434353435343435343535343536123434353534353434343434343435343534343534353534353612343435353435343434343434343534353434353435;;CP=3;;R=0;;
# LM-101LD
# get sduino_dummy raw MS;;P1=-2708;;P2=796;;P3=-1387;;P4=-8477;;P5=8136;;P6=-904;;D=2456212321212323232321212121212121212123212321212121;;CP=2;;SP=4;;
#################################################################
# note / ToDo´s / Bugs:
# - 
#################################################################

package main;

use strict;
use warnings;
use FHEM::Meta;

our $VERSION = '20221227';
our $readingFnAttributes;
our %modules;
our $init_done;

my %sets = (
  'Testalarm:noArg',
  'Counterreset:noArg',
);

my %models = (
  'FA20RF',
  'FA21RF',
  'FA22RF',
  'KD-101LA',
  'LM-101LD',
  'unknown',
);


#####################################
sub FLAMINGO_Initialize {
  my ($hash) = @_;

  $hash->{Match}     = '^P13\.?1?#[A-Fa-f0-9]+';
  $hash->{SetFn}     = \&FLAMINGO_Set;
  $hash->{DefFn}     = \&FLAMINGO_Define;
  $hash->{UndefFn}   = \&FLAMINGO_Undef;
  $hash->{ParseFn}   = \&FLAMINGO_Parse;
  $hash->{AttrList}  =  'IODev do_not_notify:0,1 showtime:0,1 ignore:0,1 '.
                        'model:'.join(q{,}, sort %models).q{ } .
                        $readingFnAttributes;
  $hash->{AutoCreate}=
  {
    'FLAMINGO.*' => { ATTR => 'event-on-change-reading:.* event-min-interval:.*:300', FILTER => '%NAME', GPLOT => q{} },
  };

  return FHEM::Meta::InitMod( __FILE__, $hash );
}

#####################################
sub FLAMINGO_Define {
  my ($hash, $def) = @_;
  my @a = split("[ \t][ \t]*", $def);

  # Argument                             0      1        2     3          4
  return 'ERROR: wrong syntax, define <name> FLAMINGO <code> <model> <optional IODev>' if(int(@a) < 3 || int(@a) > 5);
  ### check hex code ###
  return 'ERROR: wrong hex length ' . length($a[2]) . ', you need 6'if not length($a[2]) == 6;
  return 'ERROR: wrong hex value ' . $a[2] if not ($a[2] =~ /^[0-9a-fA-F]{6}$/xms);
  ### check model ###
  return 'ERROR: wrong model: '.$a[3] . "\n\n(allowed modelvalues: " . join(' | ', sort %models).')' if $a[3] && ( !grep { $_ eq $a[3] } %models );

  $hash->{CODE}     = $a[2];
  $hash->{lastMSG}  = 'no data';
  $hash->{bitMSG}   = 'no data';

  $modules{FLAMINGO}{defptr}{$a[2]} = $hash;
  $hash->{STATE} = 'Defined';

  my $name = $hash->{NAME};
  my $iodev;
  if ($a[4]) { $iodev = $a[4]; };
  if (exists $modules{FLAMINGO}{defptr}{ioname} && !$iodev) { $iodev = $modules{FLAMINGO}{defptr}{ioname}; };

  ### Attributes ###
  if ( $init_done == 1 ) {
    if ($a[3]) { CommandAttr($hash,"$name model $a[3]") ;};
    if (not $a[3]) { CommandAttr($hash,"$name model unknown") ;};
  }

  AssignIoPort($hash,$iodev);   ## sucht nach einem passenden IO-Gerät (physikalische Definition)

  return;
}

#####################################
sub FLAMINGO_Undef {
  my ($hash, $name) = @_;

  RemoveInternalTimer($hash, 'FLAMINGO_UpdateState');
  if($hash && $hash->{CODE}) {
    delete($modules{FLAMINGO}{defptr}{$hash->{CODE}});
  };

  if ( exists $modules{FLAMINGO}{defptr}{testrunning} ) {
    delete($modules{FLAMINGO}{defptr}{testrunning});
  }
  return;
}

#####################################
sub FLAMINGO_Set {
  my ( $hash, $name, @args ) = @_;

  my $ret = undef;
  my $message;
  my $list;
  my $model = AttrVal($name, 'model', 'unknown');
  my $iodev = $hash->{IODev}{NAME};

  $list = join (q{ }, %sets);
  return "ERROR: wrong command! (only $list)"  if ($args[0] ne '?' && $args[0] ne 'Testalarm' && $args[0] ne 'Counterreset');
  
  if ($args[0] eq '?') {
    if ($model eq 'unknown') {
      $ret = q{};    # no set if model unknown or no model attribut
    } else {
      $ret = $list;
    }
  }

  my $hlen = length($hash->{CODE});
  my $blen = $hlen * 4;
  my $bitData= unpack("B$blen", pack("H$hlen", $hash->{CODE}));

  my $bitAdd = substr($bitData,23,1);             # for last bit, is needed to send

  ## use the protocol ID how receive last message
  my $sendID = ReadingsVal($name, 'lastReceive_ID', q{});    # for send command, because ID´s can vary / MU / MS message

  $message = 'P'.$sendID.'#'.$bitData.$bitAdd.'P#R55';

  ## Send Message to IODev and wait for correct answer  
  if ($args[0] ne '?') {
    Log3 $hash, 3, "FLAMINGO set $name $args[0]";
  }

  ## Counterreset ##
  if ($args[0] eq 'Counterreset') {
    readingsSingleUpdate($hash, 'alarmcounter', 0, 1);
  }

  ## Testarlarm ##  
  if ($args[0] ne '?' and $args[0] ne 'Counterreset') {

    # remove InternalTimer
    RemoveInternalTimer($hash, 'FLAMINGO_UpdateState');

    $modules{FLAMINGO}{defptr}{testrunning} = 'yes';            # marker, device send Testalarm to NOT register this alarm with other receivers in FHEM
    Log3 $hash, 4, 'FLAMINGO set marker TESTALARM is running';

    readingsSingleUpdate($hash, 'state', 'Testalarm', 1);
    Log3 $hash, 4, "$iodev: FLAMINGO send raw Message: $message";

    IOWrite($hash, 'sendMsg', $message);

    InternalTimer(gettimeofday()+15, 'FLAMINGO_UpdateState', $hash, 0);   # set timer to Update status
  }

  return $ret;
}

#####################################
sub FLAMINGO_Parse {
  my ($iohash, $msg) = @_;
  #my $name = $iohash->{NAME};
  my ($protocol,$rawData) = split('#',$msg);
  $protocol=~ s/^[P](\d+)/$1/xms; # extract protocol

  my $iodev = $iohash->{NAME};
  $modules{FLAMINGO}{defptr}{ioname} = $iodev;  

  my $hlen = length($rawData);
  my $blen = $hlen * 4;
  my $bitData= unpack("B$blen", pack("H$hlen", $rawData));

  my $deviceCode = $rawData;    # Message is in hex "4d4efd"

  my $def = $modules{FLAMINGO}{defptr}{$deviceCode};
  if(!$def) {
    $def = $modules{FLAMINGO}{defptr}{$deviceCode};
  }
  my $hash = $def;

  #my $model = AttrVal($name, 'model', 'unknown');

  if(!$def) {
    Log3 $iohash, 1, "FLAMINGO UNDEFINED sensor detected, code $deviceCode, protocol $protocol";
    return "UNDEFINED FLAMINGO_$deviceCode FLAMINGO $deviceCode";
  }

  my $name = $hash->{NAME};
  if(IsIgnored($name)) {
    return q{};
  }

  $hash->{bitMSG} = $bitData;
  $hash->{lastMSG} = $rawData;
  $hash->{lastReceive} = time();

  readingsSingleUpdate($hash, 'lastReceive_ID', $protocol, 0);    # to save lastReceive_ID for send command

  ## check if Testalarm received from a other transmitter in FHEM ##
  my $testalarmcheck = q{};
  if ( exists $modules{FLAMINGO}{defptr}{testrunning} ) {
    $testalarmcheck = $modules{FLAMINGO}{defptr}{testrunning};
  }

  if ($testalarmcheck eq 'yes') {
    return q{};
  }

  my $alarmcounter = ReadingsVal($name, 'alarmcounter', 0);

  if (ReadingsVal($name, 'state', q{}) ne 'Alarm') {
    $alarmcounter = $alarmcounter+1;  
  }

  Log3 $name, 5, "$iodev: FLAMINGO actioncode: $deviceCode";
  Log3 $name, 4, "$iodev: FLAMINGO $name: is receiving Alarm (Counter $alarmcounter)";

  # remove InternalTimer
  RemoveInternalTimer($hash, 'FLAMINGO_UpdateState');

  readingsBeginUpdate($hash);
  readingsBulkUpdate($hash, 'state', 'Alarm');
  readingsBulkUpdate($hash, 'alarmcounter', $alarmcounter);       # register non testalarms how user can set via FHEM
  readingsEndUpdate($hash, 1); # Notify is done by Dispatch

  InternalTimer(gettimeofday()+15, 'FLAMINGO_UpdateState', $hash, 0);   # set timer to Update status

  return $name;
}

#####################################
sub FLAMINGO_UpdateState {
  my ($hash) = @_;
  my $name = $hash->{NAME};

  readingsBeginUpdate($hash);
  readingsBulkUpdate($hash, 'state', 'no Alarm');
  readingsEndUpdate($hash, 1); # Notify is done by Dispatch

  ## delete marker device Testalarm ##
  if ( exists $modules{FLAMINGO}{defptr}{testrunning} ) {
    Log3 $hash, 4, 'FLAMINGO delete marker TESTALARM was running';
    delete($modules{FLAMINGO}{defptr}{testrunning})
  };

  Log3 $name, 4, "FLAMINGO: $name: Alarm stopped";
  return;
}


1;

=pod
=item summary    Supports flamingo fa20rf/fa21 smoke detectors
=item summary_DE Unterst&uumltzt Flamingo FA20RF/FA21/FA22RF/LM-101LD Rauchmelder
=begin html

<a name="FLAMINGO"></a>
<h3>FLAMINGO</h3>
<ul>
  The FLAMINGO module interprets FLAMINGO FA20RF/FA21/FA22RF type of messages received by the SIGNALduino.<br>
  Of this smoke detector, there are identical types profitec KD101LA, POLLIN KD101LA or renkforce LM-101LD.
  <br><br>

  <a name="FLAMINGOdefine"></a>
  <b>Define</b>
  <ul>
    <code>define &lt;name&gt; FLAMINGO &lt;code&gt;</code> <br>

    <br>
    <li>&lt;code&gt; is the unic code of the autogenerated address of the FLAMINGO device. This changes, after pairing to the master</li>
    <li>&lt;model&gt; is the model name</li><br>
    - if autocreate, the defined model is <code>unknown</code>.<br>
    - with manual <code>define</code> you can choose the model which is available as attribute.
  </ul>
  <br><br>

  <a name="FLAMINGOset"></a>
  <b>Set</b>
  <ul>
    <li>Counterreset<br>
    - set alarmcounter to 0</li>
    <li>Testalarm<br>
    - trigger a test alarm (The testalarm does not increase the alarm ounter!)</li>
  </ul><br>

  <a name="FLAMINGOget"></a>
  <b>Get</b> <ul>N/A</ul><br>

  <a name="FLAMINGOattr"></a>
  <b>Attributes</b>
  <ul>
    <li><a href="#IODev">IODev (!)</a></li>
    <li><a href="#do_not_notify">do_not_notify</a></li>
    <li><a href="#eventMap">eventMap</a></li>
    <li><a href="#ignore">ignore</a></li>
    <a name="model"></a>
    <li>model<br>
    FA20RF, FA21RF, FA22RF, KD-101LA, LM-101LD, unknown</li>
    <a name="showtime"></a>
    <li><a href="#showtime">showtime</a></li>
    <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  </ul>
  <br><br>
  <b><u>Generated readings</u></b><br>
  - alarmcounter | counter started with 0<br>
  - lastReceive_ID | the protocol ID from SIGNALduino<br>
  - state | (no Alarm, Alarm, Testalaram)<br>
  <br><br>
  <u><b>manual<br></b></u>
  <b>Pairing (Master-Slave)</b>  
  <ul>
    <li>Determine master<br>
    LEARN button push until the green LED lights on</li>
    <li>Determine slave<br>
    LEARN button push until the red LED lights on</li>
    <li>Master, hold down the TEST button until an alarm signal generated at all "Slaves"</li>
  </ul><br>
  <b>Standalone</b>
  <ul>
    <li>LEARN button push until the green LED lights on</li>
    <li>TEST button hold down until an alarm signal generated</li>
  </ul>
</ul>

=end html

=begin html_DE

<a name="FLAMINGO"></a>
<h3>FLAMINGO</h3>
<ul>
  Das FLAMINGO module dekodiert vom SIGNALduino empfangene Nachrichten des FLAMINGO FA20RF / FA21 / FA22RF Rauchmelders.<br>
  Von diesem Rauchmelder gibt es baugleiche Typen wie profitec KD101LA, POLLIN KD101LA oder renkforce LM-101LD.
  <br><br>

  <a name="FLAMINGOdefine"></a>
  <b>Define</b>
  <ul>
    <code>define &lt;name&gt; FLAMINGO &lt;code&gt;  &lt;model&gt; </code> <br>

    <br>
    <li>&lt;code&gt; ist der automatisch angelegte eindeutige code  des FLAMINGO Rauchmelders. Dieser &auml;ndern sich nach
    dem Pairing mit einem Master.</li>
    <li>&lt;model&gt; ist die Modelbezeichnung</li><br>
    - Bei einem Autocreate wird als Model <code>unknown</code> definiert.<br>
    - Bei einem manuellen <code>define</code> kann man das Model frei w&auml;hlen welche als Attribut verf&uuml;gbar sind .
  </ul>
  <br><br>

  <a name="FLAMINGOset"></a>
  <b>Set</b>
  <ul>
    <li>Counterreset<br>
    - Alarmz&auml;hler auf 0 setzen</li>
    <li>Testalarm<br>
    - ausl&ouml;sen eines Testalarmes. (Der Testalarm erh&ouml;ht nicht den Alarmz&auml;hler!)</li>
  </ul><br>

  <a name="FLAMINGOget"></a>
  <b>Get</b> <ul>N/A</ul><br><br>

  <a name="FLAMINGOattr"></a>
  <b>Attributes</b>
  <ul>
    <li><a href="#IODev">IODev (!)</a></li>
    <li><a href="#do_not_notify">do_not_notify</a></li>
    <li><a href="#eventMap">eventMap</a></li>
    <li><a href="#ignore">ignore</a></li>
    <a name="model"></a>
    <li>model<br>
    FA20RF, FA21RF, FA22RF, KD-101LA, LM-101LD, unknown</li>
    <a name="showtime"></a>
    <li><a href="#showtime">showtime</a></li>
    <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  </ul>
  <br><br>
  <b><u>Generierte Readings</u></b><br>
  - alarmcounter | Alarmz&auml;hler beginnend mit 0<br>
  - lastReceive_ID | Protokoll ID vom SIGNALduino<br>
  - state | (no Alarm, Alarm, Testalaram)<br>
  <br><br>
  <u><b>Anleitung<br></b></u>
  <b>Melder paaren (Master-Slave Prinzip)</b>
  <ul>
    <li>Master bestimmen<br>
    LEARN-Taste bis gr&uuml;ne Anzeige LED leuchtet</li>
    <li>Slave bestimmen<br>
    LEARN-Taste bis rote Anzeige LED leuchtet</li>
    <li>Master, TEST-Taste gedr&uuml;ckt halten, bevor LEDś abschalten und alles "Slaves" ein Alarmsignal erzeugen</li>
  </ul><br>
  <b>Paarung aufheben / Standalone Betrieb</b>
  <ul>
    <li>LEARN-Taste bis gr&uuml;ne Anzeige LED leuchtet</li>
    <li>TEST-Taste gedr&uuml;ckt halten bis ein Alarmsignal erzeugt wird</li>
  </ul>
</ul>

=end html_DE
=for :application/json;q=META.json 14_FLAMINGO.pm
{
  "abstract": "Supports flamingo fa20rf/fa21 smoke detectors",
  "author": [
    "Homeautouser <>",
    "elektron-bbs <>"
  ],
  "x_fhem_maintainer": [
    "HomeAuto_User"
  ],
  "x_fhem_maintainer_github": [
    "homeautouser",
    "Sidey79",
    "elektron-bbs"
  ],
  "description": "The FLAMINGO module interprets FLAMINGO FA20RF/FA21/FA22RF type of messages received by the SIGNALduino",
  "dynamic_config": 1,
  "keywords": [
    "fhem-sonstige-systeme",
    "fhem-hausautomations-systeme",
    "fhem-mod",
    "signalduino",
    "flamingo",
    "smoke",
    "sensor"
	  ],
  "license": [
    "GPL_2"
  ],
  "meta-spec": {
    "url": "https://metacpan.org/pod/CPAN::Meta::Spec",
    "version": 2
  },
  "name": "FHEM::FLAMINGO",
  "prereqs": {
    "runtime": {
      "requires": {
      }
    },
    "develop": {
      "requires": {
      }
    }
  },
  "release_status": "stable",
  "resources": {
    "bugtracker": {
      "web": "https://github.com/RFD-FHEM/RFFHEM/issues/"
    },
    "x_testData": [
      {
        "url": "https://raw.githubusercontent.com/RFD-FHEM/RFFHEM/master/t/FHEM/14_FLAMINGO/testData.json",
        "testname": "Testdata with FLAMINGO smoke sensors"
      }
    ],
    "repository": {
      "x_master": {
        "type": "git",
        "url": "https://github.com/RFD-FHEM/RFFHEM.git",
        "web": "https://github.com/RFD-FHEM/RFFHEM/tree/master"
      },
      "type": "svn",
      "url": "https://svn.fhem.de/fhem",
      "web": "https://svn.fhem.de/trac/browser/trunk/fhem/FHEM/14_FLAMINGO.pm",
      "x_branch": "trunk",
      "x_filepath": "fhem/FHEM/",
      "x_raw": "https://svn.fhem.de/trac/export/latest/trunk/fhem/FHEM/14_FLAMINGO.pm"
    },
    "x_support_community": {
      "board": "Sonstige Systeme",
      "boardId": "29",
      "cat": "FHEM - Hausautomations-Systeme",
      "description": "Sonstige Hausautomations-Systeme",
      "forum": "FHEM Forum",
      "rss": "https://forum.fhem.de/index.php?action=.xml;type=rss;board=29",
      "title": "FHEM Forum: Sonstige Systeme",
      "web": "https://forum.fhem.de/index.php/board,29.0.html"
    }
  }
}
=end :application/json;q=META.json
=cut
