1 #include "stdafx.h"
2 #include "EnOceanESP3.h"
3 #include "../main/Logger.h"
4 #include "../main/Helper.h"
5 #include "../main/RFXtrx.h"
6 #include "../main/SQLHelper.h"
7
8 #include <string>
9 #include <algorithm>
10 #include <iostream>
11 #include <boost/bind.hpp>
12 #include "hardwaretypes.h"
13 #include "../main/localtime_r.h"
14
15 #include <boost/exception/diagnostic_information.hpp>
16 #include <cmath>
17 #include <ctime>
18
19 #if _DEBUG
20 #define ENOCEAN_BUTTON_DEBUG
21 #endif
22
23 #define ENABLE_LOGGING
24
25 #define ENOCEAN_RETRY_DELAY 30
26
27 //Write/Read has to be done in sync with ESP3
28
29 #define ESP3_SYNC 0x55
30 #define ESP3_HEADER_LENGTH 0x4
31
32 #define round(a) ( int ) ( a + .5 )
33
34 extern const char* Get_EnoceanManufacturer(unsigned long ID);
35 extern const char* Get_Enocean4BSType(const int Org, const int Func, const int Type);
36
37 // the following lines are taken from EO300I API header file
38
39 //polynomial G(x) = x8 + x2 + x1 + x0 is used to generate the CRC8 table
40 const unsigned char crc8table[256] = {
41 0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15,
42 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d,
43 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65,
44 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d,
45 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5,
46 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd,
47 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85,
48 0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd,
49 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2,
50 0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea,
51 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2,
52 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a,
53 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32,
54 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a,
55 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42,
56 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a,
57 0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c,
58 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4,
59 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec,
60 0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4,
61 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c,
62 0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44,
63 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c,
64 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34,
65 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b,
66 0x76, 0x71, 0x78, 0x7f, 0x6A, 0x6d, 0x64, 0x63,
67 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b,
68 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13,
69 0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb,
70 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8D, 0x84, 0x83,
71 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb,
72 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3
73 };
74
75 #define proc_crc8(crc, data) (crc8table[crc ^ data])
76
77 #define SER_SYNCH_CODE 0x55
78 #define SER_HEADER_NR_BYTES 0x04
79
80 //! Packet structure (ESP3)
81 typedef struct
82 {
83 // Amount of raw data bytes to be received. The most significant byte is sent/received first
84 unsigned short u16DataLength;
85 // Amount of optional data bytes to be received
86 unsigned char u8OptionLength;
87 // Packe type code
88 unsigned char u8Type;
89 // Data buffer: raw data + optional bytes
90 unsigned char *u8DataBuffer;
91 } PACKET_SERIAL_TYPE;
92
93 //! Packet type (ESP3)
94 typedef enum
95 {
96 PACKET_RESERVED = 0x00, //! Reserved
97 PACKET_RADIO = 0x01, //! Radio telegram
98 PACKET_RESPONSE = 0x02, //! Response to any packet
99 PACKET_RADIO_SUB_TEL = 0x03, //! Radio sub telegram (EnOcean internal function )
100 PACKET_EVENT = 0x04, //! Event message
101 PACKET_COMMON_COMMAND = 0x05, //! Common command
102 PACKET_SMART_ACK_COMMAND = 0x06, //! Smart Ack command
103 PACKET_REMOTE_MAN_COMMAND = 0x07, //! Remote management command
104 PACKET_PRODUCTION_COMMAND = 0x08, //! Production command
105 PACKET_RADIO_MESSAGE = 0x09, //! Radio message (chained radio telegrams)
106 PACKET_RADIO_ADVANCED = 0x0a //! Advanced Protocol radio telegram
107
108 } PACKET_TYPE;
109
110 //! Response type
111 typedef enum
112 {
113 RET_OK = 0x00, //! OK ... command is understood and triggered
114 RET_ERROR = 0x01, //! There is an error occured
115 RET_NOT_SUPPORTED = 0x02, //! The functionality is not supported by that implementation
116 RET_WRONG_PARAM = 0x03, //! There was a wrong parameter in the command
117 RET_OPERATION_DENIED = 0x04, //! Example: memory access denied (code-protected)
118 RET_USER = 0x80 //! Return codes greater than 0x80 are used for commands with special return information, not commonly useable.
119 } RESPONSE_TYPE;
120
121 //! Common command enum
122 typedef enum
123 {
124 CO_WR_SLEEP = 1, //! Order to enter in energy saving mode
125 CO_WR_RESET = 2, //! Order to reset the device
126 CO_RD_VERSION = 3, //! Read the device (SW) version / (HW) version, chip ID etc.
127 CO_RD_SYS_LOG = 4, //! Read system log from device databank
128 CO_WR_SYS_LOG = 5, //! Reset System log from device databank
129 CO_WR_BIST = 6, //! Perform Flash BIST operation
130 CO_WR_IDBASE = 7, //! Write ID range base number
131 CO_RD_IDBASE = 8, //! Read ID range base number
132 CO_WR_REPEATER = 9, //! Write Repeater Level off,1,2
133 CO_RD_REPEATER = 10, //! Read Repeater Level off,1,2
134 CO_WR_FILTER_ADD = 11, //! Add filter to filter list
135 CO_WR_FILTER_DEL = 12, //! Delete filter from filter list
136 CO_WR_FILTER_DEL_ALL= 13, //! Delete filters
137 CO_WR_FILTER_ENABLE = 14, //! Enable/Disable supplied filters
138 CO_RD_FILTER = 15, //! Read supplied filters
139 CO_WR_WAIT_MATURITY = 16, //! Waiting till end of maturity time before received radio telegrams will transmitted
140 CO_WR_SUBTEL = 17, //! Enable/Disable transmitting additional subtelegram info
141 CO_WR_MEM = 18, //! Write x bytes of the Flash, XRAM, RAM0 ….
142 CO_RD_MEM = 19, //! Read x bytes of the Flash, XRAM, RAM0 ….
143 CO_RD_MEM_ADDRESS = 20, //! Feedback about the used address and length of the config area and the Smart Ack Table
144 CO_RD_SECURITY = 21, //! Read security informations (level, keys)
145 CO_WR_SECURITY = 22, //! Write security informations (level, keys)
146 } COMMON_COMMAND_TYPE;
147
148 typedef enum {
149 RORG_ST = 0x30, //Secure telegram
150 RORG_ST_WE = 0x31, //Secure telegram with encapsulation
151 RORG_STT_FW = 0x35, //Secure Teach-In telegram for switch
152 RORG_4BS = 0xA5,
153 RORG_ADT = 0xA6,
154 RORG_SM_REC = 0xA7,
155 RORG_GP_SD = 0xB3, //Generic Profiles selective data
156 RORG_SM_LRN_REQ = 0xC6,
157 RORG_SM_LRN_ANS = 0xC7,
158 RORG_SM_ACK_SGNL = 0xD0, //Smart Acknowledge Signal telegram
159 RORG_MSC = 0xD1, // Manufacturer Specific Communicatio
160 RORG_VLD = 0xD2, // Variable length data telegram
161 RORG_UTI = 0xD4, //Universal Teach-In EEP based
162 RORG_1BS = 0xD5,
163 RORG_RPS = 0xF6,
164 RORG_SYS_EX = 0xC5,
165 } ESP3_RORG;
166
167 //! Function return codes
168 typedef enum
169 {
170 //! <b>0</b> - Action performed. No problem detected
171 OK=0,
172 //! <b>1</b> - Action couldn't be carried out within a certain time.
173 TIME_OUT,
174 //! <b>2</b> - The write/erase/verify process failed, the flash page seems to be corrupted
175 FLASH_HW_ERROR,
176 //! <b>3</b> - A new UART/SPI byte received
177 NEW_RX_BYTE,
178 //! <b>4</b> - No new UART/SPI byte received
179 NO_RX_BYTE,
180 //! <b>5</b> - New telegram received
181 NEW_RX_TEL,
182 //! <b>6</b> - No new telegram received
183 NO_RX_TEL,
184 //! <b>7</b> - Checksum not valid
185 NOT_VALID_CHKSUM,
186 //! <b>8</b> - Telegram not valid
187 NOT_VALID_TEL,
188 //! <b>9</b> - Buffer full, no space in Tx or Rx buffer
189 BUFF_FULL,
190 //! <b>10</b> - Address is out of memory
191 ADDR_OUT_OF_MEM,
192 //! <b>11</b> - Invalid function parameter
193 NOT_VALID_PARAM,
194 //! <b>12</b> - Built in self test failed
195 BIST_FAILED,
196 //! <b>13</b> - Before entering power down, the short term timer had timed out.
197 ST_TIMEOUT_BEFORE_SLEEP,
198 //! <b>14</b> - Maximum number of filters reached, no more filter possible
199 MAX_FILTER_REACHED,
200 //! <b>15</b> - Filter to delete not found
201 FILTER_NOT_FOUND,
202 //! <b>16</b> - BaseID out of range
203 BASEID_OUT_OF_RANGE,
204 //! <b>17</b> - BaseID was changed 10 times, no more changes are allowed
205 BASEID_MAX_REACHED,
206 //! <b>18</b> - XTAL is not stable
207 XTAL_NOT_STABLE,
208 //! <b>19</b> - No telegram for transmission in queue
209 NO_TX_TEL,
210 //! <b>20</b> - Waiting before sending broadcast message
211 TELEGRAM_WAIT,
212 //! <b>21</b> - Generic out of range return code
213 OUT_OF_RANGE,
214 //! <b>22</b> - Function was not executed due to sending lock
215 LOCK_SET,
216 //! <b>23</b> - New telegram transmitted
217 NEW_TX_TEL
218 } RETURN_TYPE;
219 // end of lines from EO300I API header file
220
221 /**
222 * @defgroup bitmasks Bitmasks for various fields.
223 * There are two definitions for every bit mask. First, the bit mask itself
224 * and also the number of necessary shifts.
225 * @{
226 */
227 /**
228 * @defgroup status_rps Status of telegram (for RPS telegrams)
229 * Bitmasks for the status-field, if ORG = RPS.
230 * @{
231 */
232 #define S_RPS_T21 0x20
233 #define S_RPS_T21_SHIFT 5
234 #define S_RPS_NU 0x10
235 #define S_RPS_NU_SHIFT 4
236 #define S_RPS_RPC 0x0F
237 #define S_RPS_RPC_SHIFT 0
238 /*@}*/
239
240 #define F60201_R1_MASK 0xE0
241 #define F60201_R1_SHIFT 5
242 #define F60201_EB_MASK 0x10
243 #define F60201_EB_SHIFT 4
244 #define F60201_R2_MASK 0x0E
245 #define F60201_R2_SHIFT 1
246 #define F60201_SA_MASK 0x01
247 #define F60201_SA_SHIFT 0
248
249 #define F60201_BUTTON_A1 0
250 #define F60201_BUTTON_A0 1
251 #define F60201_BUTTON_B1 2
252 #define F60201_BUTTON_B0 3
253
254 /**
255 * @defgroup status_rpc Status of telegram (for 1BS, 4BS, HRC or 6DT telegrams)
256 * Bitmasks for the status-field, if ORG = 1BS, 4BS, HRC or 6DT.
257 * @{
258 */
259 #define S_RPC 0x0F
260 #define S_RPC_SHIFT 0
261 /*@}*/
262
263 /**
264 * @defgroup data3 Meaning of data_byte 3 (for RPS telegrams, NU = 1)
265 * Bitmasks for the data_byte3-field, if ORG = RPS and NU = 1.
266 * Specification can be found at:
267 * https://www.enocean.com/fileadmin/redaktion/enocean_alliance/pdf/EnOcean_Equipment_Profiles_EEP_V2.6.3_public.pdf
268 * @{
269 */
270
271 //! Rocker ID Mask
272 #define DB3_RPS_NU_RID 0xC0
273 #define DB3_RPS_NU_RID_SHIFT 6
274
275 //! Button ID Mask
276 #define DB3_RPS_NU_BID 0xE0
277 #define DB3_RPS_NU_BID_SHIFT 5
278
279 //! Up Down Mask
280 #define DB3_RPS_NU_UD 0x20
281 #define DB3_RPS_NU_UD_SHIFT 5
282
283 //! Pressed Mask
284 #define DB3_RPS_NU_PR 0x10
285 #define DB3_RPS_NU_PR_SHIFT 4
286
287 //! Second Rocker ID Mask
288 #define DB3_RPS_NU_SRID 0x0C
289 #define DB3_RPS_NU_SRID_SHIFT 2
290
291 //! Second Button ID Mask
292 #define DB3_RPS_NU_SBID 0x0E
293 #define DB3_RPS_NU_SBID_SHIFT 1
294
295 //! Second UpDown Mask
296 #define DB3_RPS_NU_SUD 0x02
297 #define DB3_RPS_NU_SUD_SHIFT 1
298
299 //! Second Action Mask
300 #define DB3_RPS_NU_SA 0x01
301 #define DB3_RPS_NU_SA_SHIFT 0
302
303 /*@}*/
304
305 /**
306 * @defgroup data3_1 Meaning of data_byte 3 (for RPS telegrams, NU = 0)
307 * Bitmasks for the data_byte3-field, if ORG = RPS and NU = 0.
308 * @{
309 */
310 #define DB3_RPS_BUTTONS 0xE0
311 #define DB3_RPS_BUTTONS_SHIFT 5
312 #define DB3_RPS_PR 0x10
313 #define DB3_RPS_PR_SHIFT 4
314 /*@}*/
315
316 /**
317 * @defgroup data0 Meaning of data_byte 0 (for 4BS telegrams)
318 * Bitmasks for the data_byte0-field, if ORG = 4BS.
319 * @{
320 */
321 #define DB0_4BS_DI_3 0x08
322 #define DB0_4BS_DI_3_SHIFT 3
323 #define DB0_4BS_DI_2 0x04
324 #define DB0_4BS_DI_2_SHIFT 2
325 #define DB0_4BS_DI_1 0x02
326 #define DB0_4BS_DI_1_SHIFT 1
327 #define DB0_4BS_DI_0 0x01
328 #define DB0_4BS_DI_0_SHIFT 0
329 /*@}*/
330
331 /**
332 * @defgroup data3_hrc Meaning of data_byte 3 (for HRC telegrams)
333 * Bitmasks for the data_byte3-field, if ORG = HRC.
334 * @{
335 */
336 #define DB3_HRC_RID 0xC0
337 #define DB3_HRC_RID_SHIFT 6
338 #define DB3_HRC_UD 0x20
339 #define DB3_HRC_UD_SHIFT 5
340 #define DB3_HRC_PR 0x10
341 #define DB3_HRC_PR_SHIFT 4
342 #define DB3_HRC_SR 0x08
343 #define DB3_HRC_SR_SHIFT 3
344
345 // 2016-01-31 Stéphane Guillard : added 4BS learn bit definitions below
346 #define RORG_4BS_TEACHIN_LRN_BIT (1 << 3)
347 #define RORG_4BS_TEACHIN_EEP_BIT (1 << 7)
348
sendFrame(unsigned char frametype,unsigned char * databuf,unsigned short datalen,unsigned char * optdata,unsigned char optdatalen)349 bool CEnOceanESP3::sendFrame(unsigned char frametype, unsigned char *databuf, unsigned short datalen, unsigned char *optdata, unsigned char optdatalen)
350 {
351 unsigned char crc=0;
352 unsigned char buf[1024];
353 int len=0;
354
355 buf[len++]=SER_SYNCH_CODE;
356 buf[len++]=(datalen >> 8) & 0xff; // len
357 buf[len++]=datalen & 0xff;
358 buf[len++]=optdatalen;
359 buf[len++]=frametype;
360 crc = proc_crc8(crc, buf[1]);
361 crc = proc_crc8(crc, buf[2]);
362 crc = proc_crc8(crc, buf[3]);
363 crc = proc_crc8(crc, buf[4]);
364 buf[len++]=crc;
365 crc = 0;
366 for (int i=0;i<datalen;i++) {
367 buf[len]=databuf[i];
368 crc=proc_crc8(crc, buf[len++]);
369 }
370 for (int i=0;i<optdatalen;i++) {
371 buf[len]=optdata[i];
372 crc=proc_crc8(crc, buf[len++]);
373 }
374 buf[len++]=crc;
375 write((const char*)&buf,len);
376 return true;
377 }
378
sendFrameQueue(unsigned char frametype,unsigned char * databuf,unsigned short datalen,unsigned char * optdata,unsigned char optdatalen)379 bool CEnOceanESP3::sendFrameQueue(unsigned char frametype, unsigned char *databuf, unsigned short datalen, unsigned char *optdata, unsigned char optdatalen)
380 {
381 unsigned char crc=0;
382 unsigned char buf[1024];
383 int len=0;
384
385 buf[len++]=SER_SYNCH_CODE;
386 buf[len++]=(datalen >> 8) & 0xff; // len
387 buf[len++]=datalen & 0xff;
388 buf[len++]=optdatalen;
389 buf[len++]=frametype;
390 crc = proc_crc8(crc, buf[1]);
391 crc = proc_crc8(crc, buf[2]);
392 crc = proc_crc8(crc, buf[3]);
393 crc = proc_crc8(crc, buf[4]);
394 buf[len++]=crc;
395 crc = 0;
396 for (int i=0;i<datalen;i++) {
397 buf[len]=databuf[i];
398 crc=proc_crc8(crc, buf[len++]);
399 }
400 for (int i=0;i<optdatalen;i++) {
401 buf[len]=optdata[i];
402 crc=proc_crc8(crc, buf[len++]);
403 }
404 buf[len++]=crc;
405 Add2SendQueue((const char*)&buf,len);
406 return true;
407 }
408
CEnOceanESP3(const int ID,const std::string & devname,const int type)409 CEnOceanESP3::CEnOceanESP3(const int ID, const std::string& devname, const int type)
410 {
411 m_HwdID=ID;
412 m_szSerialPort=devname;
413 m_Type = type;
414 m_bufferpos=0;
415 memset(&m_buffer,0,sizeof(m_buffer));
416 m_id_base=0;
417 m_receivestate=ERS_SYNCBYTE;
418
419 //Test
420 //m_ReceivedPacketType = 0x01;
421 //m_DataSize = 0x0A;
422 //m_OptionalDataSize = 0x07;
423 //m_bufferpos = 0;
424 //m_buffer[m_bufferpos++] = 0xA5;
425 //ParseData();
426 }
427
~CEnOceanESP3()428 CEnOceanESP3::~CEnOceanESP3()
429 {
430
431 }
432
StartHardware()433 bool CEnOceanESP3::StartHardware()
434 {
435 RequestStart();
436
437 Init();
438
439 m_retrycntr=ENOCEAN_RETRY_DELAY*5; //will force reconnect first thing
440
441 //Start worker thread
442 m_thread = std::make_shared<std::thread>(&CEnOceanESP3::Do_Work, this);
443 SetThreadNameInt(m_thread->native_handle());
444
445 return (m_thread != nullptr);
446 }
447
StopHardware()448 bool CEnOceanESP3::StopHardware()
449 {
450 if (m_thread)
451 {
452 RequestStop();
453 m_thread->join();
454 m_thread.reset();
455 }
456 m_bIsStarted=false;
457 return true;
458 }
459
Init()460 void CEnOceanESP3::Init()
461 {
462 ReloadVLDNodes();
463 }
464
ReloadVLDNodes()465 void CEnOceanESP3::ReloadVLDNodes()
466 {
467 m_VLDNodes.clear();
468 std::vector<std::vector<std::string> > result;
469 result = m_sql.safe_query("SELECT ID, DeviceID, Manufacturer, Profile, [Type] FROM EnoceanSensors WHERE (HardwareID==%d)", m_HwdID);
470 if (!result.empty())
471 {
472 for (const auto & itt : result)
473 {
474 std::vector<std::string> sd = itt;
475 _tVLDNode node;
476 node.idx = atoi(sd[0].c_str());
477 node.manufacturer = atoi(sd[2].c_str());
478 node.profile = (uint8_t)atoi(sd[3].c_str());
479 node.type = (uint8_t)atoi(sd[4].c_str());
480
481 //convert to hex, and we have our ID
482 std::stringstream s_strid;
483 s_strid << std::hex << std::uppercase << sd[1];
484 uint32_t devid;
485 s_strid >> devid;
486 m_VLDNodes[devid] = node;
487 }
488 }
489 }
490
Do_Work()491 void CEnOceanESP3::Do_Work()
492 {
493 int msec_counter=0;
494 int sec_counter = 0;
495
496 _log.Log(LOG_STATUS, "EnOcean: Worker started...");
497
498 while (!IsStopRequested(200))
499 {
500 msec_counter++;
501 if (msec_counter == 5)
502 {
503 msec_counter = 0;
504 sec_counter++;
505 if (sec_counter % 12 == 0)
506 {
507 m_LastHeartbeat = mytime(NULL);
508 }
509 }
510
511 if (!isOpen())
512 {
513 if (m_retrycntr==0)
514 {
515 _log.Log(LOG_STATUS,"EnOcean: serial retrying in %d seconds...", ENOCEAN_RETRY_DELAY);
516 }
517 m_retrycntr++;
518 if (m_retrycntr/5>=ENOCEAN_RETRY_DELAY)
519 {
520 m_retrycntr=0;
521 m_bufferpos=0;
522 OpenSerialDevice();
523 }
524 }
525 if (m_sendqueue.size()>0)
526 {
527 std::lock_guard<std::mutex> l(m_sendMutex);
528
529 std::vector<std::string>::iterator itt=m_sendqueue.begin();
530 if (itt!=m_sendqueue.end())
531 {
532 std::string sBytes=*itt;
533 write(sBytes.c_str(),sBytes.size());
534 m_sendqueue.erase(itt);
535 }
536 }
537 }
538 terminate();
539
540 _log.Log(LOG_STATUS,"EnOcean: Worker stopped...");
541 }
542
Add2SendQueue(const char * pData,const size_t length)543 void CEnOceanESP3::Add2SendQueue(const char* pData, const size_t length)
544 {
545 #ifdef ENABLE_LOGGING
546 std::stringstream sstr;
547
548 for (size_t idx = 0; idx < length; idx++)
549 {
550 sstr << std::hex << std::uppercase << std::setw(2) << std::setfill('0') << (((unsigned int)pData[idx]) & 0xFF);
551 if (idx != length - 1)
552 sstr << " ";
553 }
554 _log.Log(LOG_STATUS,"EnOcean Send: %s",sstr.str().c_str());
555 #endif
556
557 std::string sBytes;
558 sBytes.insert(0,pData,length);
559 std::lock_guard<std::mutex> l(m_sendMutex);
560 m_sendqueue.push_back(sBytes);
561 }
562
563
OpenSerialDevice()564 bool CEnOceanESP3::OpenSerialDevice()
565 {
566 //Try to open the Serial Port
567 try
568 {
569 open(m_szSerialPort, 57600); //ECP3 open with 57600
570 _log.Log(LOG_STATUS,"EnOcean: Using serial port: %s", m_szSerialPort.c_str());
571 }
572 catch (boost::exception & e)
573 {
574 _log.Log(LOG_ERROR,"EnOcean: Error opening serial port!");
575 #ifdef _DEBUG
576 _log.Log(LOG_ERROR,"-----------------\n%s\n----------------", boost::diagnostic_information(e).c_str());
577 #else
578 (void)e;
579 #endif
580 return false;
581 }
582 catch ( ... )
583 {
584 _log.Log(LOG_ERROR,"EnOcean: Error opening serial port!!!");
585 return false;
586 }
587 m_bIsStarted=true;
588
589 m_receivestate=ERS_SYNCBYTE;
590 setReadCallback(boost::bind(&CEnOceanESP3::readCallback, this, _1, _2));
591 sOnConnected(this);
592
593 uint8_t buf[100];
594
595 //Request BASE_ID
596 m_bBaseIDRequested=true;
597 buf[0] = CO_RD_IDBASE;
598 sendFrameQueue(PACKET_COMMON_COMMAND,buf,1,NULL,0);
599
600 //Request Version
601 buf[0] = CO_RD_VERSION;
602 sendFrameQueue(PACKET_COMMON_COMMAND,buf,1,NULL,0);
603
604 return true;
605 }
606
readCallback(const char * data,size_t len)607 void CEnOceanESP3::readCallback(const char *data, size_t len)
608 {
609 size_t ii=0;
610
611 while (ii<len)
612 {
613 const unsigned char c = data[ii];
614
615 switch (m_receivestate)
616 {
617 case ERS_SYNCBYTE:
618 if (c!=0x55)
619 return;
620 m_receivestate = ERS_HEADER;
621 m_bufferpos=0;
622 break;
623 case ERS_HEADER:
624 m_buffer[m_bufferpos++]=c;
625 if (m_bufferpos==5)
626 {
627 m_DataSize=(m_buffer[0]<<8)|m_buffer[1];
628 m_OptionalDataSize=m_buffer[2];
629 m_ReceivedPacketType=m_buffer[3];
630 unsigned char CRCH=m_buffer[4];
631
632 unsigned char crc=0;
633 crc = proc_crc8(crc, m_buffer[0]);
634 crc = proc_crc8(crc, m_buffer[1]);
635 crc = proc_crc8(crc, m_buffer[2]);
636 crc = proc_crc8(crc, m_buffer[3]);
637
638 if (CRCH==crc)
639 {
640 m_bufferpos=0;
641 m_wantedlength=m_DataSize+m_OptionalDataSize;
642 m_receivestate = ERS_DATA;
643 }
644 else
645 {
646 _log.Log(LOG_ERROR,"EnOcean: Frame Checksum Error!...");
647 m_receivestate = ERS_SYNCBYTE;
648 }
649 }
650 break;
651 case ERS_DATA:
652 m_buffer[m_bufferpos++] = c;
653 if (m_bufferpos>=m_wantedlength)
654 {
655 m_receivestate = ERS_CHECKSUM;
656 }
657 break;
658 case ERS_CHECKSUM:
659 {
660 unsigned char CRCD=c;
661 unsigned char crc=0;
662 for (int iCheck=0; iCheck<m_wantedlength; iCheck++)
663 crc = proc_crc8(crc, m_buffer[iCheck]);
664 if (CRCD==crc)
665 {
666 ParseData();
667 }
668 m_receivestate = ERS_SYNCBYTE;
669 }
670 break;
671 }
672 ii++;
673 }
674
675 }
676
WriteToHardware(const char * pdata,const unsigned char)677 bool CEnOceanESP3::WriteToHardware(const char *pdata, const unsigned char /*length*/)
678 {
679 if (m_id_base==0)
680 return false;
681 if (!isOpen())
682 return false;
683 RBUF *tsen=(RBUF*)pdata;
684 if (tsen->LIGHTING2.packettype!=pTypeLighting2)
685 return false; //only allowed to control switches
686
687 unsigned long sID=(tsen->LIGHTING2.id1<<24)|(tsen->LIGHTING2.id2<<16)|(tsen->LIGHTING2.id3<<8)|tsen->LIGHTING2.id4;
688 if ((sID<m_id_base)||(sID>m_id_base+129))
689 {
690 _log.Log(LOG_ERROR,"EnOcean (1): Can not switch with this DeviceID, use a switch created with our id_base!...");
691 return false;
692 }
693
694 unsigned char RockerID=0;
695 unsigned char Pressed=1;
696
697 if (tsen->LIGHTING2.unitcode < 10)
698 {
699 RockerID = tsen->LIGHTING2.unitcode - 1;
700 }
701 else
702 return false;//double not supported yet!
703
704
705 //First we need to find out if this is a Dimmer switch,
706 //because they are threaded differently
707 bool bIsDimmer=false;
708 uint8_t LastLevel=0;
709 std::vector<std::vector<std::string> > result;
710 char szDeviceID[20];
711 sprintf(szDeviceID,"%08X",(unsigned int)sID);
712 result = m_sql.safe_query("SELECT SwitchType,LastLevel FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID=='%q') AND (Unit==%d)", m_HwdID, szDeviceID, int(tsen->LIGHTING2.unitcode));
713 if (result.size()>0)
714 {
715 _eSwitchType switchtype=(_eSwitchType)atoi(result[0][0].c_str());
716 if (switchtype==STYPE_Dimmer)
717 bIsDimmer=true;
718 LastLevel=(uint8_t)atoi(result[0][1].c_str());
719 }
720
721 uint8_t iLevel=tsen->LIGHTING2.level;
722 int cmnd=tsen->LIGHTING2.cmnd;
723 int orgcmd=cmnd;
724 if ((tsen->LIGHTING2.level==0)&&(!bIsDimmer))
725 cmnd=light2_sOff;
726 else
727 {
728 if (cmnd==light2_sOn)
729 {
730 iLevel=LastLevel;
731 }
732 else
733 {
734 //scale to 0 - 100 %
735 iLevel=tsen->LIGHTING2.level;
736 if (iLevel>15)
737 iLevel=15;
738 float fLevel=(100.0f/15.0f)*float(iLevel);
739 if (fLevel>99.0f)
740 fLevel=100.0f;
741 iLevel=(uint8_t)(fLevel);
742 }
743 cmnd=light2_sSetLevel;
744 }
745
746 //char buff[512];
747 //sprintf(buff,"cmnd: %d, level: %d, orgcmd: %d",cmnd, iLevel, orgcmd);
748 //_log.Log(LOG_ERROR,buff);
749 unsigned char buf[100];
750 //unsigned char optbuf[100];
751
752 if(!bIsDimmer)
753 {
754 // on/off switch without dimming capability: Profile F6-02-01
755 // cf. EnOcean Equipment Profiles v2.6.5 page 11 (RPS format) & 14
756 unsigned char UpDown = 1;
757
758 buf[0] = RORG_RPS;
759
760 UpDown = ((orgcmd != light2_sOff) && (orgcmd != light2_sGroupOff));
761
762 switch(RockerID)
763 {
764 case 0: // Button A
765 if(UpDown)
766 buf[1] = F60201_BUTTON_A1 << F60201_R1_SHIFT;
767 else
768 buf[1] = F60201_BUTTON_A0 << F60201_R1_SHIFT;
769 break;
770
771 case 1: // Button B
772 if(UpDown)
773 buf[1] = F60201_BUTTON_B1 << F60201_R1_SHIFT;
774 else
775 buf[1] = F60201_BUTTON_B0 << F60201_R1_SHIFT;
776 break;
777
778 default:
779 return false; // not supported
780 }
781
782 buf[1] |= F60201_EB_MASK; // button is pressed
783
784 buf[2]=(sID >> 24) & 0xff; // Sender ID
785 buf[3]=(sID >> 16) & 0xff;
786 buf[4]=(sID >> 8) & 0xff;
787 buf[5]=sID & 0xff;
788
789 buf[6] = S_RPS_T21|S_RPS_NU; // press button // b5=T21, b4=NU, b3-b0= RepeaterCount
790
791 //char buff[512];
792 //sprintf(buff,"%02X %02X %02X %02X %02X %02X %02X",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6]);
793 //_log.Log(LOG_ERROR,buff);
794
795 sendFrameQueue(PACKET_RADIO,buf,7,NULL,0);
796
797 //Next command is send a bit later (button release)
798 buf[1] = 0; // no button press
799 buf[6] = S_RPS_T21; // release button // b5=T21, b4=NU, b3-b0= RepeaterCount
800 //sprintf(buff,"%02X %02X %02X %02X %02X %02X %02X",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6]);
801 //_log.Log(LOG_ERROR,buff);
802
803 sendFrameQueue(PACKET_RADIO,buf,7,NULL,0);
804 }
805 else
806 {
807 // on/off switch with dimming capability: Profile A5-38-02
808 // cf. EnOcean Equipment Profiles v2.6.5 page 12 (4BS format) & 103
809 buf[0]=0xa5;
810 buf[1]=0x2;
811 buf[2]=100; //level
812 buf[3]=1; //speed
813 buf[4]=0x09; // Dim Off
814
815 buf[5]=(sID >> 24) & 0xff;
816 buf[6]=(sID >> 16) & 0xff;
817 buf[7]=(sID >> 8) & 0xff;
818 buf[8]=sID & 0xff;
819
820 buf[9]=0x30; // status
821
822 if (cmnd!=light2_sSetLevel)
823 {
824 //On/Off
825 unsigned char UpDown = 1;
826 UpDown = ((cmnd != light2_sOff) && (cmnd != light2_sGroupOff));
827
828 buf[1] = (RockerID<<DB3_RPS_NU_RID_SHIFT) | (UpDown<<DB3_RPS_NU_UD_SHIFT) | (Pressed<<DB3_RPS_NU_PR_SHIFT);//0x30;
829 buf[9] = 0x30;
830
831 sendFrameQueue(PACKET_RADIO,buf,10,NULL,0);
832
833 //char buff[512];
834 //sprintf(buff,"%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],buf[8],buf[9]);
835 //_log.Log(LOG_ERROR,buff);
836
837 //Next command is send a bit later (button release)
838 buf[1] = 0;
839 buf[9] = 0x20;
840 //sprintf(buff,"%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],buf[8],buf[9]);
841 //_log.Log(LOG_ERROR,buff);
842 sendFrameQueue(PACKET_RADIO,buf,10,NULL,0);
843 }
844 else
845 {
846 //Send dim value
847
848 //Dim On DATA_BYTE0 = 0x09
849 //Dim Off DATA_BYTE0 = 0x08
850
851 buf[1]=2;
852 buf[2]=iLevel;
853 buf[3]=1;//very fast dimming
854
855 if ((iLevel==0)||(orgcmd==light2_sOff))
856 buf[4]=0x08; //Dim Off
857 else
858 buf[4]=0x09;//Dim On
859
860 sendFrameQueue(PACKET_RADIO,buf,10,NULL,0);
861 }
862 }
863
864 return true;
865 }
866
SendDimmerTeachIn(const char * pdata,const unsigned char)867 void CEnOceanESP3::SendDimmerTeachIn(const char *pdata, const unsigned char /*length*/)
868 {
869 if (m_id_base==0)
870 return;
871 if (isOpen()) {
872 RBUF *tsen = (RBUF*)pdata;
873 if (tsen->LIGHTING2.packettype != pTypeLighting2)
874 return; //only allowed to control switches
875 unsigned long sID = (tsen->LIGHTING2.id1 << 24) | (tsen->LIGHTING2.id2 << 16) | (tsen->LIGHTING2.id3 << 8) | tsen->LIGHTING2.id4;
876 if ((sID<m_id_base) || (sID>m_id_base + 129))
877 {
878 _log.Log(LOG_ERROR, "EnOcean (2): Can not switch with this DeviceID, use a switch created with our id_base!...");
879 return;
880 }
881
882 unsigned char buf[100];
883 buf[0] = 0xa5;
884 buf[1] = 0x2;
885 buf[2] = 0;
886 buf[3] = 0;
887 buf[4] = 0x0; // DB0.3=0 -> teach in
888
889 buf[5] = (sID >> 24) & 0xff;
890 buf[6] = (sID >> 16) & 0xff;
891 buf[7] = (sID >> 8) & 0xff;
892 buf[8] = sID & 0xff;
893
894 buf[9] = 0x30; // status
895
896 if (tsen->LIGHTING2.unitcode < 10)
897 {
898 unsigned char RockerID = 0;
899 //unsigned char UpDown = 1;
900 //unsigned char Pressed = 1;
901 RockerID = tsen->LIGHTING2.unitcode - 1;
902 }
903 else
904 {
905 return;//double not supported yet!
906 }
907 sendFrame(PACKET_RADIO,buf,10,NULL,0);
908 }
909 }
910
GetValueRange(const float InValue,const float ScaleMax,const float ScaleMin,const float RangeMax,const float RangeMin)911 float CEnOceanESP3::GetValueRange(const float InValue, const float ScaleMax, const float ScaleMin, const float RangeMax, const float RangeMin)
912 {
913 float vscale=ScaleMax-ScaleMin;
914 if (vscale==0)
915 return 0.0f;
916 float vrange=RangeMax-RangeMin;
917 if (vrange==0)
918 return 0.0f;
919 float multiplyer=vscale/vrange;
920 return multiplyer*(InValue-RangeMin)+ScaleMin;
921 }
922
ParseData()923 bool CEnOceanESP3::ParseData()
924 {
925 #ifdef ENABLE_LOGGING
926 std::stringstream sstr;
927
928 sstr << std::hex << std::uppercase << std::setw(2) << std::setfill('0') << (unsigned int)m_ReceivedPacketType << " (";
929 sstr << std::hex << std::uppercase << std::setw(2) << std::setfill('0') << (unsigned int)m_DataSize << "/";
930 sstr << std::hex << std::uppercase << std::setw(2) << std::setfill('0') << (unsigned int)m_OptionalDataSize << ") ";
931
932 for (int idx=0;idx<m_bufferpos;idx++)
933 {
934 sstr << std::hex << std::uppercase << std::setw(2) << std::setfill('0') << (unsigned int)m_buffer[idx];
935 if (idx!=m_bufferpos-1)
936 sstr << " ";
937 }
938 _log.Log(LOG_STATUS,"EnOcean: %s",sstr.str().c_str());
939 #endif
940
941 if (m_ReceivedPacketType==PACKET_RESPONSE)
942 {
943 //Response
944 unsigned char ResponseCode=m_buffer[0];
945 if (ResponseCode!=0)
946 {
947 std::string szError="Unknown?";
948 switch (ResponseCode)
949 {
950 case RET_ERROR:
951 szError="RET_ERROR";
952 break;
953 case RET_NOT_SUPPORTED:
954 szError="RET_NOT_SUPPORTED";
955 break;
956 case RET_WRONG_PARAM:
957 szError="RET_WRONG_PARAM";
958 break;
959 case RET_OPERATION_DENIED:
960 szError="RET_OPERATION_DENIED";
961 break;
962 }
963 _log.Log(LOG_ERROR,"EnOcean: Response Error (Code: %d, %s)",ResponseCode,szError.c_str());
964 return false;
965 }
966 if ((m_bBaseIDRequested)&&(m_bufferpos==6))
967 {
968 m_bBaseIDRequested=false;
969 m_id_base = (m_buffer[1] << 24) + (m_buffer[2] << 16) + (m_buffer[3] << 8) + m_buffer[4];
970 //unsigned char changes_left=m_buffer[5];
971 _log.Log(LOG_STATUS,"EnOcean: Transceiver ID_Base: 0x%08lx",m_id_base);
972 }
973 if (m_bufferpos==33)
974 {
975 //Version Information
976 _log.Log(LOG_STATUS,"EnOcean: Version_Info, App: %02x.%02x.%02x.%02x, API: %02x.%02x.%02x.%02x, ChipID: %02x.%02x.%02x.%02x, ChipVersion: %02x.%02x.%02x.%02x, Description: %s",
977 m_buffer[1],m_buffer[2],m_buffer[3],m_buffer[4],
978 m_buffer[5],m_buffer[6],m_buffer[7],m_buffer[8],
979 m_buffer[9],m_buffer[10],m_buffer[11],m_buffer[12],
980 m_buffer[13],m_buffer[14],m_buffer[15],m_buffer[16],
981 (const char*)&m_buffer+17
982 );
983 }
984 return true;
985 }
986 else if (m_ReceivedPacketType==PACKET_RADIO)
987 ParseRadioDatagram();
988 else
989 {
990 char szTmp[100];
991 sprintf(szTmp,"Unhandled Packet Type (0x%02x)",m_ReceivedPacketType);
992 _log.Log(LOG_STATUS, "%s", szTmp);
993 }
994 /*
995 enocean_data_structure *pFrame=(enocean_data_structure*)&m_buffer;
996 unsigned char Checksum=enocean_calc_checksum(pFrame);
997 if (Checksum!=pFrame->CHECKSUM)
998 return false; //checksum Mismatch!
999
1000 long id = (pFrame->ID_BYTE3 << 24) + (pFrame->ID_BYTE2 << 16) + (pFrame->ID_BYTE1 << 8) + pFrame->ID_BYTE0;
1001 char szDeviceID[20];
1002 sprintf(szDeviceID,"%08X",(unsigned int)id);
1003
1004 //Handle possible OK/Errors
1005 bool bStopProcessing=false;
1006 if (pFrame->H_SEQ_LENGTH==0x8B)
1007 {
1008 switch (pFrame->ORG)
1009 {
1010 case 0x58:
1011 //OK
1012 #ifdef _DEBUG
1013 _log.Log(LOG_NORM,"EnOcean: OK");
1014 #endif
1015 bStopProcessing=true;
1016 break;
1017 case 0x28:
1018 _log.Log(LOG_ERROR,"EnOcean: ERR_MODEM_NOTWANTEDACK");
1019 bStopProcessing=true;
1020 break;
1021 case 0x29:
1022 _log.Log(LOG_ERROR,"EnOcean: ERR_MODEM_NOTACK");
1023 bStopProcessing=true;
1024 break;
1025 case 0x0C:
1026 _log.Log(LOG_ERROR,"EnOcean: ERR_MODEM_DUP_ID");
1027 bStopProcessing=true;
1028 break;
1029 case 0x08:
1030 _log.Log(LOG_ERROR,"EnOcean: Error in H_SEQ");
1031 bStopProcessing=true;
1032 break;
1033 case 0x09:
1034 _log.Log(LOG_ERROR,"EnOcean: Error in LENGTH");
1035 bStopProcessing=true;
1036 break;
1037 case 0x0A:
1038 _log.Log(LOG_ERROR,"EnOcean: Error in CHECKSUM");
1039 bStopProcessing=true;
1040 break;
1041 case 0x0B:
1042 _log.Log(LOG_ERROR,"EnOcean: Error in ORG");
1043 bStopProcessing=true;
1044 break;
1045 case 0x22:
1046 _log.Log(LOG_ERROR,"EnOcean: ERR_TX_IDRANGE");
1047 bStopProcessing=true;
1048 break;
1049 case 0x1A:
1050 _log.Log(LOG_ERROR,"EnOcean: ERR_ IDRANGE");
1051 bStopProcessing=true;
1052 break;
1053 }
1054 }
1055 if (bStopProcessing)
1056 return true;
1057
1058 switch (pFrame->ORG)
1059 {
1060 case C_ORG_INF_IDBASE:
1061 m_id_base = (pFrame->DATA_BYTE3 << 24) + (pFrame->DATA_BYTE2 << 16) + (pFrame->DATA_BYTE1 << 8) + pFrame->DATA_BYTE0;
1062 _log.Log(LOG_STATUS,"EnOcean: Transceiver ID_Base: 0x%08x",m_id_base);
1063 break;
1064 case C_ORG_RPS:
1065 if (pFrame->STATUS & S_RPS_NU) {
1066 //Rocker
1067 // NU == 1, N-Message
1068 unsigned char RockerID=(pFrame->DATA_BYTE3 & DB3_RPS_NU_RID) >> DB3_RPS_NU_RID_SHIFT;
1069 unsigned char UpDown=(pFrame->DATA_BYTE3 & DB3_RPS_NU_UD) >> DB3_RPS_NU_UD_SHIFT;
1070 unsigned char Pressed=(pFrame->DATA_BYTE3 & DB3_RPS_NU_PR)>>DB3_RPS_NU_PR_SHIFT;
1071 unsigned char SecondRockerID=(pFrame->DATA_BYTE3 & DB3_RPS_NU_SRID)>>DB3_RPS_NU_SRID_SHIFT;
1072 unsigned char SecondUpDown=(pFrame->DATA_BYTE3 & DB3_RPS_NU_SUD)>>DB3_RPS_NU_SUD_SHIFT;
1073 unsigned char SecondAction=(pFrame->DATA_BYTE3 & DB3_RPS_NU_SA)>>DB3_RPS_NU_SA_SHIFT;
1074 #ifdef _DEBUG
1075 _log.Log(LOG_NORM,"Received RPS N-Message Node 0x%08x Rocker ID: %i UD: %i Pressed: %i Second Rocker ID: %i SUD: %i Second Action: %i",
1076 id,
1077 RockerID,
1078 UpDown,
1079 Pressed,
1080 SecondRockerID,
1081 SecondUpDown,
1082 SecondAction);
1083 #endif
1084 //We distinguish 3 types of buttons from a switch: Left/Right/Left+Right
1085 if (Pressed==1)
1086 {
1087 RBUF tsen;
1088 memset(&tsen,0,sizeof(RBUF));
1089 tsen.LIGHTING2.packetlength=sizeof(tsen.LIGHTING2)-1;
1090 tsen.LIGHTING2.packettype=pTypeLighting2;
1091 tsen.LIGHTING2.subtype=sTypeAC;
1092 tsen.LIGHTING2.seqnbr=0;
1093 tsen.LIGHTING2.id1=(BYTE)pFrame->ID_BYTE3;
1094 tsen.LIGHTING2.id2=(BYTE)pFrame->ID_BYTE2;
1095 tsen.LIGHTING2.id3=(BYTE)pFrame->ID_BYTE1;
1096 tsen.LIGHTING2.id4=(BYTE)pFrame->ID_BYTE0;
1097 tsen.LIGHTING2.level=0;
1098 tsen.LIGHTING2.rssi=12;
1099
1100 if (SecondAction==0)
1101 {
1102 //Left/Right Up/Down
1103 tsen.LIGHTING2.unitcode=RockerID+1;
1104 tsen.LIGHTING2.cmnd=(UpDown==1)?light2_sOn:light2_sOff;
1105 }
1106 else
1107 {
1108 //Left+Right Up/Down
1109 tsen.LIGHTING2.unitcode=SecondRockerID+10;
1110 tsen.LIGHTING2.cmnd=(SecondUpDown==1)?light2_sOn:light2_sOff;
1111 }
1112 sDecodeRXMessage(this, (const unsigned char *)&tsen.LIGHTING2, NULL, 255);
1113 }
1114 }
1115 break;
1116 case C_ORG_4BS:
1117 break;
1118 default:
1119 {
1120 char *pszHumenTxt=enocean_hexToHuman(pFrame);
1121 if (pszHumenTxt)
1122 {
1123 _log.Log(LOG_NORM, "EnOcean: %s", pszHumenTxt);
1124 free(pszHumenTxt);
1125 }
1126 }
1127 break;
1128 }
1129 */
1130 return true;
1131 }
1132
ParseRadioDatagram()1133 void CEnOceanESP3::ParseRadioDatagram()
1134 {
1135 char szTmp[100];
1136 if (m_OptionalDataSize == 7)
1137 {
1138 sprintf(szTmp,"destination: 0x%02x%02x%02x%02x RSSI: %i",
1139 m_buffer[m_DataSize+1],m_buffer[m_DataSize+2],m_buffer[m_DataSize+3],m_buffer[m_DataSize+4],
1140 (100-m_buffer[m_DataSize+5])
1141 );
1142 }
1143 else {
1144 sprintf(szTmp, "Optional data size: %i",m_OptionalDataSize);
1145 }
1146 _log.Log(LOG_NORM, "EnOcean: %s", szTmp);
1147 switch (m_buffer[0])
1148 {
1149 case RORG_1BS: // 1 byte communication (Contacts/Switches)
1150 {
1151 sprintf(szTmp,"1BS data: Sender id: 0x%02x%02x%02x%02x Data: %02x",
1152 m_buffer[2],m_buffer[3],m_buffer[4],m_buffer[5],
1153 m_buffer[0]
1154 );
1155
1156 _log.Log(LOG_NORM, "EnOcean: %s", szTmp);
1157
1158 unsigned char DATA_BYTE0 = m_buffer[1];
1159
1160 unsigned char ID_BYTE3 = m_buffer[2];
1161 unsigned char ID_BYTE2 = m_buffer[3];
1162 unsigned char ID_BYTE1 = m_buffer[4];
1163 unsigned char ID_BYTE0 = m_buffer[5];
1164
1165 int UpDown=(DATA_BYTE0&1)==0;
1166
1167 RBUF tsen;
1168 memset(&tsen,0,sizeof(RBUF));
1169 tsen.LIGHTING2.packetlength=sizeof(tsen.LIGHTING2)-1;
1170 tsen.LIGHTING2.packettype=pTypeLighting2;
1171 tsen.LIGHTING2.subtype=sTypeAC;
1172 tsen.LIGHTING2.seqnbr=0;
1173
1174 tsen.LIGHTING2.id1=(BYTE)ID_BYTE3;
1175 tsen.LIGHTING2.id2=(BYTE)ID_BYTE2;
1176 tsen.LIGHTING2.id3=(BYTE)ID_BYTE1;
1177 tsen.LIGHTING2.id4=(BYTE)ID_BYTE0;
1178 tsen.LIGHTING2.level=0;
1179 tsen.LIGHTING2.rssi=12;
1180 tsen.LIGHTING2.unitcode=1;
1181 tsen.LIGHTING2.cmnd=(UpDown==1)?light2_sOn:light2_sOff;
1182 sDecodeRXMessage(this, (const unsigned char *)&tsen.LIGHTING2, NULL, 255);
1183 }
1184 break;
1185 case RORG_4BS: // 4 byte communication
1186 {
1187 sprintf(szTmp,"4BS data: Sender id: 0x%02x%02x%02x%02x Status: %02x Data: %02x",
1188 m_buffer[5],m_buffer[6],m_buffer[7],m_buffer[8],
1189 m_buffer[9],
1190 m_buffer[3]
1191 );
1192 _log.Log(LOG_NORM, "EnOcean: %s", szTmp);
1193
1194 unsigned char DATA_BYTE3 = m_buffer[1];
1195 unsigned char DATA_BYTE2 = m_buffer[2];
1196 unsigned char DATA_BYTE1 = m_buffer[3];
1197 unsigned char DATA_BYTE0 = m_buffer[4];
1198
1199 unsigned char ID_BYTE3 = m_buffer[5];
1200 unsigned char ID_BYTE2 = m_buffer[6];
1201 unsigned char ID_BYTE1 = m_buffer[7];
1202 unsigned char ID_BYTE0 = m_buffer[8];
1203
1204 long id = (ID_BYTE3 << 24) + (ID_BYTE2 << 16) + (ID_BYTE1 << 8) + ID_BYTE0;
1205 char szDeviceID[20];
1206 sprintf(szDeviceID,"%08X",(unsigned int)id);
1207
1208 if ((DATA_BYTE0 & RORG_4BS_TEACHIN_LRN_BIT) == 0) // LRN_BIT is 0 -> Teach-in datagram
1209 {
1210 int manufacturer;
1211 int profile;
1212 int ttype;
1213
1214 // 2016-01-31 Stéphane Guillard : added handling of this case:
1215 if ((DATA_BYTE0 & RORG_4BS_TEACHIN_EEP_BIT) == 0)
1216 {
1217 // RORG_4BS_TEACHIN_EEP_BIT is 0 -> Teach-in Variant 1 : data doesn't contain EEP and Manufacturer ID
1218 // An EEP profile must be manually allocated per sender ID (see EEP 2.6.2 specification §3.3 p173/197)
1219 _log.Log(LOG_NORM, "EnOcean: 4BS, Variant 1 Teach-in diagram: Sender_ID: 0x%08lX", id);
1220 _log.Log(LOG_NORM, "Teach-in data contains no EEP profile. Created generic A5-02-05 profile (0/40°C temp sensor); please adjust by hand using Setup button on EnOcean adapter in Setup/Hardware menu");
1221
1222 manufacturer = 0x7FF; // Generic
1223 profile = 2; // == T4BSTable[4].Func: Temperature Sensor Range 0C to +40C
1224 ttype = 5; // == T4BSTable[4].Type
1225 }
1226 else
1227 {
1228 // RORG_4BS_TEACHIN_EEP_BIT is 1 -> Teach-in Variant 2 : data contains EEP and Manufacturer ID
1229
1230 //DB3 DB3/2 DB2/1 DB0
1231 //Profile Type Manufacturer-ID RORG_4BS_TEACHIN_EEP_BIT RE2 RE1 RORG_4BS_TEACHIN_LRN_BIT
1232 //6 Bit 7 Bit 11 Bit 1Bit 1Bit 1Bit 1Bit 1Bit 1Bit 1Bit 1Bit
1233
1234 // Extract manufacturer, profile and type from data
1235 manufacturer = ((DATA_BYTE2 & 7) << 8) | DATA_BYTE1;
1236 profile = DATA_BYTE3 >> 2;
1237 ttype = ((DATA_BYTE3 & 3) << 5) | (DATA_BYTE2 >> 3);
1238
1239 _log.Log(LOG_NORM,"EnOcean: 4BS, Variant 2 Teach-in diagram: Sender_ID: 0x%08lX\nManufacturer: 0x%02x (%s)\nProfile: 0x%02X\nType: 0x%02X (%s)",
1240 id, manufacturer,Get_EnoceanManufacturer(manufacturer),
1241 profile,ttype,Get_Enocean4BSType(0xA5,profile,ttype));
1242 }
1243
1244 // Search the sensor in database
1245 std::vector<std::vector<std::string> > result;
1246 result = m_sql.safe_query("SELECT ID FROM EnoceanSensors WHERE (HardwareID==%d) AND (DeviceID=='%q')", m_HwdID, szDeviceID);
1247 if (result.size()<1)
1248 {
1249 // If not found, add it to the database
1250 m_sql.safe_query("INSERT INTO EnoceanSensors (HardwareID, DeviceID, Manufacturer, Profile, [Type]) VALUES (%d,'%q',%d,%d,%d)", m_HwdID, szDeviceID, manufacturer, profile, ttype);
1251 _log.Log(LOG_NORM, "EnOcean: Sender_ID 0x%08lX inserted in the database", id);
1252 }
1253 else
1254 _log.Log(LOG_NORM, "EnOcean: Sender_ID 0x%08lX already in the database", id);
1255 ReloadVLDNodes();
1256 }
1257 else // RORG_4BS_TEACHIN_LRN_BIT is 1 -> Data datagram
1258 {
1259 //Following sensors need to have had a teach-in
1260 std::vector<std::vector<std::string> > result;
1261 result = m_sql.safe_query("SELECT ID, Manufacturer, Profile, [Type] FROM EnoceanSensors WHERE (HardwareID==%d) AND (DeviceID=='%q')", m_HwdID, szDeviceID);
1262 if (result.size()<1)
1263 {
1264 _log.Log(LOG_NORM, "EnOcean: Need Teach-In for %s", szDeviceID);
1265 return;
1266 }
1267 int Manufacturer=atoi(result[0][1].c_str());
1268 int Profile=atoi(result[0][2].c_str());
1269 int iType=atoi(result[0][3].c_str());
1270
1271 const std::string szST=Get_Enocean4BSType(0xA5,Profile,iType);
1272
1273 if (szST=="AMR.Counter")
1274 {
1275 //0xA5, 0x12, 0x00, "Counter"
1276 unsigned long cvalue=(DATA_BYTE3<<16)|(DATA_BYTE2<<8)|(DATA_BYTE1);
1277 RBUF tsen;
1278 memset(&tsen,0,sizeof(RBUF));
1279 tsen.RFXMETER.packetlength=sizeof(tsen.RFXMETER)-1;
1280 tsen.RFXMETER.packettype=pTypeRFXMeter;
1281 tsen.RFXMETER.subtype=sTypeRFXMeterCount;
1282 tsen.RFXMETER.rssi=12;
1283 tsen.RFXMETER.id1=ID_BYTE2;
1284 tsen.RFXMETER.id2=ID_BYTE1;
1285 tsen.RFXMETER.count1 = (BYTE)((cvalue & 0xFF000000) >> 24);
1286 tsen.RFXMETER.count2 = (BYTE)((cvalue & 0x00FF0000) >> 16);
1287 tsen.RFXMETER.count3 = (BYTE)((cvalue & 0x0000FF00) >> 8);
1288 tsen.RFXMETER.count4 = (BYTE)(cvalue & 0x000000FF);
1289 sDecodeRXMessage(this, (const unsigned char *)&tsen.RFXMETER, NULL, 255);
1290 }
1291 else if (szST=="AMR.Electricity")
1292 {
1293 //0xA5, 0x12, 0x01, "Electricity"
1294 int cvalue=(DATA_BYTE3<<16)|(DATA_BYTE2<<8)|(DATA_BYTE1);
1295 _tUsageMeter umeter;
1296 umeter.id1=(BYTE)ID_BYTE3;
1297 umeter.id2=(BYTE)ID_BYTE2;
1298 umeter.id3=(BYTE)ID_BYTE1;
1299 umeter.id4=(BYTE)ID_BYTE0;
1300 umeter.dunit=1;
1301 umeter.fusage=(float)cvalue;
1302 sDecodeRXMessage(this, (const unsigned char *)&umeter, NULL, 255);
1303 }
1304 else if (szST=="AMR.Gas")
1305 {
1306 //0xA5, 0x12, 0x02, "Gas"
1307 unsigned long cvalue=(DATA_BYTE3<<16)|(DATA_BYTE2<<8)|(DATA_BYTE1);
1308 RBUF tsen;
1309 memset(&tsen,0,sizeof(RBUF));
1310 tsen.RFXMETER.packetlength=sizeof(tsen.RFXMETER)-1;
1311 tsen.RFXMETER.packettype=pTypeRFXMeter;
1312 tsen.RFXMETER.subtype=sTypeRFXMeterCount;
1313 tsen.RFXMETER.rssi=12;
1314 tsen.RFXMETER.id1=ID_BYTE2;
1315 tsen.RFXMETER.id2=ID_BYTE1;
1316 tsen.RFXMETER.count1 = (BYTE)((cvalue & 0xFF000000) >> 24);
1317 tsen.RFXMETER.count2 = (BYTE)((cvalue & 0x00FF0000) >> 16);
1318 tsen.RFXMETER.count3 = (BYTE)((cvalue & 0x0000FF00) >> 8);
1319 tsen.RFXMETER.count4 = (BYTE)(cvalue & 0x000000FF);
1320 sDecodeRXMessage(this, (const unsigned char *)&tsen.RFXMETER, NULL, 255);
1321 }
1322 else if (szST=="AMR.Water")
1323 {
1324 //0xA5, 0x12, 0x03, "Water"
1325 unsigned long cvalue=(DATA_BYTE3<<16)|(DATA_BYTE2<<8)|(DATA_BYTE1);
1326 RBUF tsen;
1327 memset(&tsen,0,sizeof(RBUF));
1328 tsen.RFXMETER.packetlength=sizeof(tsen.RFXMETER)-1;
1329 tsen.RFXMETER.packettype=pTypeRFXMeter;
1330 tsen.RFXMETER.subtype=sTypeRFXMeterCount;
1331 tsen.RFXMETER.rssi=12;
1332 tsen.RFXMETER.id1=ID_BYTE2;
1333 tsen.RFXMETER.id2=ID_BYTE1;
1334 tsen.RFXMETER.count1 = (BYTE)((cvalue & 0xFF000000) >> 24);
1335 tsen.RFXMETER.count2 = (BYTE)((cvalue & 0x00FF0000) >> 16);
1336 tsen.RFXMETER.count3 = (BYTE)((cvalue & 0x0000FF00) >> 8);
1337 tsen.RFXMETER.count4 = (BYTE)(cvalue & 0x000000FF);
1338 sDecodeRXMessage(this, (const unsigned char *)&tsen.RFXMETER, NULL, 255);
1339 }
1340 else if (szST.find("RoomOperatingPanel") == 0)
1341 {
1342 if (iType<0x0E)
1343 {
1344 // Room Sensor and Control Unit (EEP A5-10-01 ... A5-10-0D)
1345 // [Eltako FTR55D, FTR55H, Thermokon SR04 *, Thanos SR *, untested]
1346 // DATA_BYTE3 is the fan speed or night reduction for Eltako
1347 // DATA_BYTE2 is the setpoint where 0x00 = min ... 0xFF = max or
1348 // reference temperature for Eltako where 0x00 = 0°C ... 0xFF = 40°C
1349 // DATA_BYTE1 is the temperature where 0x00 = +40°C ... 0xFF = 0°C
1350 // DATA_BYTE0_bit_0 is the occupy button, pushbutton or slide switch
1351 float temp=GetValueRange(DATA_BYTE1,0,40);
1352 if (Manufacturer == 0x0D)
1353 {
1354 //Eltako
1355 int nightReduction = 0;
1356 if (DATA_BYTE3 == 0x06)
1357 nightReduction = 1;
1358 else if (DATA_BYTE3 == 0x0C)
1359 nightReduction = 2;
1360 else if (DATA_BYTE3 == 0x13)
1361 nightReduction = 3;
1362 else if (DATA_BYTE3 == 0x19)
1363 nightReduction = 4;
1364 else if (DATA_BYTE3 == 0x1F)
1365 nightReduction = 5;
1366 //float setpointTemp=GetValueRange(DATA_BYTE2,40);
1367 }
1368 else
1369 {
1370 int fspeed = 3;
1371 if (DATA_BYTE3 >= 145)
1372 fspeed = 2;
1373 else if (DATA_BYTE3 >= 165)
1374 fspeed = 1;
1375 else if (DATA_BYTE3 >= 190)
1376 fspeed = 0;
1377 else if (DATA_BYTE3 >= 210)
1378 fspeed = -1; //auto
1379 //int iswitch = DATA_BYTE0 & 1;
1380 }
1381 RBUF tsen;
1382 memset(&tsen,0,sizeof(RBUF));
1383 tsen.TEMP.packetlength=sizeof(tsen.TEMP)-1;
1384 tsen.TEMP.packettype=pTypeTEMP;
1385 tsen.TEMP.subtype=sTypeTEMP10;
1386 tsen.TEMP.id1=ID_BYTE2;
1387 tsen.TEMP.id2=ID_BYTE1;
1388 tsen.TEMP.battery_level=ID_BYTE0&0x0F;
1389 tsen.TEMP.rssi=(ID_BYTE0&0xF0)>>4;
1390
1391 tsen.TEMP.tempsign=(temp>=0)?0:1;
1392 int at10=round(std::abs(temp*10.0f));
1393 tsen.TEMP.temperatureh=(BYTE)(at10/256);
1394 at10-=(tsen.TEMP.temperatureh*256);
1395 tsen.TEMP.temperaturel=(BYTE)(at10);
1396 sDecodeRXMessage(this, (const unsigned char *)&tsen.TEMP, NULL, -1);
1397 }
1398 }
1399 else if (szST == "LightSensor.01")
1400 {
1401 // Light Sensor (EEP A5-06-01)
1402 // [Eltako FAH60, FAH63, FIH63, Thermokon SR65 LI, untested]
1403 // DATA_BYTE3 is the voltage where 0x00 = 0 V ... 0xFF = 5.1 V
1404 // DATA_BYTE3 is the low illuminance for Eltako devices where
1405 // min 0x00 = 0 lx, max 0xFF = 100 lx, if DATA_BYTE2 = 0
1406 // DATA_BYTE2 is the illuminance (ILL2) where min 0x00 = 300 lx, max 0xFF = 30000 lx
1407 // DATA_BYTE1 is the illuminance (ILL1) where min 0x00 = 600 lx, max 0xFF = 60000 lx
1408 // DATA_BYTE0_bit_0 is Range select where 0 = ILL1, 1 = ILL2
1409 float lux =0;
1410 if (Manufacturer == 0x0D)
1411 {
1412 if(DATA_BYTE2 == 0) {
1413 lux=GetValueRange(DATA_BYTE3,100);
1414 } else {
1415 lux=GetValueRange(DATA_BYTE2,30000,300);
1416 }
1417 } else {
1418 float voltage=GetValueRange(DATA_BYTE3,5100); //mV
1419 if(DATA_BYTE0 & 1) {
1420 lux=GetValueRange(DATA_BYTE2,30000,300);
1421 } else {
1422 lux=GetValueRange(DATA_BYTE1,60000,600);
1423 }
1424 RBUF tsen;
1425 memset(&tsen,0,sizeof(RBUF));
1426 tsen.RFXSENSOR.packetlength=sizeof(tsen.RFXSENSOR)-1;
1427 tsen.RFXSENSOR.packettype=pTypeRFXSensor;
1428 tsen.RFXSENSOR.subtype=sTypeRFXSensorVolt;
1429 tsen.RFXSENSOR.id=ID_BYTE1;
1430 tsen.RFXSENSOR.filler=ID_BYTE0&0x0F;
1431 tsen.RFXSENSOR.rssi=(ID_BYTE0&0xF0)>>4;
1432 tsen.RFXSENSOR.msg1 = (BYTE)(voltage/256);
1433 tsen.RFXSENSOR.msg2 = (BYTE)(voltage-(tsen.RFXSENSOR.msg1*256));
1434 sDecodeRXMessage(this, (const unsigned char *)&tsen.RFXSENSOR, NULL, 255);
1435 }
1436 _tLightMeter lmeter;
1437 lmeter.id1=(BYTE)ID_BYTE3;
1438 lmeter.id2=(BYTE)ID_BYTE2;
1439 lmeter.id3=(BYTE)ID_BYTE1;
1440 lmeter.id4=(BYTE)ID_BYTE0;
1441 lmeter.dunit=1;
1442 lmeter.fLux=lux;
1443 sDecodeRXMessage(this, (const unsigned char *)&lmeter, NULL, 255);
1444 }
1445 else if (szST.find("Temperature")==0)
1446 {
1447 //(EPP A5-02 01/30)
1448 float ScaleMax=0;
1449 float ScaleMin=0;
1450 if (iType==0x01) { ScaleMin=-40; ScaleMax=0; }
1451 else if (iType==0x02) { ScaleMin=-30; ScaleMax=10; }
1452 else if (iType==0x03) { ScaleMin=-20; ScaleMax=20; }
1453 else if (iType==0x04) { ScaleMin=-10; ScaleMax=30; }
1454 else if (iType==0x05) { ScaleMin=0; ScaleMax=40; }
1455 else if (iType==0x06) { ScaleMin=10; ScaleMax=50; }
1456 else if (iType==0x07) { ScaleMin=20; ScaleMax=60; }
1457 else if (iType==0x08) { ScaleMin=30; ScaleMax=70; }
1458 else if (iType==0x09) { ScaleMin=40; ScaleMax=80; }
1459 else if (iType==0x0A) { ScaleMin=50; ScaleMax=90; }
1460 else if (iType==0x0B) { ScaleMin=60; ScaleMax=100; }
1461 else if (iType==0x10) { ScaleMin=-60; ScaleMax=20; }
1462 else if (iType==0x11) { ScaleMin=-50; ScaleMax=30; }
1463 else if (iType==0x12) { ScaleMin=-40; ScaleMax=40; }
1464 else if (iType==0x13) { ScaleMin=-30; ScaleMax=50; }
1465 else if (iType==0x14) { ScaleMin=-20; ScaleMax=60; }
1466 else if (iType==0x15) { ScaleMin=-10; ScaleMax=70; }
1467 else if (iType==0x16) { ScaleMin=0; ScaleMax=80; }
1468 else if (iType==0x17) { ScaleMin=10; ScaleMax=90; }
1469 else if (iType==0x18) { ScaleMin=20; ScaleMax=100; }
1470 else if (iType==0x19) { ScaleMin=30; ScaleMax=110; }
1471 else if (iType==0x1A) { ScaleMin=40; ScaleMax=120; }
1472 else if (iType==0x1B) { ScaleMin=50; ScaleMax=130; }
1473 else if (iType==0x20) { ScaleMin=-10; ScaleMax=41.2f; }
1474 else if (iType==0x30) { ScaleMin=-40; ScaleMax=62.3f; }
1475
1476 float temp;
1477 if (iType<0x20)
1478 temp=GetValueRange(DATA_BYTE1,ScaleMax,ScaleMin,0,255);
1479 else
1480 temp=GetValueRange(float(((DATA_BYTE2&3)<<8)|DATA_BYTE1),ScaleMax,ScaleMin); //10bit
1481 RBUF tsen;
1482 memset(&tsen,0,sizeof(RBUF));
1483 tsen.TEMP.packetlength=sizeof(tsen.TEMP)-1;
1484 tsen.TEMP.packettype=pTypeTEMP;
1485 tsen.TEMP.subtype=sTypeTEMP10;
1486 tsen.TEMP.id1=ID_BYTE2;
1487 tsen.TEMP.id2=ID_BYTE1;
1488 tsen.TEMP.battery_level=ID_BYTE0&0x0F;
1489 tsen.TEMP.rssi=(ID_BYTE0&0xF0)>>4;
1490
1491 tsen.TEMP.tempsign=(temp>=0)?0:1;
1492 int at10=round(std::abs(temp*10.0f));
1493 tsen.TEMP.temperatureh=(BYTE)(at10/256);
1494 at10-=(tsen.TEMP.temperatureh*256);
1495 tsen.TEMP.temperaturel=(BYTE)(at10);
1496 sDecodeRXMessage(this, (const unsigned char *)&tsen.TEMP, NULL, -1);
1497 }
1498 else if (szST.find("TempHum")==0)
1499 {
1500 //(EPP A5-04 01/02)
1501 float ScaleMax = 0;
1502 float ScaleMin = 0;
1503 if (iType == 0x01) { ScaleMin = 0; ScaleMax = 40; }
1504 else if (iType == 0x02) { ScaleMin = -20; ScaleMax = 60; }
1505 else if (iType == 0x03) { ScaleMin = -20; ScaleMax = 60; } //10bit?
1506
1507 float temp = GetValueRange(DATA_BYTE1, ScaleMax, ScaleMin,250,0);
1508 float hum = GetValueRange(DATA_BYTE2, 100);
1509 RBUF tsen;
1510 memset(&tsen,0,sizeof(RBUF));
1511 tsen.TEMP_HUM.packetlength=sizeof(tsen.TEMP_HUM)-1;
1512 tsen.TEMP_HUM.packettype=pTypeTEMP_HUM;
1513 tsen.TEMP_HUM.subtype=sTypeTH5;
1514 tsen.TEMP_HUM.rssi=12;
1515 tsen.TEMP_HUM.id1=ID_BYTE2;
1516 tsen.TEMP_HUM.id2=ID_BYTE1;
1517 tsen.TEMP_HUM.battery_level=9;
1518 tsen.TEMP_HUM.tempsign=(temp>=0)?0:1;
1519 int at10=round(std::abs(temp*10.0f));
1520 tsen.TEMP_HUM.temperatureh=(BYTE)(at10/256);
1521 at10-=(tsen.TEMP_HUM.temperatureh*256);
1522 tsen.TEMP_HUM.temperaturel=(BYTE)(at10);
1523 tsen.TEMP_HUM.humidity=(BYTE)hum;
1524 tsen.TEMP_HUM.humidity_status=Get_Humidity_Level(tsen.TEMP_HUM.humidity);
1525 sDecodeRXMessage(this, (const unsigned char *)&tsen.TEMP_HUM, NULL, -1);
1526 }
1527 else if (szST == "OccupancySensor.01")
1528 {
1529 //(EPP A5-07-01)
1530 if (DATA_BYTE3 < 251)
1531 {
1532 RBUF tsen;
1533
1534 if (DATA_BYTE0 & 1)
1535 {
1536 //Voltage supported
1537 float voltage = GetValueRange(DATA_BYTE3, 5.0f, 0, 250, 0);
1538 memset(&tsen, 0, sizeof(RBUF));
1539 tsen.RFXSENSOR.packetlength = sizeof(tsen.RFXSENSOR) - 1;
1540 tsen.RFXSENSOR.packettype = pTypeRFXSensor;
1541 tsen.RFXSENSOR.subtype = sTypeRFXSensorVolt;
1542 tsen.RFXSENSOR.id = ID_BYTE1;
1543 tsen.RFXSENSOR.filler = ID_BYTE0 & 0x0F;
1544 tsen.RFXSENSOR.rssi = (ID_BYTE0 & 0xF0) >> 4;
1545 tsen.RFXSENSOR.msg1 = (BYTE)(voltage / 256);
1546 tsen.RFXSENSOR.msg2 = (BYTE)(voltage - (tsen.RFXSENSOR.msg1 * 256));
1547 sDecodeRXMessage(this, (const unsigned char *)&tsen.RFXSENSOR, NULL, 255);
1548 }
1549
1550 bool bPIROn = (DATA_BYTE1 > 127);
1551 memset(&tsen, 0, sizeof(RBUF));
1552 tsen.LIGHTING2.packetlength = sizeof(tsen.LIGHTING2) - 1;
1553 tsen.LIGHTING2.packettype = pTypeLighting2;
1554 tsen.LIGHTING2.subtype = sTypeAC;
1555 tsen.LIGHTING2.seqnbr = 0;
1556
1557 tsen.LIGHTING2.id1 = (BYTE)ID_BYTE3;
1558 tsen.LIGHTING2.id2 = (BYTE)ID_BYTE2;
1559 tsen.LIGHTING2.id3 = (BYTE)ID_BYTE1;
1560 tsen.LIGHTING2.id4 = (BYTE)ID_BYTE0;
1561 tsen.LIGHTING2.level = 0;
1562 tsen.LIGHTING2.rssi = 12;
1563 tsen.LIGHTING2.unitcode = 1;
1564 tsen.LIGHTING2.cmnd = (bPIROn) ? light2_sOn : light2_sOff;
1565 sDecodeRXMessage(this, (const unsigned char *)&tsen.LIGHTING2, NULL, 255);
1566 }
1567 else {
1568 //Error code
1569 }
1570 }
1571 else if (szST == "OccupancySensor.02")
1572 {
1573 //(EPP A5-07-02)
1574 if (DATA_BYTE3 < 251)
1575 {
1576 RBUF tsen;
1577
1578 float voltage = GetValueRange(DATA_BYTE3, 5.0f, 0, 250, 0);
1579 memset(&tsen, 0, sizeof(RBUF));
1580 tsen.RFXSENSOR.packetlength = sizeof(tsen.RFXSENSOR) - 1;
1581 tsen.RFXSENSOR.packettype = pTypeRFXSensor;
1582 tsen.RFXSENSOR.subtype = sTypeRFXSensorVolt;
1583 tsen.RFXSENSOR.id = ID_BYTE1;
1584 tsen.RFXSENSOR.filler = ID_BYTE0 & 0x0F;
1585 tsen.RFXSENSOR.rssi = (ID_BYTE0 & 0xF0) >> 4;
1586 tsen.RFXSENSOR.msg1 = (BYTE)(voltage / 256);
1587 tsen.RFXSENSOR.msg2 = (BYTE)(voltage - (tsen.RFXSENSOR.msg1 * 256));
1588 sDecodeRXMessage(this, (const unsigned char *)&tsen.RFXSENSOR, NULL, 255);
1589
1590 bool bPIROn = (DATA_BYTE0 & 0x80)!=0;
1591 memset(&tsen, 0, sizeof(RBUF));
1592 tsen.LIGHTING2.packetlength = sizeof(tsen.LIGHTING2) - 1;
1593 tsen.LIGHTING2.packettype = pTypeLighting2;
1594 tsen.LIGHTING2.subtype = sTypeAC;
1595 tsen.LIGHTING2.seqnbr = 0;
1596
1597 tsen.LIGHTING2.id1 = (BYTE)ID_BYTE3;
1598 tsen.LIGHTING2.id2 = (BYTE)ID_BYTE2;
1599 tsen.LIGHTING2.id3 = (BYTE)ID_BYTE1;
1600 tsen.LIGHTING2.id4 = (BYTE)ID_BYTE0;
1601 tsen.LIGHTING2.level = 0;
1602 tsen.LIGHTING2.rssi = 12;
1603 tsen.LIGHTING2.unitcode = 1;
1604 tsen.LIGHTING2.cmnd = (bPIROn) ? light2_sOn : light2_sOff;
1605 sDecodeRXMessage(this, (const unsigned char *)&tsen.LIGHTING2, NULL, 255);
1606 }
1607 else {
1608 //Error code
1609 }
1610 }
1611 else if (szST == "OccupancySensor.03")
1612 {
1613 //(EPP A5-07-03)
1614 if (DATA_BYTE3 < 251)
1615 {
1616 RBUF tsen;
1617
1618 float voltage = GetValueRange(DATA_BYTE3, 5.0f, 0, 250, 0);
1619 memset(&tsen, 0, sizeof(RBUF));
1620 tsen.RFXSENSOR.packetlength = sizeof(tsen.RFXSENSOR) - 1;
1621 tsen.RFXSENSOR.packettype = pTypeRFXSensor;
1622 tsen.RFXSENSOR.subtype = sTypeRFXSensorVolt;
1623 tsen.RFXSENSOR.id = ID_BYTE1;
1624 tsen.RFXSENSOR.filler = ID_BYTE0 & 0x0F;
1625 tsen.RFXSENSOR.rssi = (ID_BYTE0 & 0xF0) >> 4;
1626 tsen.RFXSENSOR.msg1 = (BYTE)(voltage / 256);
1627 tsen.RFXSENSOR.msg2 = (BYTE)(voltage - (tsen.RFXSENSOR.msg1 * 256));
1628 sDecodeRXMessage(this, (const unsigned char *)&tsen.RFXSENSOR, NULL, 255);
1629
1630 int lux = (DATA_BYTE2 << 2) | (DATA_BYTE1>>6);
1631 if (lux > 1000)
1632 lux = 1000;
1633 _tLightMeter lmeter;
1634 lmeter.id1 = (BYTE)ID_BYTE3;
1635 lmeter.id2 = (BYTE)ID_BYTE2;
1636 lmeter.id3 = (BYTE)ID_BYTE1;
1637 lmeter.id4 = (BYTE)ID_BYTE0;
1638 lmeter.dunit = 1;
1639 lmeter.fLux = (float)lux;
1640 sDecodeRXMessage(this, (const unsigned char *)&lmeter, NULL, 255);
1641
1642 bool bPIROn = (DATA_BYTE0 & 0x80)!=0;
1643 memset(&tsen, 0, sizeof(RBUF));
1644 tsen.LIGHTING2.packetlength = sizeof(tsen.LIGHTING2) - 1;
1645 tsen.LIGHTING2.packettype = pTypeLighting2;
1646 tsen.LIGHTING2.subtype = sTypeAC;
1647 tsen.LIGHTING2.seqnbr = 0;
1648
1649 tsen.LIGHTING2.id1 = (BYTE)ID_BYTE3;
1650 tsen.LIGHTING2.id2 = (BYTE)ID_BYTE2;
1651 tsen.LIGHTING2.id3 = (BYTE)ID_BYTE1;
1652 tsen.LIGHTING2.id4 = (BYTE)ID_BYTE0;
1653 tsen.LIGHTING2.level = 0;
1654 tsen.LIGHTING2.rssi = 12;
1655 tsen.LIGHTING2.unitcode = 1;
1656 tsen.LIGHTING2.cmnd = (bPIROn) ? light2_sOn : light2_sOff;
1657 sDecodeRXMessage(this, (const unsigned char *)&tsen.LIGHTING2, NULL, 255);
1658 }
1659 else {
1660 //Error code
1661 }
1662 }
1663 else if (szST.find("GasSensor.04")==0)
1664 {
1665 //(EPP A5-09-04 CO2 Gas Sensor with Temp and Humidity)
1666 // DB3 = Humidity in 0.5% steps, 0...200 -> 0...100% RH (0x51 = 40%)
1667 // DB2 = CO2 concentration in 10 ppm steps, 0...255 -> 0...2550 ppm (0x39 = 570 ppm)
1668 // DB1 = Temperature in 0.2C steps, 0...255 -> 0...51 C (0x7B = 24.6 C)
1669 // DB0 = flags (DB0.3: 1=data, 0=teach-in; DB0.2: 1=Hum Sensor available, 0=no Hum; DB0.1: 1=Temp sensor available, 0=No Temp; DB0.0 not used)
1670 // mBuffer[15] is RSSI as -dBm (ie value of 100 means "-100 dBm"), but RssiLevel is in the range 0...15 (or reported as 12 if not known)
1671 // Battery level is not reported by device, so use fixed value of 9 as per other sensor functions
1672
1673 // TODO: Check sensor availability flags and only report humidity and/or temp if available.
1674 // TODO: Report actual RSSI (scaled from dBm to 0...15 RSSI ?)
1675
1676 float temp = GetValueRange(DATA_BYTE1, 51, 0, 255, 0);
1677 float hum = GetValueRange(DATA_BYTE3, 100, 0, 200, 0);
1678 int co2 = (int)GetValueRange(DATA_BYTE2, 2550, 0, 255, 0);
1679 int NodeID = (ID_BYTE2 << 8) + ID_BYTE1;
1680
1681 // Report battery level as 9 and RSSI as 12
1682 SendTempHumSensor(NodeID, 9, temp, round(hum), "GasSensor.04", 12);
1683 SendAirQualitySensor((NodeID & 0xFF00) >> 8, NodeID & 0xFF, 9, co2, "GasSensor.04");
1684 }
1685 }
1686 }
1687 break;
1688 case RORG_RPS: // repeated switch communication
1689 {
1690 #ifdef ENOCEAN_BUTTON_DEBUG
1691 sprintf(szTmp, "RPS data: Sender id: 0x%02x%02x%02x%02x Status: %02x Data: %02x",
1692 m_buffer[2],m_buffer[3],m_buffer[4],m_buffer[5],
1693 m_buffer[6],
1694 m_buffer[1]
1695 );
1696 _log.Log(LOG_NORM, "EnOcean: %s", szTmp);
1697 if (m_buffer[6] & (1 << 2))
1698 {
1699 _log.Log(LOG_NORM, "EnOcean: T21");
1700 }
1701 #endif // ENOCEAN_BUTTON_DEBUG
1702
1703 unsigned char STATUS=m_buffer[6];
1704
1705 unsigned char T21 = (m_buffer[6] & S_RPS_T21) >> S_RPS_T21_SHIFT;
1706 unsigned char NU = (m_buffer[6] & S_RPS_NU) >> S_RPS_NU_SHIFT;
1707
1708 unsigned char ID_BYTE3=m_buffer[2];
1709 unsigned char ID_BYTE2=m_buffer[3];
1710 unsigned char ID_BYTE1=m_buffer[4];
1711 unsigned char ID_BYTE0=m_buffer[5];
1712 long id = (ID_BYTE3 << 24) + (ID_BYTE2 << 16) + (ID_BYTE1 << 8) + ID_BYTE0;
1713 char szDeviceID[20];
1714 sprintf(szDeviceID,"%08X",(unsigned int)id);
1715
1716 // if a button is attached to a module, we should ignore it else its datagram will conflict with status reported by the module using VLD datagram
1717 std::vector<std::vector<std::string> > result;
1718 result = m_sql.safe_query("SELECT ID, Profile, [Type] FROM EnoceanSensors WHERE (HardwareID==%d) AND (DeviceID=='%q')", m_HwdID, szDeviceID);
1719 if (result.size() == 1)
1720 {
1721 // hardware device was already teached-in
1722 int Profile=atoi(result[0][1].c_str());
1723 int iType=atoi(result[0][2].c_str());
1724 if( (Profile == 0x01) && // profile 1 (D2-01) is Electronic switches and dimmers with Energy Measurement and Local Control
1725 ((iType == 0x0F) || (iType == 0x12)) // type 0F and 12 have external switch/push button control, it means they also act as rocker
1726 )
1727 {
1728 #ifdef ENOCEAN_BUTTON_DEBUG
1729 _log.Log(LOG_STATUS,"EnOcean: %s, ignore button press", szDeviceID);
1730 #endif // ENOCEAN_BUTTON_DEBUG
1731 break;
1732 }
1733 }
1734
1735 // Whether we use the ButtonID reporting with ON/OFF
1736 bool useButtonIDs = true;
1737
1738 if (STATUS & S_RPS_NU)
1739 {
1740 //Rocker
1741
1742 unsigned char DATA_BYTE3=m_buffer[1];
1743
1744 // NU == 1, N-Message
1745 unsigned char ButtonID = (DATA_BYTE3 & DB3_RPS_NU_BID) >> DB3_RPS_NU_BID_SHIFT;
1746 unsigned char RockerID = (DATA_BYTE3 & DB3_RPS_NU_RID) >> DB3_RPS_NU_RID_SHIFT;
1747 unsigned char UpDown=(DATA_BYTE3 & DB3_RPS_NU_UD) >> DB3_RPS_NU_UD_SHIFT;
1748 unsigned char Pressed=(DATA_BYTE3 & DB3_RPS_NU_PR) >> DB3_RPS_NU_PR_SHIFT;
1749
1750 unsigned char SecondButtonID = (DATA_BYTE3 & DB3_RPS_NU_SBID) >> DB3_RPS_NU_SBID_SHIFT;
1751 unsigned char SecondRockerID = (DATA_BYTE3 & DB3_RPS_NU_SRID) >> DB3_RPS_NU_SRID_SHIFT;
1752 unsigned char SecondUpDown=(DATA_BYTE3 & DB3_RPS_NU_SUD)>>DB3_RPS_NU_SUD_SHIFT;
1753 unsigned char SecondAction=(DATA_BYTE3 & DB3_RPS_NU_SA)>>DB3_RPS_NU_SA_SHIFT;
1754
1755 #ifdef ENOCEAN_BUTTON_DEBUG
1756 _log.Log(LOG_NORM,
1757 "EnOcean: Received RPS N-Message message: 0x%02X Node 0x%08x RockerID: %i ButtonID: %i Pressed: %i UD: %i Second Rocker ID: %i SecondButtonID: %i SUD: %i Second Action: %i",
1758 DATA_BYTE3,
1759 id,
1760 RockerID,
1761 ButtonID,
1762 UpDown,
1763 Pressed,
1764 SecondRockerID,
1765 SecondButtonID,
1766 SecondUpDown,
1767 SecondAction);
1768 #endif // ENOCEAN_BUTTON_DEBUG
1769
1770 //We distinguish 3 types of buttons from a switch: Left/Right/Left+Right
1771 if (Pressed==1)
1772 {
1773 RBUF tsen;
1774 memset(&tsen,0,sizeof(RBUF));
1775 tsen.LIGHTING2.packetlength=sizeof(tsen.LIGHTING2)-1;
1776 tsen.LIGHTING2.packettype=pTypeLighting2;
1777 tsen.LIGHTING2.subtype=sTypeAC;
1778 tsen.LIGHTING2.seqnbr=0;
1779
1780 tsen.LIGHTING2.id1=(BYTE)ID_BYTE3;
1781 tsen.LIGHTING2.id2=(BYTE)ID_BYTE2;
1782 tsen.LIGHTING2.id3=(BYTE)ID_BYTE1;
1783 tsen.LIGHTING2.id4=(BYTE)ID_BYTE0;
1784 tsen.LIGHTING2.level=0;
1785 tsen.LIGHTING2.rssi=12;
1786
1787 if (SecondAction==0)
1788 {
1789 if (useButtonIDs)
1790 {
1791 //Left/Right Pressed
1792 tsen.LIGHTING2.unitcode = ButtonID + 1;
1793 tsen.LIGHTING2.cmnd = light2_sOn; // the button is pressed, so we don't get an OFF message here
1794 }
1795 else
1796 {
1797 //Left/Right Up/Down
1798 tsen.LIGHTING2.unitcode = RockerID + 1;
1799 tsen.LIGHTING2.cmnd = (UpDown == 1) ? light2_sOn : light2_sOff;
1800 }
1801 }
1802 else
1803 {
1804 if (useButtonIDs)
1805 {
1806 //Left+Right Pressed
1807 tsen.LIGHTING2.unitcode = ButtonID + 10;
1808 tsen.LIGHTING2.cmnd = light2_sOn; // the button is pressed, so we don't get an OFF message here
1809 }
1810 else
1811 {
1812 //Left+Right Up/Down
1813 tsen.LIGHTING2.unitcode = SecondRockerID + 10;
1814 tsen.LIGHTING2.cmnd = (SecondUpDown == 1) ? light2_sOn : light2_sOff;
1815 }
1816 }
1817
1818 #ifdef ENOCEAN_BUTTON_DEBUG
1819 _log.Log(LOG_NORM, "EnOcean message: 0x%02X Node 0x%08x UnitID: %02X cmd: %02X ",
1820 DATA_BYTE3,
1821 id,
1822 tsen.LIGHTING2.unitcode,
1823 tsen.LIGHTING2.cmnd
1824 );
1825 #endif //ENOCEAN_BUTTON_DEBUG
1826
1827 sDecodeRXMessage(this, (const unsigned char *)&tsen.LIGHTING2, NULL, 255);
1828 }
1829 }
1830 else
1831 {
1832 if ((T21 == 1) && (NU == 0))
1833 {
1834 unsigned char DATA_BYTE3 = m_buffer[1];
1835
1836 unsigned char ButtonID = (DATA_BYTE3 & DB3_RPS_BUTTONS) >> DB3_RPS_BUTTONS_SHIFT;
1837 unsigned char Pressed = (DATA_BYTE3 & DB3_RPS_PR) >> DB3_RPS_PR_SHIFT;
1838
1839 unsigned char UpDown = !((DATA_BYTE3 == 0xD0) || (DATA_BYTE3 == 0xF0));
1840
1841 #ifdef ENOCEAN_BUTTON_DEBUG
1842 _log.Log(LOG_NORM, "EnOcean: Received RPS T21-Message message: 0x%02X Node 0x%08x ButtonID: %i Pressed: %i UD: %i",
1843 DATA_BYTE3,
1844 id,
1845 ButtonID,
1846 Pressed,
1847 UpDown);
1848 #endif //ENOCEAN_BUTTON_DEBUG
1849
1850 RBUF tsen;
1851 memset(&tsen, 0, sizeof(RBUF));
1852 tsen.LIGHTING2.packetlength = sizeof(tsen.LIGHTING2) - 1;
1853 tsen.LIGHTING2.packettype = pTypeLighting2;
1854 tsen.LIGHTING2.subtype = sTypeAC;
1855 tsen.LIGHTING2.seqnbr = 0;
1856
1857 tsen.LIGHTING2.id1 = (BYTE)ID_BYTE3;
1858 tsen.LIGHTING2.id2 = (BYTE)ID_BYTE2;
1859 tsen.LIGHTING2.id3 = (BYTE)ID_BYTE1;
1860 tsen.LIGHTING2.id4 = (BYTE)ID_BYTE0;
1861 tsen.LIGHTING2.level = 0;
1862 tsen.LIGHTING2.rssi = 12;
1863
1864 if (useButtonIDs)
1865 {
1866 // It's the release message of any button pressed before
1867 tsen.LIGHTING2.unitcode = 0; // does not matter, since we are using a group command
1868 tsen.LIGHTING2.cmnd = (Pressed == 1) ? light2_sGroupOn : light2_sGroupOff;
1869 }
1870 else
1871 {
1872 tsen.LIGHTING2.unitcode = 1;
1873 tsen.LIGHTING2.cmnd = (UpDown == 1) ? light2_sOn : light2_sOff;
1874 }
1875 #ifdef ENOCEAN_BUTTON_DEBUG
1876
1877 _log.Log(LOG_NORM, "EnOcean message: 0x%02X Node 0x%08x UnitID: %02X cmd: %02X ",
1878 DATA_BYTE3,
1879 id,
1880 tsen.LIGHTING2.unitcode,
1881 tsen.LIGHTING2.cmnd);
1882
1883 #endif // ENOCEAN_BUTTON_DEBUG
1884
1885 sDecodeRXMessage(this, (const unsigned char *)&tsen.LIGHTING2, NULL, 255);
1886 }
1887 }
1888 }
1889 break;
1890
1891 case RORG_UTI:
1892 // Universal teach-in (0xD4)
1893 {
1894 unsigned char uni_bi_directional_communication = (m_buffer[1] >> 7) & 1; // 0=mono, 1= bi
1895 unsigned char eep_teach_in_response_expected = (m_buffer[1] >> 6) & 1; // 0=yes, 1=no
1896 unsigned char teach_in_request = (m_buffer[1] >> 4) & 3; // 0= request, 1= deletion request, 2=request or deletion request, 3=not used
1897 unsigned char cmd = m_buffer[1] & 0x0F;
1898
1899 if(cmd == 0x0)
1900 {
1901 // EEP Teach-In Query (UTE Message / CMD 0x0)
1902
1903 unsigned char nb_channel = m_buffer[2];
1904 unsigned int manID = ((unsigned int)(m_buffer[4] & 0x7)) << 8 | (m_buffer[3]);
1905 unsigned char type = m_buffer[5];
1906 unsigned char func = m_buffer[6];
1907 unsigned char rorg = m_buffer[7];
1908
1909 unsigned char ID_BYTE3=m_buffer[8];
1910 unsigned char ID_BYTE2=m_buffer[9];
1911 unsigned char ID_BYTE1=m_buffer[10];
1912 unsigned char ID_BYTE0=m_buffer[11];
1913 long id = (ID_BYTE3 << 24) + (ID_BYTE2 << 16) + (ID_BYTE1 << 8) + ID_BYTE0;
1914
1915 _log.Log(LOG_NORM, "EnOcean: teach-in request received from %08lX (manufacturer: %03X). number of channels: %d, device profile: %02X-%02X-%02X", id, manID, nb_channel, rorg,func,type);
1916
1917 // Record EnOcean device profile
1918 {
1919 char szDeviceID[20];
1920 std::vector<std::vector<std::string> > result;
1921 sprintf(szDeviceID,"%08X",(unsigned int)id);
1922 result = m_sql.safe_query("SELECT ID FROM EnoceanSensors WHERE (HardwareID==%d) AND (DeviceID=='%q')", m_HwdID, szDeviceID);
1923 if (result.size()<1)
1924 {
1925 // If not found, add it to the database
1926 m_sql.safe_query("INSERT INTO EnoceanSensors (HardwareID, DeviceID, Manufacturer, Profile, [Type]) VALUES (%d,'%q',%d,%d,%d)", m_HwdID, szDeviceID, manID, func, type);
1927 _log.Log(LOG_NORM, "EnOcean: Sender_ID 0x%08lX inserted in the database", id);
1928 }
1929 else
1930 _log.Log(LOG_NORM, "EnOcean: Sender_ID 0x%08lX already in the database", id);
1931 ReloadVLDNodes();
1932 }
1933
1934 if((rorg == 0xD2) && (func == 0x01) && ( (type == 0x12) || (type == 0x0F) ))
1935 {
1936 unsigned char nbc;
1937
1938 for(nbc = 0; nbc < nb_channel; nbc ++)
1939 {
1940 RBUF tsen;
1941
1942 memset(&tsen,0,sizeof(RBUF));
1943 tsen.LIGHTING2.packetlength=sizeof(tsen.LIGHTING2)-1;
1944 tsen.LIGHTING2.packettype=pTypeLighting2;
1945 tsen.LIGHTING2.subtype=sTypeAC;
1946 tsen.LIGHTING2.seqnbr=0;
1947
1948 tsen.LIGHTING2.id1=(BYTE)ID_BYTE3;
1949 tsen.LIGHTING2.id2=(BYTE)ID_BYTE2;
1950 tsen.LIGHTING2.id3=(BYTE)ID_BYTE1;
1951 tsen.LIGHTING2.id4=(BYTE)ID_BYTE0;
1952 tsen.LIGHTING2.level=0;
1953 tsen.LIGHTING2.rssi=12;
1954
1955 tsen.LIGHTING2.unitcode = nbc + 1;
1956 tsen.LIGHTING2.cmnd = light2_sOff;
1957
1958 #ifdef ENOCEAN_BUTTON_DEBUG
1959 _log.Log(LOG_NORM, "EnOcean message: 0xD4 Node 0x%08x UnitID: %02X cmd: %02X ",
1960 id,
1961 tsen.LIGHTING2.unitcode,
1962 tsen.LIGHTING2.cmnd
1963 );
1964 #endif //ENOCEAN_BUTTON_DEBUG
1965
1966 _log.Log(LOG_NORM, "EnOcean: channel = %d", nbc+1);
1967 sDecodeRXMessage(this, (const unsigned char *)&tsen.LIGHTING2, NULL, 255);
1968 }
1969 return;
1970 }
1971 break;
1972 }
1973
1974 _log.Log(LOG_NORM, "EnOcean: Unhandled RORG (%02x), uni_bi (%02x [1=bidir]), response_expected (%02x [0=yes]), request (%02x), cmd (%02x)", m_buffer[0], uni_bi_directional_communication,eep_teach_in_response_expected, teach_in_request, cmd);
1975 }
1976 break;
1977
1978 case RORG_VLD:
1979 {
1980 unsigned char DATA_BYTE3=m_buffer[1];
1981 unsigned char func = (m_buffer[1] >> 2) & 0x3F;
1982 unsigned char type = ((m_buffer[2] >> 3) & 0x1F) | ((m_buffer[1] & 0x03) << 5);
1983
1984 if (m_DataSize > 7)
1985 {
1986 unsigned char ID_BYTE3 = m_buffer[m_DataSize - 5];
1987 unsigned char ID_BYTE2 = m_buffer[m_DataSize - 4];
1988 unsigned char ID_BYTE1 = m_buffer[m_DataSize - 3];
1989 unsigned char ID_BYTE0 = m_buffer[m_DataSize - 2];
1990 unsigned long id = (ID_BYTE3 << 24) + (ID_BYTE2 << 16) + (ID_BYTE1 << 8) + ID_BYTE0;
1991
1992 auto itt = m_VLDNodes.find(id);
1993 if (itt != m_VLDNodes.end())
1994 {
1995 uint8_t Profile = itt->second.profile;
1996 uint8_t iType = itt->second.type;
1997
1998 // D2-03-0A Push Button – Single Button
1999 _log.Log(LOG_NORM, "EnOcean message VLD: Profile: %02X Type: %02X", Profile, iType);
2000
2001 switch (Profile)
2002 {
2003 case 0x03:
2004 //Light, Switching + Blind Control
2005 if (iType == 0x0A)
2006 {
2007 int battery = (int)double((255.0 / 100.0)*m_buffer[1]);
2008 unsigned char DATA_BYTE0 = m_buffer[2]; //1 = simple press, 2=double press, 3=long press, 4=press release
2009 SendGeneralSwitch(id, DATA_BYTE0, battery, 1, 0, "Switch", 12);
2010 return;
2011 }
2012 break;
2013 }
2014 }
2015 }
2016 _log.Log(LOG_NORM, "EnOcean message VLD: func: %02X Type: %02X", func, type);
2017 if (func == 0x01)
2018 {
2019 // D2-01 Electr. switches/dimmers, Energy Meas. / Local Ctrl
2020 switch (type)
2021 {
2022 case 0x0C: // D2-01-0C
2023 {
2024 unsigned char channel = m_buffer[2] & 0x7;
2025
2026 unsigned char dim_power = m_buffer[3] & 0x7F; // 0=off, 0x64=100%
2027
2028 unsigned char ID_BYTE3 = m_buffer[4];
2029 unsigned char ID_BYTE2 = m_buffer[5];
2030 unsigned char ID_BYTE1 = m_buffer[6];
2031 unsigned char ID_BYTE0 = m_buffer[7];
2032 long id = (ID_BYTE3 << 24) + (ID_BYTE2 << 16) + (ID_BYTE1 << 8) + ID_BYTE0;
2033
2034 // report status only if it is a known device else we may have an incorrect profile
2035 char szDeviceID[20];
2036 std::vector<std::vector<std::string> > result;
2037 sprintf(szDeviceID, "%08X", (unsigned int)id);
2038
2039 result = m_sql.safe_query("SELECT ID, Manufacturer, Profile, [Type] FROM EnoceanSensors WHERE (HardwareID==%d) AND (DeviceID=='%q')", m_HwdID, szDeviceID);
2040 if (result.size() < 1)
2041 {
2042 _log.Log(LOG_NORM, "EnOcean: Need Teach-In for %s", szDeviceID);
2043 return;
2044 }
2045
2046 RBUF tsen;
2047 memset(&tsen, 0, sizeof(RBUF));
2048 tsen.LIGHTING2.packetlength = sizeof(tsen.LIGHTING2) - 1;
2049 tsen.LIGHTING2.packettype = pTypeLighting2;
2050 tsen.LIGHTING2.subtype = sTypeAC;
2051 tsen.LIGHTING2.seqnbr = 0;
2052
2053 tsen.LIGHTING2.id1 = (BYTE)ID_BYTE3;
2054 tsen.LIGHTING2.id2 = (BYTE)ID_BYTE2;
2055 tsen.LIGHTING2.id3 = (BYTE)ID_BYTE1;
2056 tsen.LIGHTING2.id4 = (BYTE)ID_BYTE0;
2057 tsen.LIGHTING2.level = dim_power;
2058 tsen.LIGHTING2.rssi = 12;
2059
2060 tsen.LIGHTING2.unitcode = channel + 1;
2061 tsen.LIGHTING2.cmnd = (dim_power > 0) ? light2_sOn : light2_sOff;
2062
2063 #ifdef ENOCEAN_BUTTON_DEBUG
2064 _log.Log(LOG_NORM, "EnOcean message: 0x%02X Node 0x%08x UnitID: %02X cmd: %02X ",
2065 DATA_BYTE3,
2066 id,
2067 tsen.LIGHTING2.unitcode,
2068 tsen.LIGHTING2.cmnd
2069 );
2070 #endif //ENOCEAN_BUTTON_DEBUG
2071
2072 // Never learn device from D2-01-0C because subtype may be incorrect
2073 sDecodeRXMessage(this, (const unsigned char *)&tsen.LIGHTING2, NULL, 255);
2074
2075 // Note: if a device uses simultaneously RPS and VLD (ex: nodon inwall module), it can be partially initialized.
2076 // Domoticz will show device status but some functions may not work because EnoceanSensors table has no info on this device (until teach-in is performed)
2077 // If a device has local control (ex: nodon inwall module with physically attached switched), domoticz will record the local control as unit 0.
2078 // Ex: nodon inwall 2 channels will show 3 entries. Unit 0 is the local switch, 1 is the first channel, 2 is the second channel.
2079 // (I only have attached a switch on the first channel, I have no idea which unit number a switch on the 2nd channel will have)
2080 return;
2081 }
2082 break;
2083 }
2084 }
2085 else if (func == 0x02)
2086 {
2087 // D2-02 Temp. Sensor, Light, Occupancy, SmokeType
2088 }
2089 else if (func == 0x03)
2090 {
2091 // D2-03
2092 switch (type)
2093 {
2094 case 0x00: // D2-03-00 Light, Switching and Blind Control Type
2095 break;
2096 case 0x0A: // D2-03-0A Push Button – Single Button
2097 break;
2098 case 0x10: // D2-03-10 Mechanical Handle
2099 break;
2100 case 0x20: // D2-03-20 Beacon with Vibration Detection
2101 break;
2102 }
2103 }
2104 }
2105 default:
2106 _log.Log(LOG_NORM, "EnOcean: Unhandled RORG (%02x)", m_buffer[0]);
2107 break;
2108 }
2109 }
2110