| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650 |
- ########################################################################################
- #
- # OWX_CCC.pm
- #
- # FHEM module providing hardware dependent functions for the COC/CUNO interface of OWX
- #
- # Prof. Dr. Peter A. Henning
- #
- # $Id: 11_OWX_CCC.pm 16671 2018-04-29 05:06:35Z phenning $
- #
- ########################################################################################
- #
- # Provides the following methods for OWX
- #
- # Define
- # Detect
- # Alarms
- # Complex
- # Discover
- # Open
- # Close
- # Reopen
- # Init
- # Read
- # ReadLow
- # Ready
- # Reset
- # Verify
- # Write
- #
- ########################################################################################
- #
- # $hash->{DeviceName} =
- # $hash->{INTERFACE} = "COC/CUNO/CUBE";
- # $hash->{HWDEVICE} =
- # $hash->{TYPE} = "OWX";
- #
- ########################################################################################
- package OWX_CCC;
- use strict;
- use warnings;
- ########################################################################################
- #
- # Constructor
- #
- ########################################################################################
- sub new($) {
- my ($class,$hash) = @_;
- return bless {
- hash => $hash,
- #-- module version
- version => "7.11"
- }, $class;
- }
- ########################################################################################
- #
- # Define - Implements Define method
- #
- # Parameter def = definition string
- #
- # Return undef if ok, otherwise error message
- #
- ########################################################################################
- sub Define($) {
- my ($self,$def) = @_;
- my $hash = $self->{hash};
-
- my @a = split("[ \t][ \t]*", $def);
- #-- check syntax
- if(int(@a) < 3){
- return "OWX_CCC::Define Syntax error - must be define <name> OWX <cuno/coc/cube-device>"
- }
-
- my $name = $a[0];
- $hash->{NAME} = $name;
- my $dev = $a[2];
- $hash->{DeviceName} = $dev;
-
- #-- Second step in case of CUNO: See if we can open it
- my $msg = "OWX_CCC::Define COC/CUNO/CUBE device $dev";
- #-- hash des COC/CUNO
- my $hwdevice = $main::defs{$dev};
- if(!$hwdevice){
- main::Log3 $name,1, $msg." not defined";
- return $msg." not defined";
- }
-
- main::Log(1,$msg." defined");
-
- #-- store with OWX device
- $hash->{DeviceName} = $dev;
- $hash->{ASYNCHRONOUS} = 0;
- $hash->{INTERFACE} = "COC/CUNO/CUBE";
- $hash->{HWDEVICE} = $hwdevice;
-
- #-- loop for some time until the state is "Initialized"
- for(my $i=0;$i<6;$i++){
- last if( $hwdevice->{STATE} eq "Initialized");
- main::Log(1,"OWX_CCC::Define Waiting, at t=$i ".$dev." is still ".$hwdevice->{STATE});
- select(undef,undef,undef,3);
- }
- main::Log(1, "OWX_CCC::Define Can't open ".$dev) if( $hwdevice->{STATE} ne "Initialized");
- #-- reset the 1-Wire system in COC/CUNO/CUBE
- main::CUL_SimpleWrite($hwdevice, "Oi");
-
- main::Log3 $name,1,"OWX_CCC::Define warning: version ".$self->{version}." not identical to OWX version ".$main::owx_version
- if( $self->{version} ne $main::owx_version);
-
- #-- call low level init function for the device
- $self->Init();
- return undef;
- }
- ########################################################################################
- #
- # Detect - Find out if we have the proper interface
- #
- # Return 1 if ok, otherwise 0
- #
- ########################################################################################
- sub Detect () {
- my ($self) = @_;
- my $hash = $self->{hash};
-
- my ($ret,$ress);
- my $name = $hash->{NAME};
- my $ress0 = "OWX_CCC::Detect: 1-Wire bus $name interface ";
- $ress = $ress0;
- #-- get the interface
- my $interface;
- my $hwdevice = $hash->{HWDEVICE};
-
- select(undef,undef,undef,2);
- #-- type of interface
- main::CUL_SimpleWrite($hwdevice, "V");
- select(undef,undef,undef,0.01);
- my ($err,$ob) = ReadLow($hwdevice);
- #my $ob = CallFn($owx_hwdevice->{NAME}, "GetFn", $owx_hwdevice, (" ", "raw", "V"));
- #-- process result for detection
- if( !defined($ob)){
- $ob="";
- $ret=0;
- #-- COC
- }elsif( $ob =~ m/.*CSM.*/){
- $interface="COC";
- $ress .= "DS2482 / COC detected in $hwdevice->{NAME}";
- $ret=1;
- #-- CUNO
- }elsif( $ob =~ m/.*CUNO.*/){
- $interface="CUNO";
- $ress .= "DS2482 / CUNO detected in $hwdevice->{NAME}";
- $ret=1;
- #-- CUBE
- }elsif( $ob =~ m/.*CUBE.*/){
- $interface="CUBE";
- $ress .= "DS2482 / CUBE detected in $hwdevice->{NAME}";
- $ret=1;
- #-- something else
- } else {
- $ret=0;
- }
- #-- treat the failure cases
- if( $ret == 0 ){
- $interface=undef;
- $ress .= "in $hwdevice->{NAME} could not be addressed, return was $ob";
- }
- #-- store with OWX device
- $hash->{INTERFACE} = $interface;
- main::Log(1, $ress);
- return $ret;
- }
- ########################################################################################
- #
- # Alarms - Find devices on the 1-Wire bus, which have the alarm flag set
- #
- # Return 0 because not implemented here.
- #
- ########################################################################################
- sub Alarms () {
- my ($self) = @_;
-
- return 0;
- }
- ########################################################################################
- #
- # Complex - Send match ROM, data block and receive bytes as response
- #
- # Parameter dev = ROM ID of device
- # data = string to send
- # numread = number of bytes to receive
- #
- # Return response, if OK
- # 0 if not OK
- #
- ########################################################################################
- sub Complex ($$$) {
- my ($self,$dev,$data,$numread) =@_;
- my $hash = $self->{hash};
-
- my $select;
- my $res = "";
-
- #-- get the interface
- my $hwdevice = $hash->{HWDEVICE};
- my $name = $hash->{NAME};
-
- #-- has match ROM part
- if( $dev ){
- #-- ID of the device
- my $owx_rnf = substr($dev,3,12);
- my $owx_f = substr($dev,0,2);
- #-- 8 byte 1-Wire device address
- my @rom_id =(0,0,0,0 ,0,0,0,0);
- #-- from search string to reverse string id
- $dev=~s/\.//g;
- for(my $i=0;$i<8;$i++){
- $rom_id[7-$i]=substr($dev,2*$i,2);
- }
- $select=sprintf("Om%s%s%s%s%s%s%s%s",@rom_id);
- main::Log3 $name,5,"OWX_CCC::Complex: sending match ROM to COC/CUNO ".$select;
- #--
- main::CUL_SimpleWrite($hwdevice, $select);
- my ($err,$ob) = ReadLow($hwdevice);
- #-- padding first 9 bytes into result string, since we have this
- # in the serial interfaces as well
- $res .= "000000000";
- }
- #-- has data part
- if ( $data ){
- $self->Write($data,0);
- $res .= $data;
- }
- #-- has receive part
- if( $numread > 0 ){
- #$numread += length($data);
- main::Log3 $name,5,"OWX_CCC::Complex: COC/CUNO is expected to deliver $numread bytes";
- $res.=$self->Read($numread);
- }
- return $res;
- }
- ########################################################################################
- #
- # Discover - Discover devices on the 1-Wire bus via internal firmware
- #
- # Return 0 : error
- # 1 : OK
- #
- ########################################################################################
- sub Discover () {
- my ($self) = @_;
- my $hash = $self->{hash};
-
- my $res;
-
- #-- get the interface
- my $hwdevice = $hash->{HWDEVICE};
- my $name = $hash->{NAME};
-
- #-- zero the array
- @{$hash->{DEVS}}=();
- #-- reset the busmaster
- $self->Init();
- #-- get the devices
- main::CUL_SimpleWrite($hwdevice, "Oc");
- select(undef,undef,undef,0.5);
- my ($err,$ob) = ReadLow($hwdevice);
- if( $ob ){
- # main::Log(3,"OWX_CCC::Discover: ".$hwdevice->{NAME}." device search returns ".$ob);
- foreach my $dx (split(/\n/,$ob)){
- next if ($dx !~ /^\d\d?\:[0-9a-fA-F]{16}/);
- $dx =~ s/\d+\://;
- my $ddx = substr($dx,14,2).".";
- #-- reverse data from culfw
- for( my $i=1;$i<7;$i++){
- $ddx .= substr($dx,14-2*$i,2);
- }
- $ddx .= ".".substr($dx,0,2);
- push (@{$hash->{DEVS}},$ddx);
- }
- return 1;
- } else {
- main::Log3 $name,1, "OWX_CCC::Discover No answer to device search";
- return 0;
- }
- }
- ########################################################################################
- #
- # Open - Open Device
- #
- ########################################################################################
- sub Open () {
- my ($self) = @_;
- my $hash = $self->{hash};
- }
- ########################################################################################
- #
- # Close - Close Device
- #
- ########################################################################################
- sub Close () {
- my ($self) = @_;
- my $hash = $self->{hash};
- }
- ########################################################################################
- #
- # Reopen - Reopen Device
- #
- ########################################################################################
- sub Reopen () {
- main::Log 1,"[OWX_CCC] Warning: ->Reopen currently not defined";
- }
- ########################################################################################
- #
- # Init - Low Level Init of the 1-wire device
- #
- # Return 1 : OK
- # 0 : not OK
- #
- ########################################################################################
- sub Init () {
- my ($self) = @_;
- my $hash = $self->{hash};
- my $dev = $hash->{DeviceName};
- my $name = $hash->{NAME};
-
- #main::Log3 $name,1,"OWX_CCC::Init called on device $dev for bus $name, state is ".$hash->{STATE};
-
- #-- get the interface
- my $hwdevice = $hash->{HWDEVICE};
-
- my $ob = main::CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "ORm"));
- #--
- # main::CUL_SimpleWrite($hwdevice, "ORm");
- # select(undef,undef,undef,0.01);
- # my ($err,$ob) = ReadLow($hwdevice);
- #main::Log3 $name,1,"OWX_CCC::Init gives ob=$ob ";
- if( !defined($ob) ){
- #main::Log 1,"empty ORm";
- return "empty ORm";
- }elsif( length($ob) < 13){
- #main::Log 1,"short ORm of length ".length($ob);
- return "short ORm of length ".length($ob);
- }elsif( substr($ob,9,4) eq "OK" ){
- #main::Log 1,"=====> OK";
- $hash->{STATE} = "opened";
- return undef;
- }else{
- #main::Log 1,"=====> ORm empty -> still OK ???";
- $hash->{STATE} = "opened";
- return undef;
- }
- }
- #######################################################################################
- #
- # Read - Implement the Read function
- #
- # Parameter numexp = expected number of bytes
- #
- #######################################################################################
- sub Read(@) {
- my ($self,$numexp) = @_;
- my $hash = $self->{hash};
- my $name = $hash->{NAME};
- my $state = $hash->{STATE};
- my $buffer = "";
- my $numget = 0;
- my ($err,$ob);
- my $try;
- my $maxtry = $numexp;
-
- #-- get the interface
- my $hwdevice = $hash->{HWDEVICE};
-
- for($try=0;$try<$maxtry;$try++){
- #main::Log(1, "Sending $hwdevice->{NAME}: OrB";
- #my $ob = CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "OrB"));
- main::CUL_SimpleWrite($hwdevice, "OrB");
- ($err,$ob) = main::CUL_ReadAnswer($hwdevice,$name,0,undef);
- #select(undef,undef,undef,0.01);
- #($err,$ob) = ReadLow($hwdevice);
- #-- process results
- if( !(defined($ob)) ){
- return "";
- #-- four bytes received makes one byte of result
- }elsif( length($ob) == 4 ){
- $buffer .= sprintf("%c",hex(substr($ob,0,2)));
- $numget++;
- #-- 11 bytes received makes one byte of result
- }elsif( length($ob) == 11 ){
- $buffer .= sprintf("%c",hex(substr($ob,9,2)));
- $numget++;
- #-- 18 bytes received from CUNO
- }elsif( length($ob) == 18 ){
- main::OWX_WDBGL($name,4,"OWX_CCC::Read 18 bytes from CUNO: ",$ob);
- #-- 20 bytes received = leftover from match
- }elsif( length($ob) == 20 ){
- $maxtry++;
- }else{
- main::Log3 $name,1,"OWX_CCC::Read unexpected number of ".length($ob)." bytes on bus ".$hwdevice->{NAME};
- }
- }
- if( $numget >= $numexp){
- main::OWX_WDBGL($name,4,"OWX_CCC::Read from CUNO with error=$err: ",$buffer);
- return $buffer
- #-- ultimate failure
- }else{
- main::Log3 $name, 1,"OWX_CCC::Read $name: $numget of $numexp bytes with error=$err at state $state, this is an unrecoverable error";
- #main::DevIo_Disconnected($hwdevice);
- return "";
- }
- }
- ########################################################################################
- #
- # ReadLow - Replacement for CUL_ReadAnswer for better control
- #
- # Parameter: hash = hash of bus master
- #
- # Return: string received
- #
- ########################################################################################
- sub ReadLow($)
- {
- my ($hwdevice) = @_;
-
- my $type = $hwdevice->{TYPE};
- my $name = $hwdevice->{NAME};
- my $arg ="";
- my $anydata=0;
- my $regexp =undef;
-
- my ($mculdata, $rin) = ("", '');
- my $buf;
- my $to = 3; # 3 seconds timeout
- $to = $hwdevice->{RA_Timeout} if($hwdevice->{RA_Timeout}); # ...or less
- for(;;) {
- return ("Device lost when reading answer for get $arg", undef)
- if(!$hwdevice->{FD});
- vec($rin, $hwdevice->{FD}, 1) = 1;
- my $nfound = select($rin, undef, undef, $to);
- if($nfound < 0) {
- next if ($! == EAGAIN() || $! == EINTR() || $! == 0);
- my $err = $!;
- #main::DevIo_Disconnected($hwdevice);
- main::Log 1,"============================> DISCOINNECTING";
- return("ReadLow $arg: $err", undef);
- }
- return ("Timeout reading answer for get $arg", undef)
- if($nfound == 0);
- $buf = main::DevIo_SimpleRead($hwdevice);
- return ("No data", undef) if(!defined($buf));
- if($buf) {
- main::Log3 $name,5, "OWX_CCC::ReadLow $buf";
- $mculdata .= $buf;
- }
- # \n\n is socat special
- if($mculdata =~ m/\r\n/ || $anydata || $mculdata =~ m/\n\n/ ) {
- if($regexp && $mculdata !~ m/$regexp/) {
- main::CUL_Parse($hwdevice, $hwdevice, $hwdevice->{NAME}, $mculdata, $hwdevice->{initString});
- } else {
- return (undef, $mculdata)
- }
- }
- }
- }
- ########################################################################################
- #
- # Ready - Implement the Ready function
- #
- # Return 1 : OK
- # 0 : not OK
- #
- ########################################################################################
- sub Ready () {
- my ($self) = @_;
- my $hash = $self->{hash};
- my $name = $hash->{NAME};
- my $success;
- $success = main::DevIo_OpenDev($hash,1,"main::OWX_Init")
- if($hash->{STATE} eq "disconnected");
-
- return $success;
- }
- ########################################################################################
- #
- # Reset - Reset the 1-Wire bus
- #
- # Return 1 : OK
- # 0 : not OK
- #
- ########################################################################################
- sub Reset () {
- my ($self) = @_;
- my $hash = $self->{hash};
-
- #-- get the interface
- my $hwdevice = $hash->{HWDEVICE};
-
- my $ob = main::CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "ORb"));
-
- if( substr($ob,9,4) eq "OK:1" ){
- return 1;
- }else{
- return 0
- }
- }
- ########################################################################################
- #
- # Verify - Verify a particular device on the 1-Wire bus
- #
- # Parameter dev = 8 Byte ROM ID of device to be tested
- #
- # Return 1 : device found
- # 0 : device not
- #
- ########################################################################################
- sub Verify ($) {
- my ($self,$dev) = @_;
- my $hash = $self->{hash};
-
- my $i;
-
- #-- get the interface
- my $hwdevice = $hash->{HWDEVICE};
-
- #-- Ask the COC/CUNO
- main::CUL_SimpleWrite($hwdevice, "OCf");
- #-- sleeping for some time
- select(undef,undef,undef,3);
- main::CUL_SimpleWrite($hwdevice, "Oc");
- select(undef,undef,undef,0.5);
- my ($err,$ob) = $self->($hwdevice);
- if( $ob ){
- foreach my $dx (split(/\n/,$ob)){
- next if ($dx !~ /^\d\d?\:[0-9a-fA-F]{16}/);
- $dx =~ s/\d+\://;
- my $ddx = substr($dx,14,2).".";
- #-- reverse data from culfw
- for( my $i=1;$i<7;$i++){
- $ddx .= substr($dx,14-2*$i,2);
- }
- $ddx .= ".".substr($dx,0,2);
- return 1 if( $dev eq $ddx);
- }
- }
- return 0;
- }
- #######################################################################################
- #
- # Write - Implement the write function
- #
- # Parameter cmd = string to be sent
- # reset = 1 if initial bus reset has to be done
- #
- ########################################################################################
- sub Write(@) {
- my ($self,$cmd, $reset) = @_;
- my $hash = $self->{hash};
- my $name = $hash->{NAME};
- my $count_out;
-
- if($hash->{STATE} eq "disconnected"){
- main::Log3 $name,4,"OWX_CCC::Write attempted to disconnected device $name";
- return undef;
- }
-
- #-- if necessary, perform a reset operation
- $self->Reset()
- if( $reset );
-
- my ($i,$j,$k);
- my $res = "";
- #-- get the interface
- my $hwdevice = $hash->{HWDEVICE};
-
- for( $i=0;$i<length($cmd);$i++){
- $j = int(ord(substr($cmd,$i,1))/16);
- $k = ord(substr($cmd,$i,1))%16;
- $res = sprintf "OwB%1x%1x ",$j,$k;
- main::CUL_SimpleWrite($hwdevice, $res);
- }
- main::OWX_WDBGL($name,5,"OWX_CCC::Write Sending out ",$cmd);
- }
- 1;
- =pod
- =item device
- =item summary to address an OWX interface device via COC/CUNO
- =item summary_DE zur Adressierung eines OWX Interface Device mit COC/CUNO
- =begin html
- <a name="OWX_CCC"></a>
- <h3>OWX_CCC</h3>
- <ul>
- See <a href="/fhem/docs/commandref.html#OWX">OWX</a>
- </ul>
- =end html
- =begin html_DE
- <a name="OWX_CCC"></a>
- <h3>OWX_CCC</h3>
- <ul>
- <a href="http://fhemwiki.de/wiki/Interfaces_f%C3%BCr_1-Wire">Deutsche Dokumentation im Wiki</a> vorhanden, die englische Version gibt es hier: <a href="/fhem/docs/commandref.html#OWX">OWX</a>
- </ul>
- =end html_DE
- =cut
|