36_Vallox.pm 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124
  1. ###############################################################
  2. # $Id: 36_Vallox.pm 17170 2018-08-19 06:12:22Z Skjall $
  3. #
  4. # @File 36_Vallox.pm
  5. #
  6. # @Author Skjall
  7. # @Created 21.07.2016 10:18:23
  8. # @Version 1.6.1
  9. #
  10. # The modul reads and writes parameters via RS485 from and to a Vallox
  11. # ventilation bus.
  12. #
  13. # This module was made possible by Heinz from mysensors Community
  14. # (https://forum.mysensors.org/user/heinz). His insights to the
  15. # Vallox bus were nessecary to make this script. - Thanks!
  16. #
  17. # This script is free software; you can redistribute it and/or modify
  18. # it under the terms of the GNU General Public License as published by
  19. # the Free Software Foundation; either version 2 of the License, or
  20. # (at your option) any later version.
  21. #
  22. # The GNU General Public License can be found at
  23. # http://www.gnu.org/copyleft/gpl.html.
  24. # A copy is found in the textfile GPL.txt and important notices to the license
  25. # from the author is found in LICENSE.txt distributed with these scripts.
  26. #
  27. # This script is distributed in the hope that it will be useful,
  28. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  29. # MERCHANTABILITY || FITNESS FOR A PARTICULAR PURPOSE. See the
  30. # GNU General Public License for more details.
  31. #
  32. # This copyright notice MUST APPEAR in all copies of the script!
  33. #
  34. ################################################################
  35. ##############################################
  36. package main;
  37. use strict;
  38. use warnings;
  39. ##############################################
  40. # Global Variables
  41. ##############################################
  42. my %Vallox_datatypes;
  43. my %Vallox_datatypesReverse;
  44. ##################################
  45. # List of Datatyps of the Vallox bus
  46. ##################################
  47. my %Vallox_datatypes_base = (
  48. "06" => "FanSpeed-Relays",
  49. "07" => "MultiPurpose1",
  50. "08" => "MultiPurpose2",
  51. "29" => "FanSpeed",
  52. "2A" => "Humidity",
  53. "2B" => "CO2High",
  54. "2C" => "CO2Low",
  55. "2D" => "MachineInstalledCO2Sensors",
  56. "2E" => "CurrentVoltageIncomingOnMachine",
  57. "2F" => "HumiditySensor1",
  58. "30" => "HumiditySensor2",
  59. "32" => "TempOutside",
  60. "33" => "TempExhaust",
  61. "34" => "TempInside",
  62. "35" => "TempIncoming",
  63. "36" => "LastSystemFault",
  64. "55" => "PostHeatingOnCounter",
  65. "56" => "PostHeatingOffTime",
  66. "57" => "PostHeatingTargetValue",
  67. "6C" => "Flags1",
  68. "6D" => "Flags2",
  69. "6E" => "Flags3",
  70. "6F" => "Flags4",
  71. "70" => "Flags5",
  72. "71" => "Flags6",
  73. "79" => "FireplaceBoosterCountdownMinutes",
  74. "8F" => "ResumeBus",
  75. "91" => "SuspendBusForCO2Communication",
  76. "A3" => "Select",
  77. "A4" => "HeatingSetPoint",
  78. "A5" => "FanSpeedMax",
  79. "A6" => "ServiceReminderMonths",
  80. "A7" => "PreheatingSetPoint",
  81. "A8" => "InputFanStopTemperatureThreshold",
  82. "A9" => "FanSpeedMin",
  83. "AA" => "Program",
  84. "AB" => "MaintencanceCountdownMonths",
  85. "AE" => "BasicHumidityLevel",
  86. "AF" => "HeatRecoveryCellBypassSetpointTemperature",
  87. "B0" => "DCFanInputAdjustment",
  88. "B1" => "DCFanOutputAdjustment",
  89. "B2" => "CellDefrostingSetpointTemperature",
  90. "B3" => "CO2SetPointUpper",
  91. "B4" => "CO2SetPointLower",
  92. "B5" => "Program2",
  93. "C0" => "Initial1",
  94. "C6" => "Initial2",
  95. "C7" => "Initial3",
  96. "C8" => "Initial4",
  97. "C9" => "Initial5",
  98. );
  99. my %Vallox_datatypesReverse_base = reverse %Vallox_datatypes_base;
  100. my %Vallox_datatypes_legacy1 = (
  101. "49" => "Legacy49", # Unknown legacy Reading
  102. "4A" => "Legacy4A", # Unknown legacy Reading
  103. "4C" => "Legacy4C", # Unknown legacy Reading
  104. "53" => "Legacy53", # Unknown legacy Reading
  105. "54" => "Legacy54", # Unknown legacy Reading
  106. "58" => "TempOutside",
  107. "5A" => "TempInside",
  108. "5B" => "TempIncoming",
  109. "5C" => "TempExhaust",
  110. );
  111. my %Vallox_datatypesReverse_legacy1 = reverse %Vallox_datatypes_legacy1;
  112. ##################################
  113. # Mapping of the fan speeds
  114. ##################################
  115. my %Vallox_levelTable = (
  116. "01" => "1",
  117. "03" => "2",
  118. "07" => "3",
  119. "0F" => "4",
  120. "1F" => "5",
  121. "3F" => "6",
  122. "7F" => "7",
  123. "FF" => "8",
  124. );
  125. my %Vallox_levelTableReverse = reverse %Vallox_levelTable;
  126. ##################################
  127. # Mapping of the temperatures
  128. ##################################
  129. my %Vallox_temperatureTable = (
  130. "00" => "-74",
  131. "01" => "-70",
  132. "02" => "-66",
  133. "03" => "-62",
  134. "04" => "-59",
  135. "05" => "-56",
  136. "06" => "-54",
  137. "07" => "-52",
  138. "08" => "-50",
  139. "09" => "-48",
  140. "0A" => "-47",
  141. "0B" => "-46",
  142. "0C" => "-44",
  143. "0D" => "-43",
  144. "0E" => "-42",
  145. "0F" => "-41",
  146. "10" => "-40",
  147. "11" => "-39",
  148. "12" => "-38",
  149. "13" => "-37",
  150. "14" => "-36",
  151. "15" => "-35",
  152. "16" => "-34",
  153. "17" => "-33",
  154. "18" => "-33",
  155. "19" => "-32",
  156. "1A" => "-31",
  157. "1B" => "-30",
  158. "1C" => "-30",
  159. "1D" => "-29",
  160. "1E" => "-28",
  161. "1F" => "-28",
  162. "20" => "-27",
  163. "21" => "-27",
  164. "22" => "-26",
  165. "23" => "-25",
  166. "24" => "-25",
  167. "25" => "-24",
  168. "26" => "-24",
  169. "27" => "-23",
  170. "28" => "-23",
  171. "29" => "-22",
  172. "2A" => "-22",
  173. "2B" => "-21",
  174. "2C" => "-21",
  175. "2D" => "-20",
  176. "2E" => "-20",
  177. "2F" => "-19",
  178. "30" => "-19",
  179. "31" => "-19",
  180. "32" => "-18",
  181. "33" => "-18",
  182. "34" => "-17",
  183. "35" => "-17",
  184. "36" => "-16",
  185. "37" => "-16",
  186. "38" => "-16",
  187. "39" => "-15",
  188. "3A" => "-15",
  189. "3B" => "-14",
  190. "3C" => "-14",
  191. "3D" => "-14",
  192. "3E" => "-13",
  193. "3F" => "-13",
  194. "40" => "-12",
  195. "41" => "-12",
  196. "42" => "-12",
  197. "43" => "-11",
  198. "44" => "-11",
  199. "45" => "-11",
  200. "46" => "-10",
  201. "47" => "-10",
  202. "48" => "-9",
  203. "49" => "-9",
  204. "4A" => "-9",
  205. "4B" => "-8",
  206. "4C" => "-8",
  207. "4D" => "-8",
  208. "4E" => "-7",
  209. "4F" => "-7",
  210. "50" => "-7",
  211. "51" => "-6",
  212. "52" => "-6",
  213. "53" => "-6",
  214. "54" => "-5",
  215. "55" => "-5",
  216. "56" => "-5",
  217. "57" => "-4",
  218. "58" => "-4",
  219. "59" => "-4",
  220. "5A" => "-3",
  221. "5B" => "-3",
  222. "5C" => "-3",
  223. "5D" => "-2",
  224. "5E" => "-2",
  225. "5F" => "-2",
  226. "60" => "-1",
  227. "61" => "-1",
  228. "62" => "-1",
  229. "63" => "-1",
  230. "64" => "0",
  231. "65" => "0",
  232. "66" => "0",
  233. "67" => "1",
  234. "68" => "1",
  235. "69" => "1",
  236. "6A" => "2",
  237. "6B" => "2",
  238. "6C" => "2",
  239. "6D" => "3",
  240. "6E" => "3",
  241. "6F" => "3",
  242. "70" => "4",
  243. "71" => "4",
  244. "72" => "4",
  245. "73" => "5",
  246. "74" => "5",
  247. "75" => "5",
  248. "76" => "5",
  249. "77" => "6",
  250. "78" => "6",
  251. "79" => "6",
  252. "7A" => "7",
  253. "7B" => "7",
  254. "7C" => "7",
  255. "7D" => "8",
  256. "7E" => "8",
  257. "7F" => "8",
  258. "80" => "9",
  259. "81" => "9",
  260. "82" => "9",
  261. "83" => "10",
  262. "84" => "10",
  263. "85" => "10",
  264. "86" => "11",
  265. "87" => "11",
  266. "88" => "11",
  267. "89" => "12",
  268. "8A" => "12",
  269. "8B" => "12",
  270. "8C" => "13",
  271. "8D" => "13",
  272. "8E" => "13",
  273. "8F" => "14",
  274. "90" => "14",
  275. "91" => "14",
  276. "92" => "15",
  277. "93" => "15",
  278. "94" => "15",
  279. "95" => "16",
  280. "96" => "16",
  281. "97" => "16",
  282. "98" => "17",
  283. "99" => "17",
  284. "9A" => "18",
  285. "9B" => "18",
  286. "9C" => "18",
  287. "9D" => "19",
  288. "9E" => "19",
  289. "9F" => "19",
  290. "A0" => "20",
  291. "A1" => "20",
  292. "A2" => "21",
  293. "A3" => "21",
  294. "A4" => "21",
  295. "A5" => "22",
  296. "A6" => "22",
  297. "A7" => "22",
  298. "A8" => "23",
  299. "A9" => "23",
  300. "AA" => "24",
  301. "AB" => "24",
  302. "AC" => "24",
  303. "AD" => "25",
  304. "AE" => "25",
  305. "AF" => "26",
  306. "B0" => "26",
  307. "B1" => "27",
  308. "B2" => "27",
  309. "B3" => "27",
  310. "B4" => "28",
  311. "B5" => "28",
  312. "B6" => "29",
  313. "B7" => "29",
  314. "B8" => "30",
  315. "B9" => "30",
  316. "BA" => "31",
  317. "BB" => "31",
  318. "BC" => "32",
  319. "BD" => "32",
  320. "BE" => "33",
  321. "BF" => "33",
  322. "C0" => "34",
  323. "C1" => "34",
  324. "C2" => "35",
  325. "C3" => "35",
  326. "C4" => "36",
  327. "C5" => "36",
  328. "C6" => "37",
  329. "C7" => "37",
  330. "C8" => "38",
  331. "C9" => "38",
  332. "CA" => "39",
  333. "CB" => "40",
  334. "CC" => "40",
  335. "CD" => "41",
  336. "CE" => "41",
  337. "CF" => "42",
  338. "D0" => "43",
  339. "D1" => "43",
  340. "D2" => "44",
  341. "D3" => "45",
  342. "D4" => "45",
  343. "D5" => "46",
  344. "D6" => "47",
  345. "D7" => "48",
  346. "D8" => "49",
  347. "D9" => "49",
  348. "DA" => "50",
  349. "DB" => "51",
  350. "DC" => "52",
  351. "DD" => "53",
  352. "DE" => "53",
  353. "DF" => "54",
  354. "E0" => "55",
  355. "E1" => "56",
  356. "E2" => "57",
  357. "E3" => "59",
  358. "E4" => "60",
  359. "E5" => "61",
  360. "E6" => "62",
  361. "E7" => "63",
  362. "E8" => "65",
  363. "E9" => "66",
  364. "EA" => "68",
  365. "EB" => "69",
  366. "EC" => "71",
  367. "ED" => "73",
  368. "EE" => "75",
  369. "EF" => "77",
  370. "F0" => "79",
  371. "F1" => "81",
  372. "F2" => "82",
  373. "F3" => "86",
  374. "F4" => "90",
  375. "F5" => "93",
  376. "F6" => "97",
  377. "F7" => "100",
  378. "F8" => "100",
  379. "F9" => "100",
  380. "FA" => "100",
  381. "FB" => "100",
  382. "FC" => "100",
  383. "FD" => "100",
  384. "FE" => "100",
  385. "FF" => "100"
  386. );
  387. my %Vallox_temperatureTableReverse = reverse %Vallox_temperatureTable;
  388. ##################################
  389. # Mapping of the percentages (eg humidity)
  390. ##################################
  391. my %Vallox_percentageTable = (
  392. "34" => "0",
  393. "36" => "1",
  394. "38" => "2",
  395. "3A" => "3",
  396. "3C" => "4",
  397. "3E" => "5",
  398. "40" => "6",
  399. "42" => "7",
  400. "44" => "8",
  401. "46" => "9",
  402. "48" => "10",
  403. "4A" => "11",
  404. "4C" => "12",
  405. "4E" => "13",
  406. "50" => "14",
  407. "52" => "15",
  408. "54" => "16",
  409. "56" => "17",
  410. "58" => "18",
  411. "5A" => "19",
  412. "5C" => "20",
  413. "5E" => "21",
  414. "60" => "22",
  415. "62" => "23",
  416. "64" => "24",
  417. "66" => "25",
  418. "68" => "26",
  419. "6A" => "27",
  420. "6C" => "28",
  421. "6E" => "29",
  422. "70" => "30",
  423. "72" => "31",
  424. "74" => "32",
  425. "76" => "33",
  426. "78" => "34",
  427. "7A" => "35",
  428. "7C" => "36",
  429. "7E" => "37",
  430. "80" => "38",
  431. "82" => "39",
  432. "84" => "40",
  433. "86" => "41",
  434. "88" => "42",
  435. "8A" => "43",
  436. "8C" => "44",
  437. "8E" => "45",
  438. "90" => "46",
  439. "92" => "47",
  440. "94" => "48",
  441. "96" => "49",
  442. "98" => "50",
  443. "9A" => "51",
  444. "9C" => "52",
  445. "9E" => "53",
  446. "A0" => "54",
  447. "A2" => "55",
  448. "A4" => "56",
  449. "A6" => "57",
  450. "A8" => "58",
  451. "AA" => "59",
  452. "AC" => "60",
  453. "AE" => "61",
  454. "B0" => "62",
  455. "B2" => "63",
  456. "B4" => "64",
  457. "B6" => "65",
  458. "B8" => "66",
  459. "BA" => "67",
  460. "BC" => "68",
  461. "BE" => "69",
  462. "C0" => "70",
  463. "C2" => "71",
  464. "C4" => "72",
  465. "C6" => "73",
  466. "C8" => "74",
  467. "CA" => "75",
  468. "CC" => "76",
  469. "CE" => "77",
  470. "D0" => "78",
  471. "D2" => "79",
  472. "D4" => "80",
  473. "D6" => "81",
  474. "D8" => "82",
  475. "DA" => "83",
  476. "DC" => "84",
  477. "DE" => "85",
  478. "E0" => "86",
  479. "E2" => "87",
  480. "E4" => "88",
  481. "E6" => "89",
  482. "E8" => "90",
  483. "EA" => "91",
  484. "EC" => "92",
  485. "EE" => "93",
  486. "F0" => "94",
  487. "F2" => "95",
  488. "F4" => "96",
  489. "F6" => "97",
  490. "F8" => "98",
  491. "FA" => "99",
  492. "FC" => "100"
  493. );
  494. my %Vallox_percentageTableReverse = reverse %Vallox_percentageTable;
  495. ##################################
  496. # Mapping of the faults
  497. ##################################
  498. my %Vallox_faultTable = (
  499. "00" => "No fault stored",
  500. "05" => "Supply air temperature sensor fault",
  501. "06" => "Carbon dioxide alarm",
  502. "07" => "Outdoor air sensor fault",
  503. "08" => "Extract air sensor fault",
  504. "09" => "Water radiator danger of freezing",
  505. "0A" => "Exhaust air sensor fault",
  506. );
  507. my %Vallox_faultTableReverse = reverse %Vallox_faultTable;
  508. ##################################
  509. # Mapping of the MultiReadings with R/W
  510. # TODO: Find s.th. more elegant for all MR
  511. ##################################
  512. my %Vallox_multiReadingTable_realcmd = (
  513. "SupplyFan" => "MultiPurpose2",
  514. "ExhaustFan" => "MultiPurpose2",
  515. "CO2HigherSpeedRequest" => "Flags2",
  516. "CO2LowerRatePublicInvitation" => "Flags2",
  517. "HumidityLowerRatePublicInvitation" => "Flags2",
  518. "SwitchLowerSpeedRequest" => "Flags2",
  519. "CO2Alarm" => "Flags2",
  520. "FrostAlarmSensor" => "Flags2",
  521. "FrostAlarmWaterRadiator" => "Flags4",
  522. "MasterSlaveSelection" => "Flags4",
  523. "PreHeatingStatus" => "Flags5",
  524. "FireplaceSwitchActivation" => "Flags6",
  525. "PowerState" => "Select",
  526. "CO2AdjustState" => "Select",
  527. "RHAdjustState" => "Select",
  528. "HeatingState" => "Select",
  529. "ServiceReminderIndicator" => "Select",
  530. "AutomaticHumidityBasicLevelSeekerState" => "Program",
  531. "BoostSwitchMode" => "Program",
  532. "RadiatorType" => "Program",
  533. "CascadeAdjust" => "Program",
  534. "MaxSpeedLimitFunction" => "Program2",
  535. );
  536. my %Vallox_multiReadingTable_digit = (
  537. "SupplyFan" => 3,
  538. "ExhaustFan" => 5,
  539. "CO2HigherSpeedRequest" => 0,
  540. "CO2LowerRatePublicInvitation" => 1,
  541. "HumidityLowerRatePublicInvitation" => 2,
  542. "SwitchLowerSpeedRequest" => 3,
  543. "CO2Alarm" => 6,
  544. "FrostAlarmSensor" => 7,
  545. "FrostAlarmWaterRadiator" => 4,
  546. "MasterSlaveSelection" => 7,
  547. "PreHeatingStatus" => 7,
  548. "FireplaceSwitchActivation" => 5,
  549. "PowerState" => 0,
  550. "CO2AdjustState" => 1,
  551. "RHAdjustState" => 2,
  552. "HeatingState" => 3,
  553. "ServiceReminderIndicator" => 7,
  554. "AutomaticHumidityBasicLevelSeekerState" => 4,
  555. "BoostSwitchMode" => 5,
  556. "RadiatorType" => 6,
  557. "CascadeAdjust" => 7,
  558. "MaxSpeedLimitFunction" => 0,
  559. );
  560. ##################################
  561. # Initialize Buffer fillings
  562. ##################################
  563. my $bufferRead = "00";
  564. my $bufferDevIO = "00";
  565. my $bufferDebug = "--";
  566. ##################################
  567. # basic get commands
  568. ##################################
  569. my %Vallox_gets = ( "raw" => "raw" );
  570. ##################################
  571. # basic set commands
  572. ##################################
  573. my %Vallox_sets = ( "raw" => "raw" );
  574. ##############################################
  575. # Custom Functions
  576. ##############################################
  577. ##################################
  578. # Create a valid message to send
  579. ##################################
  580. sub Vallox_CreateMsg ($@) {
  581. my ( $hash, $readingIdentifier ) = @_;
  582. my $domain = hex "0x"
  583. . AttrVal( $hash->{NAME}, "ValloxIDDomain", "01" )
  584. ; # Domain (1 by default)
  585. my $sender = hex "0x"
  586. . AttrVal( $hash->{NAME}, "ValloxIDFHEM", "2F" ); # ID of this FHEM
  587. my $receiver = hex "0x"
  588. . AttrVal( $hash->{NAME}, "ValloxIDCentral", "11" ); # ID of the central
  589. my $datatype = hex "0x" . $readingIdentifier;
  590. my $checksum = ( $domain + $sender + $receiver + $datatype ) % 0x100;
  591. my $msg =
  592. lc(
  593. sprintf( "%02x", $domain )
  594. . sprintf( "%02x", $sender )
  595. . sprintf( "%02x", $receiver ) . "00"
  596. . sprintf( "%02x", $datatype )
  597. . sprintf( "%02x", $checksum ) );
  598. return $msg;
  599. }
  600. ##############################################
  601. # Check if a message is valid
  602. ##############################################
  603. sub Vallox_ValidateStream ($@) {
  604. my ( $hash, @a ) = @_;
  605. my $name = shift @a;
  606. return undef if ( length($bufferRead) < 12 );
  607. my $domain = hex "0x" . substr( $bufferRead, 0, 2 );
  608. my $sender = hex "0x" . substr( $bufferRead, 2, 2 );
  609. my $receiver = hex "0x" . substr( $bufferRead, 4, 2 );
  610. my $data_1 = hex "0x" . substr( $bufferRead, 6, 2 );
  611. my $data_2 = hex "0x" . substr( $bufferRead, 8, 2 );
  612. my $checksum =
  613. ( $domain + $sender + $receiver + $data_1 + $data_2 ) % 0x100;
  614. #++$hash->{"CheckCount"};
  615. if (
  616. lc($domain) eq 01
  617. && lc(
  618. sprintf( "%02x", $domain )
  619. . sprintf( "%02x", $sender )
  620. . sprintf( "%02x", $receiver )
  621. . sprintf( "%02x", $data_1 )
  622. . sprintf( "%02x", $data_2 )
  623. . sprintf( "%02x", $checksum )
  624. ) eq lc($bufferRead)
  625. )
  626. {
  627. #Log3 ($name, 5, "Vallox: Debug: DO ".$domain." - SE ".$sender." - RE ".$receiver." - D1 ".$data_1." - D2 ".$data_2." - CS ".$checksum." NE ".$bufferRead);
  628. #++$hash->{"MessageCount"};
  629. #$hash->{"BufferDatagramRatio"} = "1 : ".$hash->{"CheckCount"} / $hash->{"MessageCount"};
  630. if (
  631. (
  632. ( $sender >= 17 && $sender <= 31 ) || ( $sender >= 33
  633. && $sender <= 47 )
  634. )
  635. && (
  636. ( $receiver >= 16 && $receiver <= 31 )
  637. || ( $receiver >= 32
  638. && $receiver <= 47 )
  639. )
  640. )
  641. {
  642. return 1;
  643. }
  644. else {
  645. ++$hash->{"ErrorCount"};
  646. return 2;
  647. }
  648. }
  649. else {
  650. Log3( $name, 4,
  651. "Vallox: Debug: DO "
  652. . $domain
  653. . " - SE "
  654. . $sender
  655. . " - RE "
  656. . $receiver
  657. . " - D1 "
  658. . $data_1
  659. . " - D2 "
  660. . $data_2
  661. . " - CS "
  662. . $checksum . " NE "
  663. . $bufferRead );
  664. return 0;
  665. }
  666. }
  667. ##############################################
  668. # Change bit in MultiReading
  669. # (bitnumber = 0 (rightest) - 7 (leftest)!)
  670. ##############################################
  671. sub Vallox_ReplaceBit ($@) {
  672. my ( $hash, $bitstring, $bitnumber, $value ) = @_;
  673. return
  674. substr( $bitstring, 0, 8 - $bitnumber - 1 )
  675. . $value
  676. . substr( $bitstring, 8 - $bitnumber, $bitnumber );
  677. }
  678. ##############################################
  679. # Update reading bulk for binary reading
  680. ##############################################
  681. sub Vallox_ReadingsBulkUpdateMultiReading($@) {
  682. my ( $hash, $rawReadingType, $readingname, $bitnumber, ) = @_;
  683. readingsBulkUpdate(
  684. $hash,
  685. $readingname,
  686. substr(
  687. $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} },
  688. 8 - $bitnumber - 1, 1
  689. )
  690. )
  691. ; # if (ReadingsVal($hash->{NAME},$readingname,"unknown") ne substr($hash->{"MR_".$Vallox_datatypes{$rawReadingType}},8-$bitnumber-1,1));
  692. return;
  693. }
  694. ##############################################
  695. # Interpret datagram and handle it
  696. ##############################################
  697. sub Vallox_InterpretAndUpdate(@) {
  698. my ( $hash, $datagram ) = @_;
  699. my $name = $hash->{NAME};
  700. my $rawReadingType;
  701. my $rawReadingValue;
  702. my $rawReadingChecksum;
  703. my $singlereading = 1;
  704. my $fineReadingValue = 1;
  705. $datagram = uc($datagram);
  706. # get the type of the datagram
  707. $rawReadingType = substr( $datagram, 6, 2 );
  708. # get the value of the datagram
  709. $rawReadingValue = substr( $datagram, 8, 2 );
  710. # get the value of the datagram
  711. $rawReadingChecksum = substr( $datagram, 10, 2 );
  712. if ( $rawReadingType ne "00" ) {
  713. # Decoding for the final readings.
  714. # - rawReading... is the original information from the datagram
  715. # - fineReading... is the human readable information
  716. # Starting with the "One information in one datagram"-Section
  717. # Convert FanSpeeds by the Vallox_levelTable
  718. if ( $rawReadingType eq "29"
  719. || $rawReadingType eq "A5"
  720. || $rawReadingType eq "A9" )
  721. {
  722. $fineReadingValue = $Vallox_levelTable{$rawReadingValue};
  723. Log3( $name, 4,
  724. "Vallox: Incoming Status-Info (FanSpeed): $datagram (Level $fineReadingValue)"
  725. );
  726. # Convert Temperatures by the Vallox_temperatureTable
  727. }
  728. elsif ($rawReadingType eq "32"
  729. || $rawReadingType eq "33"
  730. || $rawReadingType eq "34"
  731. || $rawReadingType eq "35"
  732. || $rawReadingType eq "A4"
  733. || $rawReadingType eq "A7"
  734. || $rawReadingType eq "A8"
  735. || $rawReadingType eq "AF" )
  736. {
  737. if ( $rawReadingType eq "A4" && $hash->{BusVersion} eq "1" ) {
  738. $fineReadingValue = $Vallox_levelTable{$rawReadingValue};
  739. Log3( $name, 4,
  740. "Vallox: Incoming Status-Info (HeatingSetPoint): $datagram (Level $fineReadingValue)"
  741. );
  742. return;
  743. }
  744. else {
  745. $fineReadingValue = $Vallox_temperatureTable{$rawReadingValue};
  746. }
  747. if (
  748. (
  749. $rawReadingType eq "32"
  750. || $rawReadingType eq "33"
  751. || $rawReadingType eq "34"
  752. || $rawReadingType eq "35"
  753. )
  754. && $fineReadingValue < -40
  755. )
  756. {
  757. Log3( $name, 4,
  758. "Vallox: Incoming Status-Info (Temperature) invalid: $datagram ($fineReadingValue deg.)"
  759. );
  760. return;
  761. }
  762. Log3( $name, 4,
  763. "Vallox: Incoming Status-Info (Temperature): $datagram ($fineReadingValue deg.)"
  764. );
  765. # Convert Percentages by the Vallox_percentageTable
  766. }
  767. elsif ( $rawReadingType eq "AE" ) {
  768. $fineReadingValue = $Vallox_percentageTable{$rawReadingValue};
  769. Log3( $name, 4,
  770. "Vallox: Incoming Status-Info (Percentage): $datagram ($fineReadingValue pct.)"
  771. );
  772. # Convert Faults by the Vallox_faultTable
  773. }
  774. elsif ( $rawReadingType eq "36" ) {
  775. $fineReadingValue = $Vallox_faultTable{$rawReadingValue};
  776. Log3( $name, 4,
  777. "Vallox: Incoming Status-Info (Fault): $datagram ($fineReadingValue)"
  778. );
  779. # Convert Decimal Values
  780. }
  781. elsif ($rawReadingType eq "2B"
  782. || $rawReadingType eq "2C"
  783. || $rawReadingType eq "2E"
  784. || $rawReadingType eq "57"
  785. || $rawReadingType eq "A6"
  786. || $rawReadingType eq "79"
  787. || $rawReadingType eq "8F"
  788. || $rawReadingType eq "91"
  789. || $rawReadingType eq "AB"
  790. || $rawReadingType eq "B0"
  791. || $rawReadingType eq "B1"
  792. )
  793. {
  794. $fineReadingValue = sprintf( "%d", hex "0x" . $rawReadingValue );
  795. Log3( $name, 4,
  796. "Vallox: Incoming Status-Info (Decimal): $datagram ($fineReadingValue)"
  797. );
  798. # Convert PostHeating Time Values
  799. }
  800. elsif ( $rawReadingType eq "55" || $rawReadingType eq "56" ) {
  801. $fineReadingValue =
  802. sprintf( "%d", hex "0x" . $rawReadingValue ) / 2.5;
  803. Log3( $name, 4,
  804. "Vallox: Incoming Status-Info (PostHeatingCounter): $datagram ($fineReadingValue)"
  805. );
  806. # Convert CellDefrostingSetpointTemperature Values
  807. }
  808. elsif ( $rawReadingType eq "B2" ) {
  809. $fineReadingValue =
  810. sprintf( "%d", hex "0x" . $rawReadingValue ) / 3;
  811. Log3( $name, 4,
  812. "Vallox: Incoming Status-Info (CellDefrostingSetpointTemperature): $datagram ($fineReadingValue)"
  813. );
  814. # Convert measured humidity by formula (if it is negative then you don't have a humidity sensor)
  815. }
  816. elsif ($rawReadingType eq "2A"
  817. || $rawReadingType eq "2F"
  818. || $rawReadingType eq "30" )
  819. {
  820. $fineReadingValue =
  821. ( sprintf( "%d", hex "0x" . $rawReadingValue ) - 51 ) / 2.04;
  822. # Negative Humidity impossible: No Sensor attatched
  823. if ( $fineReadingValue < 0 ) {
  824. Log3( $name, 4,
  825. "Vallox: Incoming Status-Info (Humidity) invalid: $datagram ($fineReadingValue Perc. rH)"
  826. );
  827. return;
  828. }
  829. Log3( $name, 4,
  830. "Vallox: Incoming Status-Info (Humidity): $datagram ($fineReadingValue pct. rH)"
  831. );
  832. # Starting with the "Up to eight informations in one datagram"-Section
  833. # Disabled lines are unused.
  834. # FanSpeed-Relays
  835. }
  836. elsif ( $rawReadingType eq "06" ) {
  837. $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} } =
  838. sprintf( '%08b', hex( "0x" . $rawReadingValue ) );
  839. $singlereading = 0;
  840. readingsBeginUpdate($hash);
  841. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  842. "Speed1", 0 ); #RO
  843. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  844. "Speed2", 1 ); #RO
  845. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  846. "Speed3", 2 ); #RO
  847. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  848. "Speed4", 3 ); #RO
  849. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  850. "Speed5", 4 ); #RO
  851. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  852. "Speed6", 5 ); #RO
  853. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  854. "Speed7", 6 ); #RO
  855. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  856. "Speed8", 7 ); #RO
  857. readingsEndUpdate( $hash, 1 );
  858. Log3( $name, 4,
  859. "Vallox: Incoming Status-Info (FanSpeed-Relays): $datagram (Bits "
  860. . $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} }
  861. . ")" );
  862. # MultiPurpose1
  863. }
  864. elsif ( $rawReadingType eq "07" ) {
  865. $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} } =
  866. sprintf( '%08b', hex( "0x" . $rawReadingValue ) );
  867. $singlereading = 0;
  868. readingsBeginUpdate($hash);
  869. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  870. "PostHeating", 5 ); # RO
  871. readingsEndUpdate( $hash, 1 );
  872. Log3( $name, 4,
  873. "Vallox: Incoming Status-Info (MultiPurpose1): $datagram (Bits "
  874. . $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} }
  875. . ")" );
  876. #MultiPurpose2
  877. }
  878. elsif ( $rawReadingType eq "08" ) {
  879. $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} } =
  880. sprintf( '%08b', hex( "0x" . $rawReadingValue ) );
  881. $singlereading = 0;
  882. readingsBeginUpdate($hash);
  883. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  884. "DamperMotorPosition", 1 ); #RO
  885. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  886. "FaultSignalRelay", 2 ); #RO
  887. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  888. "SupplyFan", 3 );
  889. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  890. "PreHeating", 4 ); #RO
  891. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  892. "ExhaustFan", 5 );
  893. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  894. "FireplaceBooster", 6 ); #RO
  895. readingsEndUpdate( $hash, 1 );
  896. Log3( $name, 4,
  897. "Vallox: Incoming Status-Info (MultiPurpose2): $datagram (Bits "
  898. . $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} }
  899. . ")" );
  900. #MachineInstalledCO2Sensor
  901. }
  902. elsif ( $rawReadingType eq "2D" ) {
  903. $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} } =
  904. sprintf( '%08b', hex( "0x" . $rawReadingValue ) );
  905. $singlereading = 0;
  906. readingsBeginUpdate($hash);
  907. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  908. "CO2Sensor1", 1 ); #RO
  909. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  910. "CO2Sensor2", 2 ); #RO
  911. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  912. "CO2Sensor3", 3 ); #RO
  913. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  914. "CO2Sensor4", 4 ); #RO
  915. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  916. "CO2Sensor5", 5 ); #RO
  917. readingsEndUpdate( $hash, 1 );
  918. Log3( $name, 4,
  919. "Vallox: Incoming Status-Info (MultiPurpose2): $datagram (Bits "
  920. . $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} }
  921. . ")" );
  922. #Flags2
  923. }
  924. elsif ( $rawReadingType eq "6D" ) {
  925. $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} } =
  926. sprintf( '%08b', hex( "0x" . $rawReadingValue ) );
  927. $singlereading = 0;
  928. readingsBeginUpdate($hash);
  929. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  930. "CO2HigherSpeedRequest", 0 );
  931. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  932. "CO2LowerRatePublicInvitation", 1 );
  933. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  934. "HumidityLowerRatePublicInvitation", 2 );
  935. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  936. "SwitchLowerSpeedRequest", 3 );
  937. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  938. "CO2Alarm", 6 );
  939. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  940. "FrostAlarmSensor", 7 );
  941. readingsEndUpdate( $hash, 1 );
  942. Log3( $name, 4,
  943. "Vallox: Incoming Status-Info (Flags2): $datagram (Bits "
  944. . $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} }
  945. . ")" );
  946. #Flags4
  947. }
  948. elsif ( $rawReadingType eq "6F" ) {
  949. $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} } =
  950. sprintf( '%08b', hex( "0x" . $rawReadingValue ) );
  951. $singlereading = 0;
  952. readingsBeginUpdate($hash);
  953. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  954. "FrostAlarmWaterRadiator", 4 );
  955. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  956. "MasterSlaveSelection", 7 );
  957. readingsEndUpdate( $hash, 1 );
  958. Log3( $name, 4,
  959. "Vallox: Incoming Status-Info (Flags4): $datagram (Bits "
  960. . $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} }
  961. . ")" );
  962. #Flags5
  963. }
  964. elsif ( $rawReadingType eq "70" ) {
  965. $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} } =
  966. sprintf( '%08b', hex( "0x" . $rawReadingValue ) );
  967. $singlereading = 0;
  968. readingsBeginUpdate($hash);
  969. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  970. "PreHeatingStatus", 7 );
  971. readingsEndUpdate( $hash, 1 );
  972. Log3( $name, 4,
  973. "Vallox: Incoming Status-Info (Flags5): $datagram (Bits "
  974. . $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} }
  975. . ")" );
  976. #Flags6
  977. }
  978. elsif ( $rawReadingType eq "71" ) {
  979. $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} } =
  980. sprintf( '%08b', hex( "0x" . $rawReadingValue ) );
  981. $singlereading = 0;
  982. readingsBeginUpdate($hash);
  983. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  984. "RemoteMonitoringControl", 4 ); #RO
  985. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  986. "FireplaceSwitchActivation", 5 );
  987. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  988. "FireplaceBoosterStatus", 6 ); #RO
  989. readingsEndUpdate( $hash, 1 );
  990. Log3( $name, 4,
  991. "Vallox: Incoming Status-Info (Flags6): $datagram (Bits "
  992. . $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} }
  993. . ")" );
  994. # Select
  995. }
  996. elsif ( $rawReadingType eq "A3" ) {
  997. $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} } =
  998. sprintf( '%08b', hex( "0x" . $rawReadingValue ) );
  999. $singlereading = 0;
  1000. readingsBeginUpdate($hash);
  1001. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  1002. "PowerState", 0 );
  1003. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  1004. "CO2AdjustState", 1 );
  1005. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  1006. "RHAdjustState", 2 );
  1007. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  1008. "HeatingState", 3 );
  1009. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  1010. "FilterGuardIndicator", 4 ); #RO
  1011. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  1012. "HeatingIndicator", 5 ); #RO
  1013. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  1014. "FaultIndicator", 6 ); #RO
  1015. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  1016. "ServiceReminderIndicator", 7 );
  1017. readingsEndUpdate( $hash, 1 );
  1018. Log3( $name, 4,
  1019. "Vallox: Incoming Status-Info (Select): $datagram (Bits "
  1020. . $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} }
  1021. . ")" );
  1022. # Program
  1023. }
  1024. elsif ( $rawReadingType eq "AA" ) {
  1025. $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} } =
  1026. sprintf( '%08b', hex( "0x" . $rawReadingValue ) );
  1027. $singlereading = 0;
  1028. readingsBeginUpdate($hash);
  1029. # ----xxxx - Nibble is one value // # TODO: Adopt Function
  1030. readingsBulkUpdate(
  1031. $hash,
  1032. "HumidityCO2AdjustmentInterval",
  1033. oct(
  1034. "0b" . "0000"
  1035. . substr(
  1036. $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} },
  1037. 4, 4
  1038. )
  1039. )
  1040. )
  1041. if (
  1042. ReadingsVal(
  1043. $name, "HumidityCO2AdjustmentInterval", "unknown"
  1044. ) ne oct(
  1045. "0b" . "0000"
  1046. . substr(
  1047. $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} },
  1048. 4, 4
  1049. )
  1050. )
  1051. );
  1052. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  1053. "AutomaticHumidityBasicLevelSeekerState", 4 );
  1054. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  1055. "BoostSwitchMode", 5 );
  1056. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  1057. "RadiatorType", 6 );
  1058. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  1059. "CascadeAdjust", 7 );
  1060. readingsEndUpdate( $hash, 1 );
  1061. Log3( $name, 4,
  1062. "Vallox: Incoming Status-Info (Program): $datagram (Bits "
  1063. . $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} }
  1064. . ")" );
  1065. # Program2
  1066. }
  1067. elsif ( $rawReadingType eq "B5" ) {
  1068. $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} } =
  1069. sprintf( '%08b', hex( "0x" . $rawReadingValue ) );
  1070. $singlereading = 0;
  1071. readingsBeginUpdate($hash);
  1072. Vallox_ReadingsBulkUpdateMultiReading( $hash, $rawReadingType,
  1073. "MaxSpeedLimitFunction", 0 );
  1074. readingsEndUpdate( $hash, 1 );
  1075. Log3( $name, 4,
  1076. "Vallox: Incoming Status-Info (Program2): $datagram (Bits "
  1077. . $hash->{ "MR_" . $Vallox_datatypes{$rawReadingType} }
  1078. . ")" );
  1079. # Convert Unused Binarys to bits (Just not to have Hex Readings)
  1080. }
  1081. elsif ( $rawReadingType eq "6C" || $rawReadingType eq "6E" ) {
  1082. $fineReadingValue =
  1083. sprintf( '%08b', hex( "0x" . $rawReadingValue ) );
  1084. Log3( $name, 4,
  1085. "Vallox: Incoming Status-Info (Unused Binary): $datagram (Bits $fineReadingValue)"
  1086. );
  1087. # Everything else
  1088. # All Readings shall be handled before
  1089. }
  1090. else {
  1091. $fineReadingValue = $rawReadingValue;
  1092. $singlereading = 1;
  1093. if ( $rawReadingType eq "B3" ) {
  1094. Log3( $name, 4, "Vallox: Incoming CO2SetPoint-Part (Upper): $datagram");
  1095. } elsif ( $rawReadingType eq "B4" ) {
  1096. Log3( $name, 4, "Vallox: Incoming CO2SetPoint-Part (Lower): $datagram");
  1097. } else {
  1098. Log3( $name, 2, "Vallox: Incoming unhandled datagram: $datagram" );
  1099. }
  1100. }
  1101. if ( $Vallox_datatypes{$rawReadingType} ) {
  1102. if ( $singlereading == 1 ) {
  1103. Log3( $name, 5, "Vallox: Update Reading: $fineReadingValue" );
  1104. readingsSingleUpdate( $hash, $Vallox_datatypes{$rawReadingType},
  1105. $fineReadingValue, 1 );
  1106. # If this is a CO2SetPointLower && CO2SetPointUpper has been set, write CO2SetPoint
  1107. if ($rawReadingType eq "B4" && ReadingsVal( $name, "CO2SetPointUpper", "unknown" ) ne "unknown" ) {
  1108. my $CO2SetPoint = ReadingsVal( $name, "CO2SetPointUpper", "unknown" ) . $fineReadingValue;
  1109. readingsSingleUpdate( $hash, "CO2SetPoint", hex $CO2SetPoint , 1 );
  1110. }
  1111. # Efficiency Calculation
  1112. # Is this Reading a Temp?
  1113. if (
  1114. substr( $Vallox_datatypes{$rawReadingType}, 0, 4 ) eq
  1115. "Temp" )
  1116. {
  1117. # If HRC is in Bypass - Efficiency is 0
  1118. if ( ReadingsVal( $name, "DamperMotorPosition", 1 ) == 1 ) {
  1119. readingsSingleUpdate( $hash, "EfficiencyIn", 0, 1 );
  1120. readingsSingleUpdate( $hash, "EfficiencyOut", 0, 1 );
  1121. readingsSingleUpdate( $hash, "EfficiencyAverage", 0,
  1122. 1 );
  1123. Log3( $name, 5,
  1124. "Vallox: Efficiency Override: HRC Bypass" );
  1125. }
  1126. else {
  1127. my (
  1128. $EfficiencyIn, $EfficiencyOut, $TempIncoming,
  1129. $TempOutside, $TempInside, $TempExhaust
  1130. ) = 0;
  1131. # Efficiency on Keep Temp Inside
  1132. # Do we have all nessecary Readings?
  1133. if ( ReadingsVal( $name, "TempIncoming", "unknown" ) ne
  1134. "unknown"
  1135. && ReadingsVal( $name, "TempOutside", "unknown" ) ne
  1136. "unknown"
  1137. && ReadingsVal( $name, "TempInside", "unknown" ) ne
  1138. "unknown" )
  1139. {
  1140. $TempIncoming =
  1141. ReadingsVal( $name, "TempIncoming", -100 );
  1142. $TempOutside =
  1143. ReadingsVal( $name, "TempOutside", -100 );
  1144. $TempInside =
  1145. ReadingsVal( $name, "TempInside", -100 );
  1146. # Prevent DIV/0 (if Inside=Outside the HRC does nothing = 100% Efficient)
  1147. if ( $TempInside - $TempOutside != 0 ) {
  1148. $EfficiencyIn =
  1149. ( $TempIncoming - $TempOutside ) /
  1150. ( $TempInside - $TempOutside ) * 100;
  1151. $EfficiencyIn = 100 if ( $EfficiencyIn > 100 );
  1152. Log3( $name, 5,
  1153. "Vallox: Efficiency Inside: ("
  1154. . $TempIncoming . "-"
  1155. . $TempIncoming . ")/("
  1156. . $TempInside . "-"
  1157. . $TempOutside
  1158. . ")*100 = "
  1159. . $EfficiencyIn );
  1160. readingsSingleUpdate( $hash, "EfficiencyIn",
  1161. $EfficiencyIn, 1 );
  1162. }
  1163. else {
  1164. Log3( $name, 5,
  1165. "Vallox: Efficiency Inside (DIV/0 Prevention): ("
  1166. . $TempIncoming . "-"
  1167. . $TempIncoming . ")/("
  1168. . $TempInside . "-"
  1169. . $TempOutside
  1170. . ")*100 = 100" );
  1171. readingsSingleUpdate( $hash, "EfficiencyIn",
  1172. 100, 1 );
  1173. }
  1174. }
  1175. # Efficiency on Keep Temp Outside
  1176. # Do we have all nessecary Readings?
  1177. if ( ReadingsVal( $name, "TempOutside", "unknown" ) ne
  1178. "unknown"
  1179. && ReadingsVal( $name, "TempIncoming", "unknown" )
  1180. ne "unknown"
  1181. && ReadingsVal( $name, "TempExhaust", "unknown" ) ne
  1182. "unknown" )
  1183. {
  1184. $TempOutside =
  1185. ReadingsVal( $name, "TempOutside", -100 );
  1186. $TempIncoming =
  1187. ReadingsVal( $name, "TempIncoming", -100 );
  1188. $TempExhaust =
  1189. ReadingsVal( $name, "TempExhaust", -100 );
  1190. # Prevent DIV/0 (if Inside=Outside the HRC does nothing = 100% Efficient)
  1191. if ( $TempOutside - $TempIncoming != 0 ) {
  1192. $EfficiencyOut =
  1193. ( $TempExhaust - $TempIncoming ) /
  1194. ( $TempOutside - $TempIncoming ) * 100;
  1195. $EfficiencyOut = 100
  1196. if ( $EfficiencyOut > 100 );
  1197. Log3( $name, 5,
  1198. "Vallox: Efficiency Outside: ("
  1199. . $TempExhaust . "-"
  1200. . $TempIncoming . ")/("
  1201. . $TempOutside . "-"
  1202. . $TempIncoming
  1203. . ")*100 = "
  1204. . $EfficiencyOut );
  1205. readingsSingleUpdate( $hash, "EfficiencyOut",
  1206. $EfficiencyOut, 1 );
  1207. }
  1208. else {
  1209. Log3( $name, 5,
  1210. "Vallox: Efficiency Outside (DIV/0 Protection): ("
  1211. . $TempExhaust . "-"
  1212. . $TempIncoming . ")/("
  1213. . $TempOutside . "-"
  1214. . $TempIncoming
  1215. . ")*100 = 100" );
  1216. readingsSingleUpdate( $hash, "EfficiencyOut",
  1217. 100, 1 );
  1218. }
  1219. }
  1220. # Average Efficiency
  1221. if ( ReadingsVal( $name, "EfficiencyIn", "unknown" ) ne
  1222. "unknown"
  1223. && ReadingsVal( $name, "EfficiencyOut", "unknown" )
  1224. ne "unknown" )
  1225. {
  1226. $EfficiencyIn =
  1227. ReadingsVal( $name, "EfficiencyIn", -100 );
  1228. $EfficiencyOut =
  1229. ReadingsVal( $name, "EfficiencyOut", -100 );
  1230. my $EfficiencyAverage =
  1231. ( $EfficiencyIn + $EfficiencyOut ) / 2;
  1232. Log3( $name, 5,
  1233. "Vallox: Efficiency Average: ("
  1234. . $EfficiencyIn . "+"
  1235. . $EfficiencyOut
  1236. . ")/2 = "
  1237. . $EfficiencyAverage );
  1238. readingsSingleUpdate( $hash, "EfficiencyAverage",
  1239. $EfficiencyAverage, 1 );
  1240. }
  1241. else {
  1242. Log3(
  1243. $name, 5,
  1244. "Vallox: Efficiency Average unknown: ("
  1245. . ReadingsVal( $name, "EfficiencyIn",
  1246. "unknown" )
  1247. . "+"
  1248. . ReadingsVal( $name, "EfficiencyOut",
  1249. "unknown" )
  1250. . ")/2"
  1251. );
  1252. }
  1253. }
  1254. }
  1255. }
  1256. }
  1257. else {
  1258. Log3( $name, 4,
  1259. "Vallox: Datagram not in Datatypes-Table: " . $datagram );
  1260. }
  1261. }
  1262. else {
  1263. Log3( $name, 5, "Vallox: Incoming Status-Request: $datagram" );
  1264. }
  1265. }
  1266. ##############################################
  1267. # Create Datagram and send it (Set)
  1268. ##############################################
  1269. sub Vallox_CreateAndSend(@) {
  1270. my ( $hash, $domain, $sender, $receiver, $datatype, $datavalue, $broadcast ) = @_;
  1271. my $name = $hash->{NAME};
  1272. my $checksum;
  1273. my $msg;
  1274. if ($broadcast == 1) {
  1275. ## Send to all Remotes
  1276. $checksum = ( $domain + $sender + 0x20 + $datatype + $datavalue ) % 0x100;
  1277. $msg =
  1278. lc(
  1279. sprintf( "%02x", $domain )
  1280. . sprintf( "%02x", $sender )
  1281. . sprintf( "%02x", 0x20 )
  1282. . sprintf( "%02x", $datatype )
  1283. . sprintf( "%02x", $datavalue )
  1284. . sprintf( "%02x", $checksum ) );
  1285. DevIo_SimpleWrite( $hash, $msg, 1 );
  1286. Log3( $name, 3, "Vallox: Command " . $msg . " has been sent." );
  1287. ## Send to all ventilation units
  1288. $checksum = ( $domain + $sender + 0x10 + $datatype + $datavalue ) % 0x100;
  1289. $msg =
  1290. lc(
  1291. sprintf( "%02x", $domain )
  1292. . sprintf( "%02x", $sender )
  1293. . sprintf( "%02x", 0x10 )
  1294. . sprintf( "%02x", $datatype )
  1295. . sprintf( "%02x", $datavalue )
  1296. . sprintf( "%02x", $checksum ) );
  1297. DevIo_SimpleWrite( $hash, $msg, 1 );
  1298. Log3( $name, 3, "Vallox: Command " . $msg . " has been sent." );
  1299. }
  1300. $checksum = ( $domain + $sender + $receiver + $datatype + $datavalue ) % 0x100;
  1301. $msg =
  1302. lc(
  1303. sprintf( "%02x", $domain )
  1304. . sprintf( "%02x", $sender )
  1305. . sprintf( "%02x", $receiver )
  1306. . sprintf( "%02x", $datatype )
  1307. . sprintf( "%02x", $datavalue )
  1308. . sprintf( "%02x", $checksum ) );
  1309. DevIo_SimpleWrite( $hash, $msg, 1 );
  1310. Log3( $name, 3, "Vallox: Command " . $msg . " has been sent." );
  1311. if ( AttrVal( $hash->{NAME}, "ValloxProcessOwnCommands", "0" ) == 1
  1312. || $hash->{BusVersion} eq "1" )
  1313. {
  1314. Vallox_InterpretAndUpdate( $hash, $msg );
  1315. Log3( $name, 3,
  1316. "Vallox: Command " . $msg . " has been internal processed." );
  1317. }
  1318. if ( AttrVal( $hash->{NAME}, "ValloxForceBroadcast", "0" ) == 1
  1319. || $hash->{BusVersion} eq "1" )
  1320. {
  1321. $checksum =
  1322. ( $domain + $sender + 0x10 + $datatype + $datavalue ) % 0x100;
  1323. $msg =
  1324. lc(
  1325. sprintf( "%02x", $domain )
  1326. . sprintf( "%02x", $sender )
  1327. . 10
  1328. . sprintf( "%02x", $datatype )
  1329. . sprintf( "%02x", $datavalue )
  1330. . sprintf( "%02x", $checksum ) );
  1331. DevIo_SimpleWrite( $hash, $msg, 1 );
  1332. Log3( $name, 3,
  1333. "Vallox: Broadcast-Command " . $msg . " has been sent." );
  1334. $checksum =
  1335. ( $domain + $sender + 0x20 + $datatype + $datavalue ) % 0x100;
  1336. $msg =
  1337. lc(
  1338. sprintf( "%02x", $domain )
  1339. . sprintf( "%02x", $sender )
  1340. . 20
  1341. . sprintf( "%02x", $datatype )
  1342. . sprintf( "%02x", $datavalue )
  1343. . sprintf( "%02x", $checksum ) );
  1344. DevIo_SimpleWrite( $hash, $msg, 1 );
  1345. Log3( $name, 3,
  1346. "Vallox: Broadcast-Command " . $msg . " has been sent." );
  1347. }
  1348. }
  1349. ##############################################
  1350. # Default Functions
  1351. ##############################################
  1352. ##################################
  1353. sub Vallox_Initialize($) {
  1354. my ($hash) = @_;
  1355. require "$attr{global}{modpath}/FHEM/DevIo.pm";
  1356. $hash->{DefFn} = "Vallox_Define";
  1357. $hash->{UndefFn} = "Vallox_Undef";
  1358. $hash->{DeleteFn} = "Vallox_Delete";
  1359. $hash->{SetFn} = "Vallox_Set";
  1360. $hash->{GetFn} = "Vallox_Get";
  1361. $hash->{AttrFn} = "Vallox_Attr";
  1362. # $hash->{NotifyFn} = "Vallox_Notify";
  1363. $hash->{ReadFn} = "Vallox_Read";
  1364. # $hash->{ReadyFn} = "Vallox_Ready";
  1365. $hash->{ShutdownFn} = "Vallox_Shutdown";
  1366. $hash->{AttrList} =
  1367. "ValloxBufferDebug:0,1 ValloxForceBroadcast:0,1 ValloxProcessOwnCommands:0,1 ValloxIDDomain ValloxIDFHEM ValloxIDCentral "
  1368. . $readingFnAttributes;
  1369. }
  1370. ##################################
  1371. sub Vallox_Define($) {
  1372. my ( $hash, $def ) = @_;
  1373. my @a = split( "[ \t][ \t]*", $def );
  1374. if ( @a != 3 && @a != 4 ) {
  1375. my $msg =
  1376. "wrong syntax: define <name> Vallox devicename[\@baudrate] [busversion]";
  1377. Log3 undef, 2, "Vallox: " . $msg;
  1378. return $msg;
  1379. }
  1380. DevIo_CloseDev($hash);
  1381. my $name = $a[0];
  1382. my $dev = $a[2];
  1383. $hash->{BusVersion} = "2";
  1384. $dev .= "\@9600" if ( $dev !~ m/\@/ && $def !~ m/:/ );
  1385. $hash->{DeviceName} = $dev;
  1386. if ( @a == 4 ) {
  1387. $hash->{BusVersion} = $a[3];
  1388. }
  1389. if ( $hash->{BusVersion} eq "1" ) {
  1390. %Vallox_datatypes =
  1391. ( %Vallox_datatypes_legacy1, %Vallox_datatypes_base );
  1392. %Vallox_datatypesReverse =
  1393. ( %Vallox_datatypesReverse_legacy1, %Vallox_datatypesReverse_base );
  1394. }
  1395. else {
  1396. %Vallox_datatypes = %Vallox_datatypes_base;
  1397. %Vallox_datatypesReverse = %Vallox_datatypesReverse_base;
  1398. }
  1399. my $ret = DevIo_OpenDev( $hash, 0, undef );
  1400. return $ret;
  1401. }
  1402. ##################################
  1403. sub Vallox_Undef($$) {
  1404. my ( $hash, $name ) = @_;
  1405. DevIo_CloseDev($hash);
  1406. RemoveInternalTimer($hash);
  1407. return undef;
  1408. }
  1409. ##################################
  1410. sub Vallox_Delete($$) {
  1411. my ( $hash, $name ) = @_;
  1412. return undef;
  1413. }
  1414. ##################################
  1415. sub Vallox_Get($@) {
  1416. my ( $hash, @a ) = @_;
  1417. return "\"get Vallox\" needs at least one argument" if ( @a < 2 );
  1418. my $name = shift @a;
  1419. my $cmd = shift @a;
  1420. my $arg = shift @a;
  1421. # "reading" is a predefined list of readings from the bus
  1422. if ( $cmd eq "reading" ) {
  1423. my $msg;
  1424. if ($arg eq "CO2SetPoint") {
  1425. $msg = Vallox_CreateMsg( $hash, "b3" );
  1426. DevIo_SimpleWrite( $hash, $msg, 1 );
  1427. Log3( $name, 3, "Vallox: Request " . $msg . " has been sent." );
  1428. $msg = Vallox_CreateMsg( $hash, "b4" );
  1429. DevIo_SimpleWrite( $hash, $msg, 1 );
  1430. Log3( $name, 3, "Vallox: Request " . $msg . " has been sent." );
  1431. } else {
  1432. my $argKey = $Vallox_datatypesReverse{$arg};
  1433. $msg = Vallox_CreateMsg( $hash, $argKey );
  1434. DevIo_SimpleWrite( $hash, $msg, 1 );
  1435. Log3( $name, 3, "Vallox: Request " . $msg . " has been sent." );
  1436. }
  1437. return undef;
  1438. # "update" shall be ask for all possible data.
  1439. # Not working at the moment. Need a new idea :(
  1440. }
  1441. elsif ( $cmd eq "update" ) {
  1442. while ( my ( $argKey, $argValue ) = each %Vallox_datatypes ) {
  1443. my $msg = Vallox_CreateMsg( $hash, $argKey );
  1444. DevIo_SimpleWrite( $hash, $msg, 1 );
  1445. }
  1446. return undef;
  1447. # "raw is a custom Hex Code
  1448. }
  1449. elsif ( $cmd eq "raw" ) {
  1450. return "Usage: get $name raw {HexValue}"
  1451. if ( !defined($arg) || $arg =~ m/[^a-fA-F0-9]{2}/ );
  1452. my $msg = Vallox_CreateMsg( $hash, $arg );
  1453. DevIo_SimpleWrite( $hash, $msg, 1 );
  1454. Log3( $name, 3, "Vallox: Request " . $msg . " has been sent." );
  1455. return undef;
  1456. }
  1457. else {
  1458. my $retmsg;
  1459. my @commandList = keys %Vallox_gets;
  1460. my $commandCount = keys %Vallox_gets;
  1461. my @readingList = keys %Vallox_datatypesReverse;
  1462. my $readingCount = keys %Vallox_datatypesReverse;
  1463. $retmsg .= join( " ", @commandList ) if ( $commandCount > 0 );
  1464. if ( $readingCount > 0 ) {
  1465. $retmsg .= " reading:" . join( ",", sort @readingList );
  1466. $retmsg .= ",CO2SetPoint";
  1467. $readingCount = $readingCount + 1;
  1468. }
  1469. ## Ich kann nicht alle befehle bulken. ... :(
  1470. # $retmsg .= " update:noArg";
  1471. Log3( $name, 2, "Vallox: Unknown argument $cmd." )
  1472. if ( $cmd ne '?' && length $cmd );
  1473. return "Unknown argument $cmd, choose one of " . $retmsg;
  1474. }
  1475. }
  1476. ##################################
  1477. sub Vallox_Set($@) {
  1478. my ( $hash, @a ) = @_;
  1479. if ( @a < 2 ) {
  1480. return "\"set Vallox\" needs at least an argument";
  1481. }
  1482. my $domain = hex "0x"
  1483. . AttrVal( $hash->{NAME}, "ValloxIDDomain", "01" )
  1484. ; # Domain (1 by default)
  1485. my $sender = hex "0x"
  1486. . AttrVal( $hash->{NAME}, "ValloxIDFHEM", "2F" ); # ID of this FHEM
  1487. my $receiver = hex "0x"
  1488. . AttrVal( $hash->{NAME}, "ValloxIDCentral", "11" ); # ID of the central
  1489. my $broadcast = 0;
  1490. my $name = shift @a;
  1491. my $cmd = shift @a;
  1492. my $arg = shift @a;
  1493. my $datatype;
  1494. my $datavalue;
  1495. my $setCommands;
  1496. my @commandList = keys %Vallox_sets;
  1497. my $commandCount = keys %Vallox_sets;
  1498. $setCommands .= join( " ", @commandList ) if ( $commandCount > 0 );
  1499. $setCommands .= " FanSpeed:slider,1,1,8";
  1500. $setCommands .= " FanSpeedMin:slider,1,1,8";
  1501. $setCommands .= " FanSpeedMax:slider,1,1,8";
  1502. $setCommands .= " BasicHumidityLevel:slider,0,1,100";
  1503. $setCommands .= " HeatRecoveryCellBypassSetpointTemperature:slider,0,1,20";
  1504. $setCommands .= " ServiceReminderMonths:slider,1,1,15";
  1505. $setCommands .= " DCFanInputAdjustment:slider,0,1,100";
  1506. $setCommands .= " DCFanOutputAdjustment:slider,0,1,100";
  1507. $setCommands .= " CellDefrostingSetpointTemperature:slider,0,1,10";
  1508. $setCommands .= " HeatingSetPoint:slider,10,1,30";
  1509. $setCommands .= " CO2SetPoint:slider,500,100,2000";
  1510. foreach my $MR_key ( keys %Vallox_multiReadingTable_realcmd ) {
  1511. $setCommands .= " " . $MR_key . ":0,1";
  1512. }
  1513. # MR: Prepare Values and Command for datagram
  1514. if ( exists( $Vallox_multiReadingTable_realcmd{$cmd} )
  1515. && exists( $hash->{ "MR_" . $Vallox_multiReadingTable_realcmd{$cmd} } )
  1516. )
  1517. {
  1518. # TODO: Integrate get before set;
  1519. if ($hash->{ "MR_" . $Vallox_multiReadingTable_realcmd{$cmd} } ) {
  1520. return
  1521. "Vallox: Internal "
  1522. . $Vallox_multiReadingTable_realcmd{$cmd}
  1523. . " empty ("
  1524. . $hash->{ "MR_" . $cmd }
  1525. . "). Read "
  1526. . $Vallox_multiReadingTable_realcmd{$cmd}
  1527. . " first!";
  1528. }
  1529. $arg = Vallox_ReplaceBit(
  1530. $hash,
  1531. $hash->{ "MR_" . $Vallox_multiReadingTable_realcmd{$cmd} },
  1532. $Vallox_multiReadingTable_digit{$cmd}, $arg
  1533. );
  1534. $cmd = $Vallox_multiReadingTable_realcmd{$cmd};
  1535. }
  1536. ## TODO
  1537. if ( exists $Vallox_datatypesReverse{$cmd} ) {
  1538. $datatype = hex "0x" . $Vallox_datatypesReverse{$cmd};
  1539. # Method: FSM
  1540. if ( $datatype == 0x29 || $datatype == 0xA5 || $datatype == 0xA9 ) {
  1541. $datavalue = hex "0x" . $Vallox_levelTableReverse{$arg};
  1542. }
  1543. # Method: PCTM
  1544. elsif ( $datatype == 0xAE ) {
  1545. $datavalue = hex "0x" . $Vallox_percentageTableReverse{$arg};
  1546. }
  1547. # Method: TM
  1548. elsif ( $datatype == 0xA4 || $datatype == 0xAF ) {
  1549. $datavalue = hex "0x" . $Vallox_temperatureTableReverse{$arg};
  1550. }
  1551. # Method: DF
  1552. elsif ( $datatype == 0xA6 || $datatype == 0xB0 || $datatype == 0xB1 ) {
  1553. $datavalue = hex "0x" . sprintf("%x", $arg);
  1554. }
  1555. # Method: CDSTF
  1556. elsif ( $datatype == 0xB2 ) {
  1557. $datavalue = hex "0x" . sprintf("%x", $arg * 3);
  1558. }
  1559. # All other is Hex Input
  1560. else {
  1561. $datavalue = hex "0x" . $arg;
  1562. }
  1563. Vallox_CreateAndSend($hash, $domain, $sender, $receiver, $datatype, $datavalue, $broadcast);
  1564. }
  1565. elsif ( $cmd eq "CO2SetPoint" ) {
  1566. $broadcast = 1;
  1567. my $datatype_high = hex "0xb3";
  1568. my $datavalue_high = hex "0x" . substr( sprintf("%04x", $arg), 0, 2 );
  1569. Vallox_CreateAndSend($hash, $domain, $sender, $receiver, $datatype_high, $datavalue_high, $broadcast);
  1570. ## Sleep is needed due to broadcast. Message cache on bus devices seems very limited.
  1571. sleep(1);
  1572. my $datatype_low = hex "0xb4";
  1573. my $datavalue_low = hex "0x" . substr( sprintf("%04x", $arg), 2, 2 );
  1574. Vallox_CreateAndSend($hash, $domain, $sender, $receiver, $datatype_low, $datavalue_low, $broadcast);
  1575. }
  1576. elsif ( $cmd eq "raw" ) {
  1577. $datatype = hex "0x" . substr( $arg, 0, 2 );
  1578. $datavalue = hex "0x" . substr( $arg, 2, 2 );
  1579. Vallox_CreateAndSend($hash, $domain, $sender, $receiver, $datatype, $datavalue, $broadcast);
  1580. }
  1581. else {
  1582. Log3( $name, 2, "Vallox: Unknown argument $cmd." )
  1583. if ( $cmd ne '?' && length $cmd );
  1584. return "Unknown argument $cmd, choose one of " . $setCommands;
  1585. }
  1586. return undef;
  1587. }
  1588. sub Vallox_Attr(@) {
  1589. my ( $cmd, $name, $aName, $aVal ) = @_;
  1590. # $cmd can be "del" || "set"
  1591. # $name is device name
  1592. # aName and aVal are Attribute name and value
  1593. if ( $cmd eq "set" ) {
  1594. if ( $aName eq "ValloxIDDomain"
  1595. || $aName eq "ValloxIDFHEM"
  1596. || $aName eq "ValloxIDCentral" )
  1597. {
  1598. if ( $aVal =~ m/[^a-fA-F0-9]/ || length($aVal) != 2 ) {
  1599. Log3 $name, 2,
  1600. "Vallox: Invalid HexValue in attr $name $aName $aVal: $@";
  1601. return "Invalid HexValue $aVal";
  1602. }
  1603. }
  1604. }
  1605. return undef;
  1606. }
  1607. sub Vallox_Read($) {
  1608. my ($hash) = @_;
  1609. my $name = $hash->{NAME};
  1610. my $datagram;
  1611. my $rawReadingType;
  1612. my $rawReadingValue;
  1613. my $rawReadingChecksum;
  1614. my $singlereading = 1;
  1615. my $fineReadingValue = 1;
  1616. my $bufferDebugName = AttrVal( $hash->{NAME}, "ValloxBufferDebug", "0" );
  1617. # read from serial device
  1618. my $buf = DevIo_SimpleRead($hash);
  1619. return "" if ( !defined($buf) );
  1620. # Convert read data to hex and add to debug if nessecary
  1621. if ( $bufferDebugName eq 1 ) {
  1622. $hash->{"BufferDebug"} .= unpack( 'H*', $buf );
  1623. }
  1624. # Convert read data to hex and fill DevIO-Buffer
  1625. $bufferDevIO .= unpack( 'H*', $buf );
  1626. # DO Run Validation until DevIO-Buffer is less than 2 chars long
  1627. do {
  1628. # If DevIO-Buffer is filled add difference to 14 Chars to ReadBuffer and remove it from DevIO-Buffer
  1629. if ( length($bufferDevIO) >= 2 ) {
  1630. my $bufferReadSpace = 14 - length($bufferRead);
  1631. $bufferRead .= substr( $bufferDevIO, 0, $bufferReadSpace );
  1632. # If the bufferDevIO buffer is shorter than the filling, set it to empty to avoid error
  1633. if ( length($bufferDevIO) >= $bufferReadSpace ) {
  1634. $bufferDevIO = substr( $bufferDevIO, $bufferReadSpace );
  1635. }
  1636. else {
  1637. $bufferDevIO = "";
  1638. }
  1639. }
  1640. #$hash->{"BufferDevIOLength"} = length($bufferDevIO);
  1641. # Once ReadBuffer filled up, remove first Byte
  1642. if ( length($bufferRead) >= 14 ) {
  1643. $bufferRead = substr( $bufferRead, 2, 12 );
  1644. }
  1645. # If ReadBuffer has valid length start validating content
  1646. if ( length($bufferRead) == 12 ) {
  1647. if ( Vallox_ValidateStream($hash) == 1 ) {
  1648. Log3( $name, 5, "Vallox: Buffer: " . $bufferRead );
  1649. my $datagram = uc($bufferRead);
  1650. Vallox_InterpretAndUpdate( $hash, $datagram );
  1651. }
  1652. elsif ( Vallox_ValidateStream($hash) == 2 ) {
  1653. Log3( $name, 4, "Vallox: Invalid Status-Request: $bufferRead" );
  1654. }
  1655. }
  1656. } while ( length($bufferDevIO) >= 2 );
  1657. }
  1658. sub Vallox_Shutdown($) {
  1659. my ($hash) = @_;
  1660. DevIo_CloseDev($hash);
  1661. return undef;
  1662. }
  1663. 1;
  1664. =pod
  1665. =item device
  1666. =item summary Reads and writes parameters via RS485 from and to a Vallox ventilation bus.
  1667. =item summary_DE Liest und schreibt über RS485 aus und in einen Bus einer Vallox Belüftungsanlage
  1668. =begin html
  1669. <a name="Vallox"></a>
  1670. <h3>Vallox</h3>
  1671. <div>
  1672. <ul>
  1673. Vallox is a manufacturer for ventilation devices.
  1674. <br>
  1675. Their products have a built-in RS485-Interface on the central ventilation unit as well as on connected control units on which all control communication is handeled.
  1676. <br>
  1677. More Info on the particular <a href="http://www.fhemwiki.de/wiki/Vallox">page of FHEM-Wiki</a> (in German).
  1678. <br>
  1679. &nbsp;
  1680. <br>
  1681. <a name="Valloxdefine"></a>
  1682. <b>Define</b>
  1683. <ul>
  1684. <code>define &lt;name&gt; Vallox &lt;RS485-Device[@baud]&gt; [BusVersion]</code><br>
  1685. If the baudrate is omitted, it is set to 9600 (default Vallox baudrate).<br>
  1686. The BusVersion can be set to 1 for older systems. (Default: 2).<br>
  1687. <br>
  1688. Example: <code>define Ventilation Vallox /dev/ttyUSB1</code>
  1689. </ul>
  1690. <br>
  1691. <a name="Valloxset"></a>
  1692. <b>Set</b>
  1693. <ul>
  1694. <li><code>FanSpeed &lt; 1-8 &gt;</code>
  1695. <br>
  1696. Allows to set the fan speed (1 = lowest; 8 = highest).<br>
  1697. </li><br>
  1698. <li><code>BasicHumidityLevel &lt; 0-100 &gt;</code>
  1699. <br>
  1700. Allows to set the basic humidity level in percentage.<br>
  1701. </li><br>
  1702. <li><code>HeatRecoveryCellBypassSetpointTemperature &lt; 0-20 &gt;</code>
  1703. <br>
  1704. Allows to set the heat recovery cell bypass setpoint temperature.<br>
  1705. </li><br>
  1706. <li><code>raw &lt; HexValue &gt;</code><br>
  1707. HexValue is two 2-digit hex number to identify the type and value of setting.
  1708. </li><br>
  1709. <br>
  1710. Example to set the fan speed to 3:<br>
  1711. <code>set Ventilation raw 2907</code><br>
  1712. or:<br>
  1713. <code>set Ventilation FanSpeed 3</code>
  1714. </ul>
  1715. <br>
  1716. <a name="Valloxget"></a>
  1717. <b>Get</b>
  1718. <ul>
  1719. <li><code>reading &lt; readingname &gt;</code>
  1720. <br>
  1721. Allows to get any predefined reading.<br>
  1722. </li><br>
  1723. <li><code>raw &lt; HexValue &gt;</code><br>
  1724. HexValue is a 2-digit hex number to identify the requested reading.
  1725. </li><br>
  1726. </ul>
  1727. <br>
  1728. <a name="Valloxattr"></a>
  1729. <b>Attributes</b>
  1730. <ul><li><code>ValloxIDDomain &lt; HexValue &gt;</code>
  1731. <br>
  1732. HexValue is a 2-digit hex number to identify the &QUOT;address&QUOT; of the bus domain. (01 by default).
  1733. </li><br>
  1734. <li><code>ValloxIDCentral &lt; HexValue &gt;</code>
  1735. <br>
  1736. HexValue is a 2-digit hex number to identify the &QUOT;address&QUOT; of the central ventilation unit. (11 by default).<br>
  1737. In a normal installation ventilation units in the scope 11 to 19 and are addressed with 10 for broadcast-messages.
  1738. </li><br>
  1739. <li><code>ValloxIDFHEM &lt; HexValue &gt;</code>
  1740. <br>
  1741. HexValue is a 2-digit hex number to identify the &QUOT;address&QUOT; of this system as a virtual control terminal. (2F by default)<br>
  1742. In a normal installation control terminals are in the scope 21 to 29 and are addressed with 20 for broadcast-messages.<br>
  1743. The address must be unique.<br>
  1744. The &QUOT;panel address&QUOT; of the physical control terminal can be set on the settings of it. Possible values are 1-15 which is the second digit of the Hex-Value (1-F). The first digit is always 2.<br>
  1745. The physical control terminal is usually 21.
  1746. </li><br>
  1747. <li><code>ValloxBufferDebug &lt; 0/1 &gt;</code>
  1748. <br>
  1749. When 1, modul creates an Internal which fills with the raw Hex-Data from the bus. DEBUG ONLY! (0 by default).
  1750. </li><br>
  1751. <li><code>ValloxForceBroadcast &lt; 0/1 &gt;</code>
  1752. <br>
  1753. When 1, modul sends commands not only to the central ventilation unit (11) but to all possible addresses by broadcast (10/20). This is sometimes nessecary for older systems. (0 by default; Function always on on BusVersion 1).
  1754. </li><br>
  1755. <li><code>ValloxProcessOwnCommands &lt; 0/1 &gt;</code>
  1756. <br>
  1757. When 1, modul sends commands not only to the bus but processes it as a received reading. This is sometimes nessecary for older systems. (0 by default; Function always on on BusVersion 1).
  1758. </li><br>
  1759. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  1760. </ul>
  1761. </ul>
  1762. </div>
  1763. =end html
  1764. =begin html_DE
  1765. <a name="Vallox"></a>
  1766. <h3>Vallox</h3>
  1767. <div>
  1768. <ul>
  1769. Vallox ist ein Hersteller von Bel&uuml;ftungsanlagen mit W&auml;rmetauscher.
  1770. <br>
  1771. Die Systeme verf&uuml;gen sowohl an der zentralen L&uuml;ftungskomponente, als auch an den Terminals &uuml;ber eine RS485-Schnittstelle &uuml;ber die die gesamte interne Kommunikation abgewickelt wird.
  1772. <br>
  1773. Mehr Informationen sind auf der <a href="http://www.fhemwiki.de/wiki/Vallox">FHEM-Wiki-Seite</a> verf&uuml;gbar.
  1774. <br>
  1775. &nbsp;
  1776. <br>
  1777. <a name="Valloxdefine"></a>
  1778. <b>Define</b>
  1779. <ul>
  1780. <code>define &lt;name&gt; Vallox &lt;RS485-Device[@baud]&gt; [BusVersion]</code><br>
  1781. Wird die Baudrate weggelassen wird mit 9600 baud kommuniziert. (Standardrate des Vallox-Busses).<br>
  1782. Die BusVersion kann bei &auml;lteren Anlagen auf 1 gesetzt werden. (Standard: 2).<br>
  1783. <br>
  1784. Beispiel: <code>define Ventilation Vallox /dev/ttyUSB1</code>
  1785. </ul>
  1786. <br>
  1787. <a name="Valloxset"></a>
  1788. <b>Set</b>
  1789. <ul>
  1790. <li><code>FanSpeed &lt; 1-8 &gt;</code>
  1791. <br>
  1792. Erlaubt das &Auml;ndern der L&uuml;ftergeschwindigkeit (1 = minimal; 8 = maximal).<br>
  1793. </li><br>
  1794. <li><code>BasicHumidityLevel &lt; 0-100 &gt;</code>
  1795. <br>
  1796. Erlaubt das &Auml;ndern des Luftfeuchtigkeits-Grenzwertes (Terminaldisplay: <code>Grenzwert &#037;RH</code>).<br>
  1797. </li><br>
  1798. <li><code>HeatRecoveryCellBypassSetpointTemperature &lt; 0-20 &gt;</code>
  1799. <br>
  1800. Erlaubt das &Auml;ndern des Grenzwertes f&uuml;r den W&auml;rmetauscher-Bypass (Terminaldisplay: <code>WRG Bypass</code>)<br>
  1801. </li><br>
  1802. <li><code>raw &lt; HexWert &gt;</code><br>
  1803. HexWert sind <u>zwei</u> 2-stellige Hex-Zahlen, welche den Typ und den Wert der Einstellung identifiziert.
  1804. </li><br>
  1805. <br>
  1806. Beispiel um die L&uuml;ftergeschwindigkeit auf 3 zu setzen:<br>
  1807. <code>set Ventilation raw 2907</code><br>
  1808. oder:<br>
  1809. <code>set Ventilation FanSpeed 3</code>
  1810. </ul>
  1811. <br>
  1812. <a name="Valloxget"></a>
  1813. <b>Get</b>
  1814. <ul>
  1815. <li><code>reading &lt; readingname &gt;</code>
  1816. <br>
  1817. Erlaubt das Auslesen der vorgegebenen Datenpunkte aus dem Bus.<br>
  1818. </li><br>
  1819. <li><code>raw &lt; HexWert &gt;</code><br>
  1820. HexWert ist <u>eine</u> 2-stellige Hex-Zahl, welche den Typ der abzufragenden Einstellung identifiziert.
  1821. </li><br>
  1822. </ul>
  1823. <br>
  1824. <a name="Valloxattr"></a>
  1825. <b>Attribute</b>
  1826. <ul><li><code>ValloxIDDomain &lt; HexWert &gt;</code>
  1827. <br>
  1828. HexWert ist eine 2-stellige Hex-Zahl die als &QUOT;Adresse&QUOT; der Bus-Dom&auml;ne dient. (Standard: 01).
  1829. </li><br>
  1830. <li><code>ValloxIDCentral &lt; HexWert &gt;</code>
  1831. <br>
  1832. HexWert ist eine 2-stellige Hex-Zahl die als &QUOT;Adresse&QUOT; der zentralen Ventilationseinheit dient. (Standard: 11).<br>
  1833. In einer normalen Umgebung werden die Ventilationseinheiten mit 11 - 1F adressiert. 10 ist die Broadcast-Adresse.<br>
  1834. </li><br>
  1835. <li><code>ValloxIDFHEM &lt; HexWert &gt;</code>
  1836. <br>
  1837. HexWert ist eine 2-stellige Hex-Zahl die als &QUOT;Adresse&QUOT; dieses Systems als virtuelles Kontrollterminal dient. (Standard: 2F).<br>
  1838. Sie darf nicht bereits im Bus genutzt werden.<br>
  1839. In einer normalen Umgebung werden die Kontrollterminals mit 21 - 2F adressiert. 20 ist die Broadcast-Adresse.<br>
  1840. In den Einstellungen der physikalisch vorhandenen Terminals kann die &QUOT;FBD-Adresse&QUOT; des jeweiligen Terminals eingestellt werden.<br>
  1841. Hierbei stehen die Werte 1-15 zur Verf&uuml;gung, was der zweiten Stelle dieser Adresse (1-F) entspricht. Die erste Stelle ist immer 2.<br>
  1842. Das physikalische Kontrollterminal ist &uuml;blicherweise die 21.
  1843. </li><br>
  1844. <li><code>ValloxBufferDebug &lt; 0/1 &gt;</code>
  1845. <br>
  1846. Wenn 1, erzeugt das Modul ein Internal in welches die rohen Hex-Daten aus dem Bus herein geschrieben. NUR ZUM DEBUGGEN! (Standard: 0).
  1847. </li><br>
  1848. <li><code>ValloxForceBroadcast &lt; 0/1 &gt;</code>
  1849. <br>
  1850. Wenn 1, sendet das Modul die Befehle nicht nur an die zentrale Ventilationseinheit (11), sondern auch an alle Broadcast-Adressen (10/20). Dies ist manchmal bei &auml;lteren Anlagen notwendig, wenn sich die Anzeige auf den Kontrollterminals nicht mit aktualisiert. (Standard: 0; Funktion immer an bei BusVersion 1).
  1851. </li><br>
  1852. <li><code>ValloxProcessOwnCommands &lt; 0/1 &gt;</code>
  1853. <br>
  1854. Wenn 1, behandelt das Modul die eigenen Befehle auch als Empfangene Befehle und verarbeitet sie intern weiter. Dies ist manchmal bei &auml;lteren Anlagen notwendig. (Standard: 0; Funktion immer an bei BusVersion 1).
  1855. </li><br>
  1856. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  1857. </ul>
  1858. </ul>
  1859. </div>
  1860. =end html_DE
  1861. =cut