00_SmartMeterP1.pm 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. ##############################################
  2. # $Id: 00_SmartMeterP1.pm 14594 2017-06-29 06:03:03Z fhemmiv $
  3. #
  4. # Version: 1.8
  5. # Date: 2017-06-29
  6. # Corrected documentation.
  7. #
  8. # Version: 1.7
  9. # Date: 2015-12-29
  10. # Updated documentation for new attribute
  11. #
  12. # Version: 1.6
  13. # Date: 2015-12-29
  14. # Added attribute to select if leading zero's are trimmed off
  15. #
  16. # Version: 1.5
  17. # Date: 2015-12-28
  18. # Added extra values
  19. #
  20. # Version: 1.4
  21. # Date: 2015-12-28
  22. # Fixed problem where wilcard was not removed and
  23. # added power returned values
  24. #
  25. # Version: 1.3
  26. # Date: 2015-12-24
  27. # Removed the wildcard between value and unit
  28. #
  29. # Version: 1.2
  30. # Date: 2015-12-23
  31. # Added code to handle other protocol from smartmeter. (DSMR P1)
  32. package main;
  33. use strict;
  34. use warnings;
  35. use Time::HiRes qw(gettimeofday);
  36. use Data::Dumper;
  37. use DBI;
  38. sub SmartMeterP1_Attr(@);
  39. sub SmartMeterP1_ParseTelegramLine($$$@);
  40. sub SmartMeterP1_Parse($$$$);
  41. sub SmartMeterP1_Read($);
  42. sub SmartMeterP1_Ready($);
  43. sub
  44. SmartMeterP1_Initialize($)
  45. {
  46. my ($hash) = @_;
  47. require "$attr{global}{modpath}/FHEM/DevIo.pm";
  48. # Provider
  49. $hash->{ReadFn} = "SmartMeterP1_Read";
  50. $hash->{ReadyFn} = "SmartMeterP1_Ready";
  51. # Normal devices
  52. $hash->{DefFn} = "SmartMeterP1_Define";
  53. $hash->{UndefFn} = "SmartMeterP1_Undef";
  54. $hash->{AttrFn} = "SmartMeterP1_Attr";
  55. $hash->{AttrList}= "dbName dbHost dbPort dbUser dbPassword write2db:1,0 dbUpdateInterval:0,1,5,10,15,20,25,30,45,60 removeUnitSeparator:false,true removeLeadingZero:false,true " .
  56. $readingFnAttributes;
  57. $hash->{ShutdownFn} = "SmartMeterP1_Shutdown";
  58. }
  59. #####################################
  60. sub
  61. SmartMeterP1_Define($$)
  62. {
  63. my ($hash, $def) = @_;
  64. my @a = split("[ \t][ \t]*", $def);
  65. if(@a != 3) {
  66. my $msg = "wrong syntax: define <name> SmartMeterP1 {none | devicename[\@baudrate]} ";
  67. Log3 undef, 2, $msg;
  68. return $msg;
  69. }
  70. DevIo_CloseDev($hash);
  71. my $name = $a[0];
  72. my $dev = $a[2];
  73. if($dev eq "none") {
  74. Log3 $name, 1, "$name device is none, will use deault /dev/ttyUSB0@115200";
  75. }
  76. $hash->{DeviceName} = $dev;
  77. my $ret = DevIo_OpenDev($hash, 0, "SmartMeterP1_DoInit");
  78. $hash->{Telegram} = {};
  79. $hash->{TelegramStart} = 0;
  80. $hash->{DBH} = undef;
  81. return $ret;
  82. }
  83. #####################################
  84. sub
  85. SmartMeterP1_Undef($$)
  86. {
  87. my ($hash, $arg) = @_;
  88. my $name = $hash->{NAME};
  89. foreach my $d (sort keys %defs) {
  90. if(defined($defs{$d}) &&
  91. defined($defs{$d}{IODev}) &&
  92. $defs{$d}{IODev} == $hash)
  93. {
  94. my $lev = ($reread_active ? 4 : 2);
  95. Log3 $name, $lev, "deleting port for $d";
  96. delete $defs{$d}{IODev};
  97. }
  98. }
  99. DevIo_CloseDev($hash);
  100. $hash->{DBH}->disconnect if (defined($hash->{DBH}));
  101. return undef;
  102. }
  103. #####################################
  104. sub
  105. SmartMeterP1_Shutdown($)
  106. {
  107. my ($hash) = @_;
  108. return undef;
  109. }
  110. #####################################
  111. sub
  112. SmartMeterP1_DoInit($)
  113. {
  114. my $hash = shift;
  115. my $name = $hash->{NAME};
  116. my $err;
  117. my $msg = undef;
  118. readingsSingleUpdate($hash, "state", "Initialized", 1);
  119. return undef;
  120. }
  121. #####################################
  122. # called from the global loop, when the select for hash->{FD} reports data
  123. sub
  124. SmartMeterP1_Read($)
  125. {
  126. my ($hash) = @_;
  127. my $buf = DevIo_SimpleRead($hash);
  128. return "" if(!defined($buf));
  129. my $name = $hash->{NAME};
  130. my $culdata = $hash->{PARTIAL};
  131. Log3 $name, 5, "SmartMeterP1/RAW: $culdata/$buf";
  132. $culdata .= $buf;
  133. while($culdata =~ m/\n/) {
  134. my $rmsg;
  135. ($rmsg,$culdata) = split("\n", $culdata, 2);
  136. $rmsg =~ s/\r//;
  137. SmartMeterP1_Parse($hash, $hash, $name, $rmsg) if($rmsg);
  138. }
  139. $hash->{PARTIAL} = $culdata;
  140. }
  141. sub
  142. ConvertTelegramTime($)
  143. {
  144. my $inTime = shift;
  145. $inTime =~ s/(..)(..)(..)(..)(..)(..)[SW]*/20$1\-$2\-$3 $4\:$5\:$6/;
  146. return $inTime;
  147. }
  148. sub
  149. SmartMeterP1_Connect2DB($$$)
  150. {
  151. my ($hash,$name,$write2db) = @_;
  152. if ($write2db == 1) {
  153. if (defined($hash->{DBH})) {
  154. $hash->{DBH}->disconnect;
  155. }
  156. my $dbHost = AttrVal($name,"dbHost",undef);
  157. my $dbPort = AttrVal($name,"dbPort",3306);
  158. my $dbName = AttrVal($name,"dbName",undef);
  159. my $dbUser = AttrVal($name,"dbUser",undef);
  160. my $dbPassword = AttrVal($name,"dbPassword",undef);
  161. Log3 $name, 5, "SmartMeterP1:Connecting to DBI:mysql:database=$dbName;host=$dbHost;port=$dbPort,$dbUser,$dbPassword";
  162. if (defined($dbHost) && defined($dbPort) && defined($dbName) && defined($dbUser) && defined($dbPassword)) {
  163. $hash->{DBH} = DBI->connect("DBI:mysql:database=$dbName;host=$dbHost;port=$dbPort",$dbUser,$dbPassword);
  164. if (!defined($hash->{DBH})) {
  165. Log3 $name, 1, "ERROR connecting to database: ".DBI->errstr;
  166. }
  167. else {
  168. Log3 $name, 3, "SmartMeterP1:Connected to database DBI:mysql:database=$dbName;host=$dbHost;port=$dbPort;user=$dbUser";
  169. $hash->{dbInsertSQL} = $hash->{DBH}->prepare('INSERT INTO smartmeter (`date`,obis_ref,value,unit) VALUES (?,?,?,?)')
  170. }
  171. }
  172. }
  173. }
  174. sub
  175. SmartMeterP1_Write2DB($$$$$)
  176. {
  177. my ($hash,$name,$obis_ref,$date,$valueStr) = @_;
  178. SmartMeterP1_Connect2DB($hash,$name,AttrVal($name,"write2db",0)) if (!defined($hash->{DBH}));
  179. if (!defined($hash->{DBH})) {
  180. Log3 $name, 4, "SmartMeterP1: Error reconnecting to database.";
  181. return;
  182. }
  183. my $dateValue = myStr2Date($date);
  184. my $interval = AttrVal($name,"dbUpdateInterval", 5);
  185. if (defined($hash->{$obis_ref})) {
  186. return if ($dateValue < ($hash->{$obis_ref} + ($interval*60)));
  187. }
  188. $hash->{$obis_ref} = $dateValue;
  189. my $value;
  190. my $unit;
  191. if ( AttrVal($name,"removeUnitSeparator", "false") eq "true" ) {
  192. ($value,$unit) = split(/ /, $valueStr, 2);
  193. }
  194. else {
  195. ($value,$unit) = split(/\*/, $valueStr, 2);
  196. }
  197. $hash->{dbInsertSQL}->execute($date,$obis_ref,$value,$unit);
  198. if (DBI->err) {
  199. SmartMeterP1_Connect2DB($hash,$name,AttrVal($name,"write2db",0));
  200. if (defined($hash->{DBH})) {
  201. $hash->{dbInsertSQL}->execute($date,$obis_ref,$value,$unit);
  202. }
  203. else {
  204. Log3 $name, 1, "SmartMeterP1: Error reconnecting to database.";
  205. return;
  206. }
  207. }
  208. $hash->{dbInsertSQL}->finish;
  209. }
  210. sub
  211. RemoveLeadingZero($$)
  212. {
  213. my ($name, $value) = @_;
  214. if ( AttrVal($name,"removeLeadingZero", "false") eq "true" ) {
  215. $value =~ s/^(0*)?([0-9]+\..*)/$2/;
  216. }
  217. return $value;
  218. }
  219. sub
  220. RemoveUnitSeparator($$)
  221. {
  222. my ($name, $value) = @_;
  223. if ( AttrVal($name,"removeUnitSeparator", "false") eq "true" ) {
  224. $value =~ s/(.*)?\*(.*)/$1 $2/;
  225. }
  226. $value = RemoveLeadingZero($name,$value);
  227. return $value;
  228. }
  229. sub
  230. SmartMeterP1_ParseTelegramLine($$$@)
  231. {
  232. my ($hash,$name,$obis_ref,@attributes) = @_;
  233. Log3 $name, 4, " Telegram obis: $obis_ref";
  234. Log3 $name, 4, " Telegram attr: ".Dumper(@attributes);
  235. if ($obis_ref eq "0-0:1.0.0") {
  236. #Date-time stamp of the P1 message
  237. # YYMMDDhhmmssW
  238. $hash->{TelegramTime} = ConvertTelegramTime($attributes[0]);
  239. readingsSingleUpdate($hash,"TelegramTime",$hash->{TelegramTime},1);
  240. }
  241. elsif ($obis_ref eq "1-0:1.8.1") {
  242. # Meter reading electricity delivered to client low Tariff in 0,001 kWh
  243. $hash->{".updateTimestamp"} = $hash->{TelegramTime};
  244. my $tmp = ReadingsVal($name,"ElectricityDeliveredLowTariff", "-");
  245. $attributes[0] = RemoveUnitSeparator($name, $attributes[0]);
  246. readingsSingleUpdate($hash,"ElectricityDeliveredLowTariff",$attributes[0],1) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  247. SmartMeterP1_Write2DB($hash,$name,$obis_ref,$hash->{TelegramTime},$attributes[0]) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  248. }
  249. elsif ($obis_ref eq "1-0:1.8.2") {
  250. # Meter reading electricity delivered to client normal Tariff in 0,001 kWh
  251. $hash->{".updateTimestamp"} = $hash->{TelegramTime};
  252. my $tmp = ReadingsVal($name,"ElectricityDeliveredNormalTariff", "-");
  253. $attributes[0] = RemoveUnitSeparator($name, $attributes[0]);
  254. readingsSingleUpdate($hash,"ElectricityDeliveredNormalTariff",$attributes[0],1) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  255. SmartMeterP1_Write2DB($hash,$name,$obis_ref,$hash->{TelegramTime},$attributes[0]) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  256. }
  257. elsif ($obis_ref eq "1-0:2.8.1") {
  258. # Meter reading electricity delivered by client low Tariff in 0,001 kWh
  259. $hash->{".updateTimestamp"} = $hash->{TelegramTime};
  260. my $tmp = ReadingsVal($name,"ElectricityProducedLowTariff", "-");
  261. $attributes[0] = RemoveUnitSeparator($name, $attributes[0]);
  262. readingsSingleUpdate($hash,"ElectricityProducedLowTariff",$attributes[0],1) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  263. SmartMeterP1_Write2DB($hash,$name,$obis_ref,$hash->{TelegramTime},$attributes[0]) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  264. }
  265. elsif ($obis_ref eq "1-0:2.8.2") {
  266. # Meter reading electricity delivered by client normal Tariff in 0,001 kWh
  267. $hash->{".updateTimestamp"} = $hash->{TelegramTime};
  268. my $tmp = ReadingsVal($name,"ElectricityProducedNormalTariff", "-");
  269. $attributes[0] = RemoveUnitSeparator($name, $attributes[0]);
  270. readingsSingleUpdate($hash,"ElectricityProducedNormalTariff",$attributes[0],1) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  271. SmartMeterP1_Write2DB($hash,$name,$obis_ref,$hash->{TelegramTime},$attributes[0]) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  272. }
  273. elsif ($obis_ref eq "1-0:1.7.0") {
  274. # Actual electricity power delivered (+P) in 1 Watt resolution
  275. $hash->{".updateTimestamp"} = $hash->{TelegramTime};
  276. my $tmp = ReadingsVal($name,"ElectricityPowerDelivered", "-");
  277. $attributes[0] = RemoveUnitSeparator($name, $attributes[0]);
  278. readingsSingleUpdate($hash,"ElectricityPowerDelivered",$attributes[0],1) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  279. SmartMeterP1_Write2DB($hash,$name,$obis_ref,$hash->{TelegramTime},$attributes[0]) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  280. }
  281. elsif ($obis_ref eq "1-0:2.7.0") {
  282. # Actual electricity power received (-P) in 1 Watt resolution
  283. $hash->{".updateTimestamp"} = $hash->{TelegramTime};
  284. my $tmp = ReadingsVal($name,"ElectricityPowerProduced", "-");
  285. $attributes[0] = RemoveUnitSeparator($name, $attributes[0]);
  286. readingsSingleUpdate($hash,"ElectricityPowerProduced",$attributes[0],1) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  287. SmartMeterP1_Write2DB($hash,$name,$obis_ref,$hash->{TelegramTime},$attributes[0]) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  288. }
  289. elsif ($obis_ref eq "0-0:17.0.0") {
  290. # The actual threshold Electricity in kW
  291. $hash->{".updateTimestamp"} = $hash->{TelegramTime};
  292. my $tmp = ReadingsVal($name,"ElectricityThreshold", "-");
  293. $attributes[0] = RemoveUnitSeparator($name, $attributes[0]);
  294. readingsSingleUpdate($hash,"ElectricityThreshold",$attributes[0],1) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  295. SmartMeterP1_Write2DB($hash,$name,$obis_ref,$hash->{TelegramTime},$attributes[0]) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  296. }
  297. elsif ($obis_ref eq "0-0:96.14.0") {
  298. # Tariff indicator electricity.
  299. $hash->{".updateTimestamp"} = $hash->{TelegramTime};
  300. my $tmp = ReadingsVal($name,"TariffIndicatorElectricity", "-");
  301. $attributes[0] = RemoveUnitSeparator($name, $attributes[0]);
  302. readingsSingleUpdate($hash,"TariffIndicatorElectricity",$attributes[0],1) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  303. SmartMeterP1_Write2DB($hash,$name,$obis_ref,$hash->{TelegramTime},$attributes[0]) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  304. }
  305. elsif ($obis_ref eq "0-0:96.3.10") {
  306. # Switch position electricity
  307. $hash->{".updateTimestamp"} = $hash->{TelegramTime};
  308. my $tmp = ReadingsVal($name,"SwitchPositionElectricity", "-");
  309. $attributes[0] = RemoveUnitSeparator($name, $attributes[0]);
  310. readingsSingleUpdate($hash,"SwitchPositionElectricity",$attributes[0],1) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  311. SmartMeterP1_Write2DB($hash,$name,$obis_ref,$hash->{TelegramTime},$attributes[0]) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  312. }
  313. elsif ($obis_ref eq "0-1:24.2.1") {
  314. # Last hourly value gas delivered to client in m3
  315. $hash->{".updateTimestamp"} = ConvertTelegramTime($attributes[0]);
  316. my $tmp = ReadingsVal($name,"GasDeliveredTime", "-");
  317. if (($tmp eq "-") || ($tmp ne ConvertTelegramTime($attributes[0]))) {
  318. readingsSingleUpdate($hash,"GasDeliveredTime",ConvertTelegramTime($attributes[0]),1);
  319. $attributes[1] = RemoveUnitSeparator($name, $attributes[1]);
  320. readingsSingleUpdate($hash,"GasDelivered",$attributes[1],1);
  321. SmartMeterP1_Write2DB($hash,$name,$obis_ref,ConvertTelegramTime($attributes[0]),$attributes[1]);
  322. }
  323. }
  324. elsif ($obis_ref eq "0-1:24.3.0") {
  325. $hash->{Telegram}->{Gas}->{used} = 1;
  326. $hash->{Telegram}->{Gas}->{time} = ConvertTelegramTime($attributes[0]);
  327. $hash->{Telegram}->{Gas}->{obis_ref} = $attributes[4];
  328. $hash->{Telegram}->{Gas}->{unit} = $attributes[5];
  329. }
  330. elsif ($obis_ref eq "") {
  331. if (($hash->{Telegram}->{Gas}->{used} == 1) && ($hash->{Telegram}->{Gas}->{obis_ref} eq "0-1:24.2.1" )) {
  332. # Last hourly value gas delivered to client in m3
  333. $hash->{".updateTimestamp"} = $hash->{Telegram}->{Gas}->{time};
  334. my $tmp = ReadingsVal($name,"GasDeliveredTime", "-");
  335. $attributes[0] = RemoveUnitSeparator($name, $attributes[0]);
  336. if (($tmp eq "-") || ($tmp ne $hash->{Telegram}->{Gas}->{time})) {
  337. readingsSingleUpdate($hash,"GasDeliveredTime",$hash->{Telegram}->{Gas}->{time},1);
  338. if ( AttrVal($name,"removeUnitSeparator", "false") eq "true" ) {
  339. readingsSingleUpdate($hash,"GasDelivered",$attributes[0]." ".$hash->{Telegram}->{Gas}->{unit},1);
  340. SmartMeterP1_Write2DB($hash,$name,$obis_ref,$hash->{Telegram}->{Gas}->{time},$attributes[0]." ".$hash->{Telegram}->{Gas}->{unit});
  341. }
  342. else {
  343. readingsSingleUpdate($hash,"GasDelivered",$attributes[0]."*".$hash->{Telegram}->{Gas}->{unit},1);
  344. SmartMeterP1_Write2DB($hash,$name,$obis_ref,$hash->{Telegram}->{Gas}->{time},$attributes[0]."*".$hash->{Telegram}->{Gas}->{unit});
  345. }
  346. }
  347. $hash->{Telegram}->{Gas}->{used} = 0;
  348. }
  349. }
  350. elsif ($obis_ref eq "0-1:24.4.0") {
  351. # Valve position Gas
  352. $hash->{".updateTimestamp"} = $hash->{TelegramTime};
  353. my $tmp = ReadingsVal($name,"ValvePositionGas", "-");
  354. $attributes[0] = RemoveUnitSeparator($name, $attributes[0]);
  355. readingsSingleUpdate($hash,"ValvePositionGas",$attributes[0],1) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  356. SmartMeterP1_Write2DB($hash,$name,$obis_ref,$hash->{TelegramTime},$attributes[0]) if (($tmp eq "-") || ($tmp ne $attributes[0]));
  357. }
  358. }
  359. sub
  360. SmartMeterP1_Parse($$$$)
  361. {
  362. my ($hash, $iohash, $name, $rmsg) = @_;
  363. Log3 $name, 5, "SmartMeterP1/Telegramline: $rmsg";
  364. if ($rmsg =~ m/^\/(.*)/) {
  365. $hash->{TelegramStart} = 1;
  366. $hash->{Telegram} = {};
  367. $hash->{Telegram}->{Gas} = {};
  368. $hash->{Telegram}->{Gas}->{used} = 0;
  369. Log3 $name, 4, " Telegram start: $1";
  370. }
  371. else {
  372. if ($hash->{TelegramStart} == 1) {
  373. if ($rmsg =~ m/^\!(....)/) {
  374. Log3 $name, 4, " Telegram end: $1";
  375. }
  376. elsif ($rmsg =~ m/^\!/) {
  377. Log3 $name, 4, " Telegram end.";
  378. }
  379. else {
  380. Log3 $name, 4, " Telegram line: $rmsg";
  381. my $obis_ref;
  382. my @attributes;
  383. ($obis_ref,@attributes) = split('\(', $rmsg);
  384. my $count = 0;
  385. while ($count < @attributes) {
  386. $attributes[$count] =~ s/\)//;
  387. $count++;
  388. }
  389. SmartMeterP1_ParseTelegramLine($hash,$name,$obis_ref,@attributes);
  390. }
  391. }
  392. }
  393. }
  394. #####################################
  395. sub
  396. SmartMeterP1_Ready($)
  397. {
  398. my ($hash) = @_;
  399. return DevIo_OpenDev($hash, 1, "SmartMeterP1_DoInit")
  400. if($hash->{STATE} eq "disconnected");
  401. # This is relevant for windows/USB only
  402. my $po = $hash->{USBDev};
  403. my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags);
  404. if($po) {
  405. ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
  406. }
  407. return ($InBytes && $InBytes>0);
  408. }
  409. sub
  410. SmartMeterP1_Attr(@)
  411. {
  412. my ($cmd,$name,$aName,$aVal) = @_;
  413. my $hash = $defs{$name};
  414. Log3 $name, 3, "SmartMeterP1:Updating attribute '$aName' to '$aVal'";
  415. if (($aName eq "write2db") || ($aName eq "dbHost") || ($aName eq "dbPort") || ($aName eq "dbName") || ($aName eq "dbUser") || ($aName eq "dbPassword")) {
  416. # Something in the db attributes changed reconnect.
  417. if (defined($hash->{DBH})) {
  418. $hash->{DBH}->disconnect;
  419. undef $hash->{DBH};
  420. }
  421. # if ($aName eq "write2db") {
  422. # SmartMeterP1_Connect2DB($hash,$name,$aVal);
  423. # }
  424. # else {
  425. # SmartMeterP1_Connect2DB($hash,$name,AttrVal($name,"write2db",0));
  426. # }
  427. }
  428. return undef;
  429. }
  430. 1;
  431. =pod
  432. =item device
  433. =item summary Read data from your Electricity and Gas smart meter
  434. =begin html
  435. <a name="SmartMeterP1"></a>
  436. <h3>SmartMeterP1</h3>
  437. <ul>
  438. <table>
  439. <tr><td>
  440. The SmartMeterP1 is a module which can interpret the data received from
  441. a Smart Meter used to keep track of electricity and gas usage.<br><br>
  442. Currently it can proces P1 protocol DSMR 4.0. Probably also others but
  443. not tested.<br>
  444. Tested with a Landys+Gyr E350 and a Iskra - ME382.<br><br>
  445. Note: This module may require the <code>Device::SerialPort</code> or
  446. <code>Win32::SerialPort</code> module if you attach the device via USB
  447. and the OS sets strange default parameters for serial devices.<br><br>
  448. </td><td>
  449. <img src="Landis-Gyr-E350-meter.jpg"/>
  450. </td></tr>
  451. </table>
  452. <a name="SmartMeterP1define"></a>
  453. <b>Define</b>
  454. <ul>
  455. <code>define &lt;name&gt; SmartMeterP1 &lt;device&gt;</code> <br>
  456. <br>
  457. USB-connected device (P1 USB-Serial cable):<br><ul>
  458. &lt;device&gt; specifies the serial port to read the incoming data from.
  459. The name of the serial-device depends on your distribution, under
  460. linux the ftdi-sio kernel module is responsible, and usually a
  461. /dev/ttyUSB0 device will be created.<br><br>
  462. You can specify a baudrate of 115200, e.g.: /dev/ttyUSB0@115200<br><br>
  463. For the Landys+Gyr E350 use: define SmartMeterP1 SmartMeterP1 /dev/ttyUSB0@115200<BR>
  464. For the Iskra - ME382 use: define SmartMeterP1 SmartMeterP1 /dev/p1usb@9600,7,E,1<BR><BR>
  465. </ul>
  466. <br>
  467. If the device is called none, then no device will be opened, so you
  468. can experiment without hardware attached.<br>
  469. </ul>
  470. <br>
  471. <a name="SmartMeterP1attr"></a>
  472. <b>Attributes</b>
  473. <ul>
  474. <li><a name="write2db">write2db</a><br>
  475. If you would like to store your read data into a mysql database you can activate
  476. it with this setting. Allowed values are:<BR>
  477. 0 - Do not write to datbase (default)<BR>
  478. 1 - Write to database<BR><BR>
  479. If you want to write to a database you need to specify also the following attributes:<BR><BR>
  480. <code>dbHost<BR>
  481. dbName<BR>
  482. dbPassword<BR>
  483. dbPort<BR>
  484. dbUpdateInterval<BR>
  485. dbUser</code>
  486. <BR><BR>
  487. And create a table in your database called 'smartmeter with the following syntax:<BR>
  488. <code>CREATE TABLE `smartmeter` (
  489. `date` datetime NOT NULL,
  490. `obis_ref` varchar(45) COLLATE utf8_bin NOT NULL,
  491. `value` float DEFAULT NULL,
  492. `unit` varchar(45) COLLATE utf8_bin DEFAULT NULL,
  493. PRIMARY KEY (`date`,`obis_ref`)
  494. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin</code>
  495. </li><br>
  496. <li><a name="dbHost">dbHost</a><br>
  497. The hostname or ip address of the MySQL server.
  498. </li><br>
  499. <li><a name="dbPort">dbPort</a><br>
  500. The TCP port the MySQL server is listening on. Default is 3306.
  501. </li><br>
  502. <li><a name="dbName">dbName</a><br>
  503. The name of the dabase to use.
  504. </li><br>
  505. <li><a name="dbUsername">dbUsername</a><br>
  506. The name of the MySQL use which has read and write access to the database
  507. and table 'smartmeter'.
  508. </li><br>
  509. <li><a name="dbPassword">dbPassword</a><br>
  510. Password of the MySQL user.
  511. </li><br>
  512. <li><a name="dbUpdateInterval">dbUpdateInterval</a><br>
  513. How often should the measured value be written to the database.<BR>
  514. This value is in minutes.<BR><BR>
  515. So when a new value is read from the smartmeter the time will be checked
  516. to the time of the last value written to the database. If the difference is
  517. bigger than this interval the value will be written to the database.<BR><BR>
  518. With this value you can control how much and how fast data is written into your database.
  519. </li><br>
  520. <li><a name="removeUnitSeparator">removeUnitSeparator</a><br>
  521. When set to true it will replace the unit asterisk separator by a space character.
  522. So 00900.701*m3 becomes 00900.701 m3
  523. </li><br>
  524. <li><a name="removeLeadingZero">removeLeadingZero</a><br>
  525. When set to true it will remove all leading zeros in a value.
  526. So 00900.701 m3 becomes 900.701 m3 and <BR>
  527. 0000.123 kWh becomes 0.123 kWh
  528. </li><br>
  529. </ul>
  530. <br>
  531. </ul>
  532. =end html
  533. =cut