- Files:
-
- 6 added
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
/trunk/S_EnOceanGateway1.xml
r20 r30 11 11 </stateVariable> 12 12 <stateVariable> 13 <name>NumDevices</name> 13 <name>MaxID</name> 14 <dataType>ui4</dataType> 15 </stateVariable> 16 <stateVariable> 17 <name>TeachInStage</name> 18 <dataType>ui4</dataType> 19 </stateVariable> 20 <stateVariable> 21 <name>A_ARG_TYPE_UI4</name> 14 22 <dataType>ui4</dataType> 15 23 </stateVariable> … … 26 34 </argumentList> 27 35 </action> 36 <action> 37 <name>TeachIn</name> 38 <argumentList> 39 <argument> 40 <name>pinCode</name> 41 <direction>in</direction> 42 <relatedStateVariable>A_ARG_TYPE_UI4</relatedStateVariable> 43 </argument> 44 </argumentList> 45 </action> 28 46 </actionList> 29 47 </scpd> -
/trunk/I_EnOceanGateway1.xml
r20 r30 15 15 -- Callbacks 16 16 ClearSysMessage = enoceanPlugin.ClearSysMessage 17 TeachIn = enoceanPlugin.TeachIn 17 18 18 19 return enoceanPlugin.Init( lul_device ) … … 40 41 </run> 41 42 </action> 43 <action> 44 <serviceId>urn:micasaverde-com:serviceId:EnOceanGateway1</serviceId> 45 <name>TeachIn</name> 46 <run> 47 enoceanPlugin.TeachIn( 1, lul_settings.pinCode ) 48 </run> 49 </action> 42 50 </actionList> 43 51 </implementation> -
/trunk/L_EnOceanGateway1.lua
r20 r30 1 -- Steps to get Vera learned into a device (e.g. an actuator), to be able to control it: 2 -- 0. User enters pin code into GUI and presses the Teach button. 3 -- 1. Vera transmits 'Unlock device' telegram. (DONE) 4 -- 2. Vera transmits 'Query ID' telegram. (DONE) 5 -- 3. Vera receives 'Query ID' answer and creates the devices with the appropriate sender ID. (DONE) 6 -- 4. Vera transmits 'Enter learn mode' telegram. (DONE) 7 -- 5. Vera transmits RPS teach-in telegram. (DONE) 8 -- 6. Vera transmits 'Stop learn' telegram. (DONE) 9 -- 7. Vera transmits 'Lock device' telegram. (DONE) 10 -- 11 -- Requirements 12 -- * Vera must have an unique sender ID. (DONE) 13 -- 14 -- FAQ 15 -- Q: When Vera broadcasts the 'Unlock device' command, won't all the devices be unlocked and learn about Vera? 16 -- A: We assume that only devices which accepted the pin code will be unlocked. 17 -- So, if the user has 3 actuators but only 1 accepted the PIN code, 18 -- that will be the only actuator that will learn about Vera and respond to commands from Vera. 19 1 20 module("L_EnOceanGateway1", package.seeall) 2 21 … … 9 28 local F_DEBUG_MODE = true 10 29 local F_LEARN_MODE = false 11 local F_WAIT_FOR_RESPONSE = false -- If this is 'true', no further messages will be sent12 30 local F_SENDING_IN_PROGRESS = false 13 31 … … 46 64 -- Message types 47 65 local SYS_MESSAGE_TYPES = { 48 BUSY = 49 ERROR = 50 SUCCESS = 66 BUSY = 1, 67 ERROR = 2, 68 SUCCESS = 4 51 69 } 52 70 53 71 -- Delays for luup.delay, in seconds 54 72 local DELAYS = { 55 SEND_NEXT_MESSAGE = 2 73 SEND_NEXT_MESSAGE = 2, 74 TEACHIN_STAGES = 3 56 75 } 57 76 … … 76 95 77 96 local TELEGRAM_TYPES = { 78 ["RPS"] = 0xF6, 79 ["1BS"] = 0xD5, 80 ["4BS"] = 0xA5 97 ["RPS"] = 0xF6, 98 ["1BS"] = 0xD5, 99 ["4BS"] = 0xA5, 100 ["ADT"] = 0xA6, 101 ["SYS_EX"] = 0xC5 81 102 } 82 103 … … 86 107 ["RPS_EB"] = { 3, 1 }, 87 108 ["RPS_T21"] = { 2, 1 }, 109 ["RPS_NU"] = { 3, 1 }, 88 110 ["1BS_LRN"] = { 4, 1 }, 89 111 ["1BS_CO"] = { 7, 1 }, … … 101 123 ["4BS_06_ILL1"] = { 16, 8 }, 102 124 ["4BS_06_ILL2"] = { 8, 8 }, 103 ["4BS_07_PIRS"] = { 16, 8 } 125 ["4BS_07_PIRS"] = { 16, 8 }, 126 ["SYS_EX_FUNC_CODE"] = { 36, 12 }, 127 ["SYS_EX_MANUF_ID"] = { 25, 11 } 104 128 } 105 129 … … 113 137 [6] = "DI", 114 138 [7] = "DO", 139 } 140 141 -- Base ID related variables. 142 local MIN_BASE_ID = 0xFF800000 143 local MAX_BASE_ID = 0xFFFFFF00 -- The last sender IDs interval has only 127 values instead of 128, 144 -- so don't use it, to avoid handling it separately. 145 local BASE_ID_GAP = 128 146 ---- 147 148 local NUM_TEACHIN_STAGES = 7 149 150 local TEACHIN_STAGES = { 151 NONE = 0, 152 SEND_UNLOCK_RMCC = 1, 153 SEND_QUERY_ID_RMCC = 2, 154 RECV_QUERY_ID_ANSWER = 3, 155 SEND_START_LEARN_RPC = 4, 156 SEND_TEACHIN_TELEGRAM = 5, 157 SEND_STOP_LEARN_RPC = 6, 158 SEND_LOCK_RMCC = 7 159 } 160 161 local TEACHIN_STAGE_STRINGS = { 162 [ TEACHIN_STAGES.NONE ] = "NONE", 163 [ TEACHIN_STAGES.SEND_UNLOCK_RMCC ] = "SEND UNLOCK RMCC", 164 [ TEACHIN_STAGES.SEND_QUERY_ID_RMCC ] = "SEND QUERY ID RMCC", 165 [ TEACHIN_STAGES.RECV_QUERY_ID_ANSWER ] = "RECEIVE QUERY ID ANSWER", 166 [ TEACHIN_STAGES.SEND_START_LEARN_RPC ] = "SEND START LEARN RPC", 167 [ TEACHIN_STAGES.SEND_TEACHIN_TELEGRAM ]= "SEND TEACH-IN TELEGRAM", 168 [ TEACHIN_STAGES.SEND_STOP_LEARN_RPC ] = "SEND STOP LEARN RPC", 169 [ TEACHIN_STAGES.SEND_LOCK_RMCC ] = "SEND LOCK RMCC" 170 } 171 172 -- Remote management commands function numbers 173 local RMCC = { 174 UNLOCK = 0x001, 175 LOCK = 0x002, 176 QUERY_ID = 0x004, 177 QUERY_ID_ANSWER = 0x604 178 } 179 180 -- Remote procedure calls function numbers 181 local RPC = { 182 REMOTE_LEARN = 200 183 } 184 185 local DATA_LENGTHS = { 186 [RMCC.UNLOCK] = 4, 187 [RMCC.LOCK] = 4, 188 [RMCC.QUERY_ID] = 3, 189 [RPC.REMOTE_LEARN] = 4 115 190 } 116 191 … … 163 238 164 239 local g_childDevices = {} -- The list of all our child devices 165 local g_numDevices = 0 -- The number of learned EnOcean devices. This is different than the number of child devices, 166 -- because there can be several child devices for one EnOcean device. 240 local g_maxId = 0 -- A number that increments with every device learned. 167 241 168 242 local g_knownSenderIds = {} -- The list of the newly learned EnOcean devices while in Learn Mode. … … 180 254 ------------------------------------------------------------------------------------------------------------------------ 181 255 182 local log 183 -- Getthe 'getinfo' function from the 'debug' module before overwriting the 'debug' keyword.184 local getinfo 256 local log = luup.log 257 -- Save the 'getinfo' function from the 'debug' module before overwriting the 'debug' keyword. 258 local getinfo = debug.getinfo 185 259 local function debug() end 186 260 187 261 188 local function ShowSysMessage (message, mode, critical)262 local function ShowSysMessage (message, mode, permanent) 189 263 mode = mode or SYS_MESSAGE_TYPES.BUSY 190 critical = criticalor false264 permanent = permanent or false 191 265 log( "(EnOceanPlugin::ShowSysMessage-"..(getinfo(2).name or "N/A")..") mode: ".. mode 192 ..", critical: ".. tostring( critical) ..", message: ".. message )266 ..", permanent: ".. tostring( permanent ) ..", message: ".. message ) 193 267 194 268 luup.task( message, mode, "EnOcean Plugin", g_taskHandle ) 195 269 g_lastSysMessage = tostring( os.time() ) 196 270 197 if not criticalthen271 if not permanent then 198 272 -- Clear the previous system message, since it's transient. 199 273 luup.call_delay( "ClearSysMessage", 30, g_lastSysMessage ) 200 else 274 elseif mode == SYS_MESSAGE_TYPES.ERROR then 275 -- Critical error. 201 276 luup.set_failure( true, g_parentDevice ) 202 277 end … … 258 333 -- Concatenates a table of numbers into a string separated by the given separator. 259 334 local function ConcatTableHex( t, sep, start, length ) 260 sep = sep or "-"335 sep = sep or '-' 261 336 start = start or 1 262 337 if start < 0 then … … 291 366 292 367 368 -- Appends the second array at the end of the first array. 369 local function AppendArray( a1, a2 ) 370 local table_insert = table.insert 371 for _, v in ipairs(a2) do 372 table_insert( a1, v ) 373 end 374 end 375 376 377 -- Splits a string based on the given separator. Returns a table. 378 local function SplitString( s, sep ) 379 sep = sep or ' ' 380 local t = {} 381 for token in s:gmatch( "[^"..sep.."]+" ) do 382 table.insert( t, token ) 383 end 384 return t 385 end 386 293 387 ------------------------------------------------------------------------------------------------------------------------ 294 388 -- Misc plugin functions … … 303 397 crc = CRC8_TABLE[bit.bxor( crc, data[i] ) + 1] 304 398 end 305 debug( "(EnOceanPlugin::ComputeCrc) crc=".. ToHex( crc ))399 --debug( "(EnOceanPlugin::ComputeCrc) crc=".. ToHex( crc )) 306 400 return crc 401 end 402 403 404 local function GetBaseIdArray() 405 -- If the base ID array is in cache, return the cached version, 406 -- otherwise compute it. 407 -- The 'c' prefix means the variable is the cache. 408 if c_baseIdArray then 409 return c_baseIdArray 410 else 411 c_baseIdArray = { 0, 0, 0, 0 } 412 for nibbleNum = 1, 4 do 413 local numBitsShift = (4 - nibbleNum) * 8 414 local mask = bit.lshift( 0xFF, numBitsShift ) 415 c_baseIdArray[nibbleNum] = bit.rshift( bit.band( g_baseId, mask ), numBitsShift ) 416 end 417 return c_baseIdArray 418 end 419 end 420 421 422 -- Splits the given number into an array of bits indexed from 1. 423 -- The first bit (bit 0) is the first element in the array. 424 local function SplitToBits( number, numBits ) 425 local bits = {} 426 427 local table_insert = table.insert 428 while number > 0 do 429 table_insert( bits, number % 2 ) 430 number = math.floor( number / 2 ) 431 end 432 -- Pad with 0 up to the number of required bits. 433 for i = #bits + 1, numBits do 434 table_insert( bits, 0 ) 435 end 436 437 return bits 438 end 439 440 441 -- Padds the value to occupy the given number of bits, 442 -- and inserts it in the data, which is an array of bytes. 443 local function InsertValueAtLocation (data, value, offset, numBits) 444 -- Split value into an array of bits indexed from 1. 445 local bits = SplitToBits( value, numBits ) 446 -- Insert all the bits starting from the last bit. 447 for bitIdx = #bits, 1, -1 do 448 -- Find byte index, indexed from 1. 449 local byteIdx = math.floor( offset / 8 ) + 1 450 -- Find bit position, indexed from 0. 451 local bitPos = byteIdx * 8 - offset - 1 452 if bits[bitIdx] == 1 then 453 data[byteIdx] = bit.set( data[byteIdx], bit.lshift( 1, bitPos )) 454 else 455 data[byteIdx] = bit.unset( data[byteIdx], bit.lshift( 1, bitPos )) 456 end 457 offset = offset + 1 458 end 307 459 end 308 460 … … 374 526 375 527 local firstAction = GetValueAtLocation( data, LOCATIONS["RPS_R1"] ) 376 local altid = senderId ..'-'.. ROCKER_BUTTONS[firstAction] 528 local altid = senderId ..'-'.. ROCKER_BUTTONS[firstAction]:sub( 1, 1 ) 377 529 debug( "(EnOceanPlugin::RPS) altid: ".. altid ) 378 530 379 531 -------------------------------------------------------------------------------------------- 380 -- The device is learned .532 -- The device is learned and it's not a "number of buttons pressed" telegram. 381 533 -------------------------------------------------------------------------------------------- 382 534 if g_childDevices[altid] then 383 debug( "(EnOceanPlugin::RPS) Device known: ".. g_childDevices[altid] ) 384 local pressed = GetValueAtLocation( data, LOCATIONS["RPS_EB"] ) 535 log( "(EnOceanPlugin::RPS) Device known: ".. g_childDevices[altid] ) 536 -- Ignore "number of buttons pressed" telegrams. 537 if GetValueAtLocation( { status }, LOCATIONS["RPS_NU"] ) == 0 then 538 debug( "(EnOceanPlugin::RPS) 'Number of buttons pressed' telegram. Return" ) 539 return 540 end 541 -- Ignore "button released" telegrams. 542 if GetValueAtLocation( data, LOCATIONS["RPS_EB"] ) == 0 then 543 debug( "(EnOceanPlugin::RPS) 'Button released' telegram. Return" ) 544 return 545 end 546 local pressed = ( ROCKER_BUTTONS[firstAction]:sub( 2, 2 ) == "I" ) and "1" or "0" 385 547 luup.variable_set( SID.SW_POWER, "Status", pressed, g_childDevices[altid] ) 386 548 387 549 -------------------------------------------------------------------------------------------- 388 550 -- The device is not learned, and we're in Learn Mode. 389 -- RPS devices don't have Teach -in telegrams.551 -- RPS devices don't have TeachIn-in telegrams. 390 552 -------------------------------------------------------------------------------------------- 391 553 elseif F_LEARN_MODE and not TableContains( g_knownSenderIds, senderId ) then 392 554 -- We don't want to add the same device twice. 393 555 set.add( g_knownSenderIds, senderId ) 394 g_ numDevices = g_numDevices+ 1556 g_maxId = g_maxId + 1 395 557 396 558 -- Find the device type. If T21 is 1, this is a 2 rocker switch, otherwise it's a 4 rocker switch. … … 400 562 -- Create the devices. 401 563 log( "(EnOceanPlugin::RPS) T21: ".. t21 ..", num rockers: ".. numRockers ) 402 ShowSysMessage( "Found ".. numRockers .." Rocker Switch (EO-".. PadLeft( g_ numDevices) ..")" )403 -- We have 2 buttons for each rocker, so we'll create a switch for each button.564 ShowSysMessage( "Found ".. numRockers .." Rocker Switch (EO-".. PadLeft( g_maxId ) ..")" ) 565 -- Create a switch for each rocker. 404 566 local deviceName 405 567 local parameters = SID.SW_POWER ..",Status=0" 406 for i = 0, 2 * numRockers - 1 do 407 altid = senderId ..'-'.. ROCKER_BUTTONS[i] 408 deviceName = "(EO-".. PadLeft( g_numDevices ) ..") Button ".. ROCKER_BUTTONS[i] 568 for i = 0, numRockers - 1 do 569 -- Get only the button, ignore the state. 570 local button = ROCKER_BUTTONS[2 * i]:sub( 1, 1 ) 571 altid = senderId ..'-'.. button 572 deviceName = "(EO-".. PadLeft( g_maxId ) ..") Button ".. button 409 573 log( "(EnOceanPlugin::RPS) New device: ".. deviceName ) 410 574 table.insert( g_newDevices, { … … 417 581 } ) 418 582 end 583 else 584 log( "(EnOceanPlugin::RPS) Unkown device" ) 419 585 end 420 586 end, … … 427 593 428 594 local isDataTelegram = GetValueAtLocation( data, LOCATIONS["1BS_LRN"] ) == 1 429 debug( "(EnOceanPlugin::1BS) ".. (isDataTelegram and "Data" or "Teach -in") .." telegram" )595 debug( "(EnOceanPlugin::1BS) ".. (isDataTelegram and "Data" or "TeachIn-in") .." telegram" ) 430 596 431 597 -------------------------------------------------------------------------------------------- … … 433 599 -------------------------------------------------------------------------------------------- 434 600 if g_childDevices[senderId] and isDataTelegram then 435 debug( "(EnOceanPlugin::1BS) Device known: ".. g_childDevices[senderId] )601 log( "(EnOceanPlugin::1BS) Device known: ".. g_childDevices[senderId] ) 436 602 local tripped = 1 - GetValueAtLocation( data, LOCATIONS["1BS_CO"] ) 437 603 luup.variable_set( SID.SECURITY, "Tripped", tripped, g_childDevices[senderId] ) … … 439 605 -------------------------------------------------------------------------------------------- 440 606 -- The device is not learned, and we're in Learn Mode. 441 -- Only learn the device if this is a Teach -in telegram, even if it doesn't contain any useful information.607 -- Only learn the device if this is a TeachIn-in telegram, even if it doesn't contain any useful information. 442 608 -- We don't want to accidentaly learn devices that the user doesn't want. 443 609 -------------------------------------------------------------------------------------------- … … 445 611 -- We don't want to add the same device twice. 446 612 set.add( g_knownSenderIds, senderId ) 447 g_ numDevices = g_numDevices+ 1613 g_maxId = g_maxId + 1 448 614 log( "(EnOceanPlugin::1BS) New device: ".. deviceName ) 449 ShowSysMessage( "Found Single Input Contact (EO-".. PadLeft( g_ numDevices) ..")" )450 deviceName = "(EO-".. PadLeft( g_ numDevices) ..") Single Input Contact"615 ShowSysMessage( "Found Single Input Contact (EO-".. PadLeft( g_maxId ) ..")" ) 616 deviceName = "(EO-".. PadLeft( g_maxId ) ..") Single Input Contact" 451 617 table.insert( g_newDevices, { 452 618 ["altid"] = senderId, … … 457 623 ["parameters"] = SID.SECURITY ..",Tripped=0\n".. SID.SECURITY ..",Armed=0" 458 624 } ) 625 else 626 log( "(EnOceanPlugin::1BS) Unkown device or TeachIn-in telegram" ) 459 627 end 460 628 end, … … 464 632 -------------------------------------------------------------------------------------------------------------------- 465 633 [TELEGRAM_TYPES["4BS"]] = function (senderId, data, status) 466 debug( "(EnOceanPlugin::4BS) 4BS telegram handler")634 debug( "(EnOceanPlugin::4BS) 4BS telegram handler" ) 467 635 468 636 local isDataTelegram = GetValueAtLocation( data, LOCATIONS["4BS_LRN"] ) == 1 469 debug( "(EnOceanPlugin::4BS) ".. (isDataTelegram and "Data" or "Teach-in") .." telegram")637 debug( "(EnOceanPlugin::4BS) ".. (isDataTelegram and "Data" or "TeachIn-in") .." telegram" ) 470 638 471 639 -------------------------------------------------------------------------------------------- … … 481 649 if eepFunc == 0x02 then 482 650 if not g_childDevices[altid] then 483 debug( "(EnOceanPlugin::4BS) Device not known. Return" )651 log( "(EnOceanPlugin::4BS) Device not known. Return" ) 484 652 return 485 653 end 486 debug( "(EnOceanPlugin::4BS) Device known: ".. g_childDevices[altid] )654 log( "(EnOceanPlugin::4BS) Device known: ".. g_childDevices[altid] ) 487 655 488 656 local sensorType = math.floor( eepType / 0x10 ) … … 522 690 local altidTemp = altid .."-temp" 523 691 if not g_childDevices[altidHum] then 524 debug( "(EnOceanPlugin::4BS) Device not known. Return" )692 log( "(EnOceanPlugin::4BS) Device not known. Return" ) 525 693 return 526 694 end 527 debug( "(EnOceanPlugin::4BS) Device known: hum ".. g_childDevices[altidHum] ..", temp "..( g_childDevices[altidTemp] or "NONE" ))695 log( "(EnOceanPlugin::4BS) Device known: hum ".. g_childDevices[altidHum] ..", temp "..( g_childDevices[altidTemp] or "NONE" )) 528 696 529 697 -- Get the humidity. … … 543 711 elseif eepFunc == 0x06 then 544 712 if not g_childDevices[altid] then 545 debug( "(EnOceanPlugin::4BS) Device not known. Return" )713 log( "(EnOceanPlugin::4BS) Device not known. Return" ) 546 714 return 547 715 end 548 debug( "(EnOceanPlugin::4BS) Device known: ".. g_childDevices[altid] )716 log( "(EnOceanPlugin::4BS) Device known: ".. g_childDevices[altid] ) 549 717 550 718 local range = GetValueAtLocation( data, LOCATIONS["4BS_06_RS"] ) … … 569 737 elseif eepFunc == 0x07 then 570 738 if not g_childDevices[altid] then 571 debug( "(EnOceanPlugin::4BS) Device not known. Return" )739 log( "(EnOceanPlugin::4BS) Device not known. Return" ) 572 740 return 573 741 end 574 debug( "(EnOceanPlugin::4BS) Device known: ".. g_childDevices[altid] )742 log( "(EnOceanPlugin::4BS) Device known: ".. g_childDevices[altid] ) 575 743 576 744 local tripped = GetValueAtLocation( data, LOCATIONS["4BS_07_PIRS"] ) -- 0...255 … … 579 747 end 580 748 -------------------------------------------------------------------------------------------- 581 -- The device is not learned, we're in Learn Mode, and we received a Teach -in telegram.749 -- The device is not learned, we're in Learn Mode, and we received a TeachIn-in telegram. 582 750 -------------------------------------------------------------------------------------------- 583 751 elseif F_LEARN_MODE and not isDataTelegram and not g_childDevices[senderId] and not TableContains( g_knownSenderIds, senderId ) then 584 752 local lrnType = GetValueAtLocation( data, LOCATIONS["4BS_LRN_TYPE"] ) 585 753 if lrnType ~= 1 and lrnType ~= 2 then 586 log( "(EnOceanPlugin::4BS) Teach -in telegram variation 1, discard it" )754 log( "(EnOceanPlugin::4BS) TeachIn-in telegram variation 1, discard it" ) 587 755 return 588 756 end … … 590 758 -- We don't want to add the same device twice. 591 759 set.add( g_knownSenderIds, senderId ) 592 g_ numDevices = g_numDevices+ 1760 g_maxId = g_maxId + 1 593 761 594 762 local eepFunc = GetValueAtLocation( data, LOCATIONS["4BS_LRN_EEP_FUNC"] ) 595 763 local eepType = GetValueAtLocation( data, LOCATIONS["4BS_LRN_EEP_TYPE"] ) 596 local manufId = GetValueAtLocation( data, LOCATIONS["4BS_LRN_MANUF_ID"] ) 764 local manufId = GetValueAtLocation( data, LOCATIONS["4BS_LRN_MANUF_ID"] ) -- not used in this version 597 765 598 766 log( "(EnOceanPlugin::4BS) Func ".. ToHex( eepFunc ) ..", Type ".. ToHex( eepType )) … … 603 771 -- 604 772 if eepFunc == 0x02 then 605 local deviceName = "(EO-".. PadLeft( g_ numDevices) ..") Temperature Sensor"773 local deviceName = "(EO-".. PadLeft( g_maxId ) ..") Temperature Sensor" 606 774 log( "(EnOceanPlugin::4BS) New device: ".. deviceName ) 607 ShowSysMessage( "Found Temperature sensor (EO-".. PadLeft( g_ numDevices) ..")" )775 ShowSysMessage( "Found Temperature sensor (EO-".. PadLeft( g_maxId ) ..")" ) 608 776 table.insert( g_newDevices, { 609 777 ["altid"] = senderId, … … 618 786 -- 619 787 elseif eepFunc == 0x04 then 620 ShowSysMessage( "Found Temperature and Humidity sensor (EO-".. PadLeft( g_ numDevices) ..")" )788 ShowSysMessage( "Found Temperature and Humidity sensor (EO-".. PadLeft( g_maxId ) ..")" ) 621 789 -- Create the humidity sensor. 622 deviceName = "(EO-".. PadLeft( g_ numDevices) ..") Humidity Sensor"790 deviceName = "(EO-".. PadLeft( g_maxId ) ..") Humidity Sensor" 623 791 log( "(EnOceanPlugin::4BS) New device: ".. deviceName ) 624 792 table.insert( g_newDevices, { … … 631 799 } ) 632 800 -- Create the temperature sensor. 633 local deviceName = "(EO-".. PadLeft( g_ numDevices) ..") Temperature Sensor"801 local deviceName = "(EO-".. PadLeft( g_maxId ) ..") Temperature Sensor" 634 802 log( "(EnOceanPlugin::4BS) New device: ".. deviceName ) 635 803 table.insert( g_newDevices, { … … 645 813 -- 646 814 elseif eepFunc == 0x06 then 647 local deviceName = "(EO-".. PadLeft( g_ numDevices) ..") Light Sensor"815 local deviceName = "(EO-".. PadLeft( g_maxId ) ..") Light Sensor" 648 816 log( "(EnOceanPlugin::4BS) New device: ".. deviceName ) 649 ShowSysMessage( "Found Light sensor (EO-".. PadLeft( g_ numDevices) ..")" )817 ShowSysMessage( "Found Light sensor (EO-".. PadLeft( g_maxId ) ..")" ) 650 818 table.insert( g_newDevices, { 651 819 ["altid"] = senderId, … … 660 828 -- 661 829 elseif eepFunc == 0x07 then 662 local deviceName = "(EO-".. PadLeft( g_ numDevices) ..") Occupancy Sensor"830 local deviceName = "(EO-".. PadLeft( g_maxId ) ..") Occupancy Sensor" 663 831 log( "(EnOceanPlugin::4BS) New device: ".. deviceName ) 664 ShowSysMessage( "Found Occupancy sensor (EO-".. PadLeft( g_ numDevices) ..")" )832 ShowSysMessage( "Found Occupancy sensor (EO-".. PadLeft( g_maxId ) ..")" ) 665 833 table.insert( g_newDevices, { 666 834 ["altid"] = senderId, … … 675 843 ShowSysMessage( "Found unknown device A5-".. ToHex( eepFunc ) .."-".. ToHex( eepType ) ) 676 844 end 845 else 846 log( "(EnOceanPlugin::4BS) Unkown device or TeachIn-in telegram" ) 847 end 848 end, 849 850 -------------------------------------------------------------------------------------------------------------------- 851 -- ADT 852 -------------------------------------------------------------------------------------------------------------------- 853 [TELEGRAM_TYPES["ADT"]] = function (senderId, data, status) 854 debug( "(EnOceanPlugin::ADT) ADT telegram handler" ) 855 856 -- Check if we're in a teach-in procedure and we're expecting a Query ID answer. 857 if g_teachInStage == TEACHIN_STAGES.RECV_QUERY_ID_ANSWER then 858 -- Check if this telegram is a Query ID answer telegram: 859 -- 1. Check if the encapsulated RORG is the one we expect. 860 if data[1] ~= TELEGRAM_TYPES.SYS_EX then 861 log( "(EnOceanPlugin::ADT) Unhandled encapsulated RORG: ".. data[1] ) 862 return 863 end 864 -- 2. Check if the RMC function code is the one we expect. 865 local functionCode = GetValueAtLocation( data, LOCATIONS["SYS_EX_FUNC_CODE"] ) 866 if functionCode ~= RMCC.QUERY_ID_ANSWER then 867 log( "(EnOceanPlugin::ADT) Unhandled remote management control command: ".. functionCode ) 868 return 869 end 870 -- Get the manufacturer ID. (not used in this version) 871 local manufId = GetValueAtLocation( data, LOCATIONS["SYS_EX_MANUF_ID"] ) 872 873 -------------------------------------------------------------------- 874 -- Create the device and move to the next stage. 875 -------------------------------------------------------------------- 876 g_teachInStage = TEACHIN_STAGES.SEND_START_LEARN_RPC 877 luup.variable_set( SID.ENOCEAN, "TeachInStage", g_teachInStage, g_parentDevice ) 878 879 g_maxId = g_maxId + 1 880 luup.variable_set( SID.ENOCEAN, "MaxID", g_maxId, g_parentDevice ) 881 882 local ptr = luup.chdev.start( g_parentDevice ) 883 luup.chdev.append( g_parentDevice, ptr, senderId, "(EO-".. PadLeft( g_maxId ) ..") Actuator", 884 DEVICE_TYPES.BINARY_LIGHT, DEVICE_FILES.BINARY_LIGHT, "", SID.SW_POWER ..",Status=0", false ) 885 luup.chdev.sync( g_parentDevice, ptr ) 677 886 end 678 887 end … … 697 906 .. ", senderId: ".. senderId 698 907 .. ", status: ".. ToHex( status ) 699 .. ", data: ".. ConcatTableHex( data , '-'))908 .. ", data: ".. ConcatTableHex( data )) 700 909 701 910 if RADIO_TELEGRAM_HANDLERS[radioTelegramType] then … … 741 950 [STATE.GET_SYNC_STATE] = function (rxByte) 742 951 if rxByte == SYNC_BYTE then 743 debug( "(EnOceanPlugin::GET_SYNC_STATE) Got sync byte, go to GET_HEADER_STATE" )952 --debug( "(EnOceanPlugin::GET_SYNC_STATE) Got sync byte, go to GET_HEADER_STATE" ) 744 953 state = STATE.GET_HEADER_STATE 745 954 crc = 0 … … 755 964 -- All the header bytes received? 756 965 if rxCount == 4 then 757 debug( "(EnOceanPlugin::GET_HEADER_STATE) Received all header bytes, go to CHECK_CRC8H_STATE" )966 --debug( "(EnOceanPlugin::GET_HEADER_STATE) Received all header bytes, go to CHECK_CRC8H_STATE" ) 758 967 state = STATE.CHECK_CRC8H_STATE 759 968 end … … 764 973 -- Header CRC correct? 765 974 if ComputeCrc( rxBuf, 1, rxCount ) ~= rxByte then 766 debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) Incorrect header CRC" )975 --debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) Incorrect header CRC" ) 767 976 -- No. Check if there is a sync byte (0x55) in the header. 768 977 local pos = -1 … … 777 986 if pos == -1 and rxByte ~= SYNC_BYTE then 778 987 -- Neither the header nor the CRC8H contain the sync byte 779 debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) Neither the header nor the CRC8H contain the sync byte, go to GET_SYNC_STATE" )988 --debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) Neither the header nor the CRC8H contain the sync byte, go to GET_SYNC_STATE" ) 780 989 state = STATE.GET_SYNC_STATE 781 990 return … … 783 992 -- The header does not contain the sync byte but the CRC8H does 784 993 -- The sync byte could be the beginning of a packet 785 debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) The header does not contain the sync byte but the CRC8H does, go to GET_HEADER_STATE" )994 --debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) The header does not contain the sync byte but the CRC8H does, go to GET_HEADER_STATE" ) 786 995 state = STATE.GET_HEADER_STATE 787 996 rxCount = 0 … … 802 1011 803 1012 if rxCount < 4 then 804 debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) There are still header bytes to receive, go to GET_HEADER_STATE" )1013 --debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) There are still header bytes to receive, go to GET_HEADER_STATE" ) 805 1014 state = STATE.GET_HEADER_STATE 806 1015 end 807 1016 808 debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) All header bytes received, stay in CHECK_CRC8H_STATE" )1017 --debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) All header bytes received, stay in CHECK_CRC8H_STATE" ) 809 1018 return 810 1019 end … … 813 1022 rxDataLen = rxBuf[1] * 0x100 + rxBuf[2] 814 1023 rxOptDataLen = rxBuf[3] 815 debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) Header CRC correct, rxDataLen=".. rxDataLen ..", rxOptDataLen=".. rxOptDataLen )1024 --debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) Header CRC correct, rxDataLen=".. rxDataLen ..", rxOptDataLen=".. rxOptDataLen ) 816 1025 if (rxDataLen + rxOptDataLen) == 0 then 817 1026 -- No. Sync byte received? 818 1027 if rxByte == SYNC_BYTE then 819 1028 -- Yes 820 debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) Header CRC incorrect, sync byte received, go to GET_HEADER_STATE" )1029 --debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) Header CRC incorrect, sync byte received, go to GET_HEADER_STATE" ) 821 1030 state = STATE.GET_HEADER_STATE 822 1031 rxCount = 0 … … 825 1034 826 1035 -- Packet with correct CRC8H, but wrong length fields 827 debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) Packet with correct CRC8H, but wrong length fields, go to GET_SYNC_STATE" )1036 --debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) Packet with correct CRC8H, but wrong length fields, go to GET_SYNC_STATE" ) 828 1037 state = STATE.GET_SYNC_STATE 829 1038 return … … 831 1040 832 1041 -- Correct header CRC8. Go to the reception of data. 833 debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) Correct header CRC8, go to GET_DATA_STATE" )1042 --debug( "(EnOceanPlugin::CHECK_CRC8H_STATE) Correct header CRC8, go to GET_DATA_STATE" ) 834 1043 state = STATE.GET_DATA_STATE 835 1044 rxCount = 0 … … 844 1053 -- When all the expected bytes are received, go to calculate data checksum. 845 1054 if rxCount == (rxDataLen + rxOptDataLen) then 846 debug( "(EnOceanPlugin::GET_DATA_STATE) All expected bytes received, go to CHECK_CRC8D_STATE" )1055 --debug( "(EnOceanPlugin::GET_DATA_STATE) All expected bytes received, go to CHECK_CRC8D_STATE" ) 847 1056 state = STATE.CHECK_CRC8D_STATE 848 1057 end … … 856 1065 if ComputeCrc( rxBuf, 1, rxCount ) == rxByte then 857 1066 -- Correct packet received 858 debug( "(EnOceanPlugin::CHECK_CRC8D_STATE) Correct packet received, handle it and go to GET_SYNC_STATE" )1067 --debug( "(EnOceanPlugin::CHECK_CRC8D_STATE) Correct packet received, handle it and go to GET_SYNC_STATE" ) 859 1068 if PACKET_HANDLERS[rxPacketType] then 860 1069 PACKET_HANDLERS[rxPacketType]( ExtractSubtable( rxBuf, 1, rxDataLen ), ExtractSubtable( rxBuf, rxDataLen + 1, rxOptDataLen )) … … 868 1077 -- If the received byte is the sync byte, then it could be the sync byte for next packet. 869 1078 if rxByte == SYNC_BYTE then 870 debug( "(EnOceanPlugin::CHECK_CRC8D_STATE) Incorrect CRC8, the received byte is the sync byte, go to GET_HEADER_STATE" )1079 --debug( "(EnOceanPlugin::CHECK_CRC8D_STATE) Incorrect CRC8, the received byte is the sync byte, go to GET_HEADER_STATE" ) 871 1080 state = STATE.GET_HEADER_STATE 872 1081 rxCount = 0 … … 880 1089 function HandleIncoming (lul_data) 881 1090 local rxByte = string.byte( lul_data ) 882 debug( "(EnOceanPlugin::HandleIncoming) state ".. state ..", rxByte=".. ToHex( rxByte ))1091 --debug( "(EnOceanPlugin::HandleIncoming) state ".. state ..", rxByte=".. ToHex( rxByte )) 883 1092 if STATE_MACHINE[state] then 884 1093 STATE_MACHINE[state]( rxByte ) … … 895 1104 ------------------------------------------------------------------------------------------------------------------------ 896 1105 897 local function AddToSendQueue (packetType, data, optData, waitForResponse) 898 local insert = table.insert 899 local char = string.char 900 901 local packet = { SYNC_BYTE } -- The sync byte 902 903 insert( packet, char( #data / 0x100 )) -- First byte of the data length 904 insert( packet, char( #data % 0x100 )) -- Seconds byte of the data length 905 insert( packet, char( #optData )) -- Length of the optional data 906 insert( packet, packetType ) -- The packet type 907 insert( packet, ComputeCrc(packet, 2, 4)) -- The header CRC8 908 909 insert( packet, data ) -- The user data 910 insert( packet, optData ) -- The optional user data 911 insert( packet, ComputeCrc( packet, 7, #data + #optData )) -- The data CRC8 912 913 table.insert( g_messageQueue, {content = table.concat(packet), waitForResponse = (waitForResponse or false)} ) 1106 local function CreateSysExTelegram (rmcc, data) 1107 -- 9 bytes - The first byte is always the RORG. 1108 local sysEx = { TELEGRAM_TYPES["SYS_EX"], 0, 0, 0, 0, 0, 0, 0, 0 } 1109 local offset = 8 1110 1111 -- SEQ - 2 bits 1112 local numBits = 2 1113 math.randomseed( os.time() ) 1114 local seq = math.random(4) - 1 1115 InsertValueAtLocation( sysEx, seq, offset, numBits ) 1116 offset = offset + numBits 1117 1118 -- IDX - 6 bits 1119 numBits = 6 1120 InsertValueAtLocation( sysEx, 0, offset, numBits ) 1121 offset = offset + numBits 1122 1123 -- Data length - 9 bits 1124 numBits = 9 1125 InsertValueAtLocation( sysEx, DATA_LENGTHS[rmcc], offset, numBits ) 1126 offset = offset + numBits 1127 1128 -- Manufacturer ID - 11 bits 1129 numBits = 11 1130 InsertValueAtLocation( sysEx, 0x7FF, offset, numBits ) 1131 offset = offset + numBits 1132 1133 -- Function number - 12 bits 1134 numBits = 12 1135 InsertValueAtLocation( sysEx, rmcc, offset, numBits ) 1136 offset = offset + numBits 1137 1138 -- Data - 32 bits 1139 numBits = 32 1140 InsertValueAtLocation( sysEx, data, offset, numBits ) 1141 1142 return sysEx 1143 end 1144 1145 1146 local function AddToSendQueue (packetType, data, optData) 1147 optData = optData or {} 1148 1149 -- Declare often used functions locally for better performance. 1150 local table_insert = table.insert 1151 1152 -- The sync byte 1153 local packet = { SYNC_BYTE } 1154 1155 -- The header 1156 table_insert( packet, string.char( #data / 0x100 )) -- First byte of the data length 1157 table_insert( packet, string.char( #data % 0x100 )) -- Seconds byte of the data length 1158 table_insert( packet, string.char( #optData )) -- Length of the optional data 1159 table_insert( packet, packetType ) -- The packet type 1160 table_insert( packet, ComputeCrc(packet, 2, 4)) -- The header CRC8 1161 1162 -- The data 1163 AppendArray( packet, data ) -- The user data 1164 AppendArray( packet, optData ) -- The optional user data 1165 table_insert( packet, ComputeCrc( packet, 7, #data + #optData )) -- The data CRC8 1166 1167 table_insert( g_messageQueue, packet ) 914 1168 if not F_SENDING_IN_PROGRESS then 915 1169 SendPacket() … … 919 1173 920 1174 function SendPacket() 1175 -- If we don't have any message to send, return. 1176 if #g_messageQueue == 0 then 1177 F_SENDING_IN_PROGRESS = false 1178 return 1179 end 1180 921 1181 F_SENDING_IN_PROGRESS = true 922 1182 923 if F_WAIT_FOR_RESPONSE then 924 g_triesCount = g_triesCount + 1 925 if g_triesCount < 3 then 926 debug( "(EnOceanPlugin::SendPacket) F_WAIT_FOR_RESPONSE flag is set, g_triesCount=".. g_triesCount .."; try again in ".. DELAYS.SEND_NEXT_MESSAGE .." seconds." ) 927 luup.call_delay( "SendPacket", DELAYS.SEND_NEXT_MESSAGE ) 928 return 929 else 930 log( "(EnOceanPlugin::SendPacket) Didn't receive response for the last message. Stop waiting for response." ) 931 F_WAIT_FOR_RESPONSE = false 932 g_triesCount = 0 933 end 934 end 935 936 local message 937 if #g_messageQueue > 0 then 938 message = g_messageQueue[1] 939 else 940 return 941 end 942 943 debug( "(EnOceanPlugin::SendPacket) Wait for response: ".. message.waitForResponse ) 944 945 if not luup.io.write( message.content ) then 1183 local message = g_messageQueue[1] 1184 log( "(EnOceanPlugin::SendPacket) Send message: ".. ConcatTableHex( message )) 1185 if not luup.io.write( table.concat( message )) then 946 1186 ShowSysMessage( "Failed to send message", SYS_MESSAGE_TYPES.ERROR ) 947 1187 return 948 1188 end 949 1189 950 F_WAIT_FOR_RESPONSE = message.waitForResponse951 1190 table.remove( g_messageQueue, 1 ) 952 1191 … … 973 1212 -- If we learned any new device, create its Luup counterpart and update the number of EnOcean devices learned. 974 1213 elseif #g_newDevices > 0 then 975 luup.variable_set( SID.ENOCEAN, " NumDevices", g_numDevices, g_parentDevice )1214 luup.variable_set( SID.ENOCEAN, "MaxID", g_maxId, g_parentDevice ) 976 1215 local ptr = luup.chdev.start( g_parentDevice ) 977 1216 for i, v in ipairs( g_newDevices ) do … … 982 1221 -- We exited Learn Mode and no new device was learned. Display a message and do nothing. 983 1222 else 984 ShowSysMessage( "Exited Learn Mode. No new device was learned." ) 985 end 1223 ShowSysMessage( "Exited Learn Mode. No new device was learned" ) 1224 end 1225 end 1226 1227 1228 function TeachIn (stage, pinCode) 1229 g_teachInStage = tonumber( stage, 10 ) or TEACHIN_STAGES.NONE 1230 1231 log( "(EnOceanPlugin::TeachIn) Stage ".. g_teachInStage .." (".. TEACHIN_STAGE_STRINGS[g_teachInStage] ..")" ) 1232 luup.variable_set( SID.ENOCEAN, "TeachInStage", g_teachInStage, g_parentDevice ) 1233 ShowSysMessage( "Teach-in stage ".. g_teachInStage .."/".. NUM_TEACHIN_STAGES, SYS_MESSAGE_TYPES.BUSY, true ) 1234 1235 ------------------------------------------------------------------------------------------------ 1236 -- Stage 1: Send 'Unlock' remote management control command (RMCC) 1237 ------------------------------------------------------------------------------------------------ 1238 if g_teachInStage == TEACHIN_STAGES.SEND_UNLOCK_RMCC then 1239 1240 pinCode = tonumber( pinCode ) or 0 1241 if pinCode == 0 then 1242 luup.variable_set( SID.ENOCEAN, "TeachInStage", TEACHIN_STAGES.NONE, g_parentDevice ) 1243 ShowSysMessage( "ERROR: missing or invalid PIN code", SYS_MESSAGE_TYPES.ERROR ) 1244 return 1245 end 1246 -- Save the PIN code to be used for the 'Lock' command. 1247 luup.variable_set( SID.ENOCEAN, "PinCode", pinCode, g_parentDevice ) 1248 ------------------------------------------------------------------------ 1249 -- Compose the 'Unlock' command. 1250 ------------------------------------------------------------------------ 1251 local data = {} 1252 -- RORG - 1 byte 1253 table.insert( data, TELEGRAM_TYPES["ADT"] ) 1254 -- Encapsulate a SYS_EX telegram - 9 bytes 1255 AppendArray( data, CreateSysExTelegram( RMCC.UNLOCK, pinCode )) 1256 -- Destination ID - 4 bytes 1257 AppendArray( data, { 0xFF, 0xFF, 0xFF, 0xFF } ) 1258 -- Sender ID - 4 bytes 1259 AppendArray( data, GetBaseIdArray() ) 1260 -- Status - 1 byte 1261 table.insert( data, 0xF ) 1262 -- Compose and send the packet. 1263 AddToSendQueue( PACKET_TYPES.RADIO, data ) 1264 -- Wait a few seconds before moving on to the next stage. 1265 log( "(EnOceanPlugin::TeachIn) 'Unlock' command added in the send queue,".. 1266 " wait ".. DELAYS.TEACHIN_STAGES .." seconds and move to the next stage" ) 1267 luup.call_delay( "TeachIn", DELAYS.TEACHIN_STAGES, TEACHIN_STAGES.SEND_QUERY_ID_RMCC ) 1268 ------------------------------------------------------------------------------------------------ 1269 -- Stage 2: Send 'Query ID' RMCC 1270 ------------------------------------------------------------------------------------------------ 1271 elseif g_teachInStage == TEACHIN_STAGES.SEND_QUERY_ID_RMCC then 1272 1273 local data = {} 1274 -- RORG - 1 byte 1275 table.insert( data, TELEGRAM_TYPES["ADT"] ) 1276 -- Encapsulate a SYS_EX telegram - 9 bytes 1277 AppendArray( data, CreateSysExTelegram( RMCC.QUERY_ID, 0 )) 1278 -- Destination ID - 4 bytes 1279 AppendArray( data, { 0xFF, 0xFF, 0xFF, 0xFF } ) 1280 -- Sender ID - 4 bytes 1281 AppendArray( data, GetBaseIdArray() ) 1282 -- Status - 1 byte 1283 table.insert( data, 0xF ) 1284 -- Compose and send the packet. 1285 AddToSendQueue( PACKET_TYPES.RADIO, data ) 1286 -------------------------------------------------------------------------------------------- 1287 -- Stage 3: Receive the Query ID answer and create the device. 1288 -------------------------------------------------------------------------------------------- 1289 g_teachInStage = TEACHIN_STAGES.RECV_QUERY_ID_ANSWER 1290 luup.variable_set( SID.ENOCEAN, "TeachInStage", g_teachInStage, g_parentDevice ) 1291 ShowSysMessage( "Teach-in stage ".. g_teachInStage .."/".. NUM_TEACHIN_STAGES, SYS_MESSAGE_TYPES.BUSY, true ) 1292 log( "(EnOceanPlugin::TeachIn) 'Query ID' command added in the send queue, wait for answer" ) 1293 ------------------------------------------------------------------------------------------------ 1294 -- Stage 4: Send 'Enter learn mode' remote procedure call (RPC) 1295 ------------------------------------------------------------------------------------------------ 1296 elseif g_teachInStage == TEACHIN_STAGES.SEND_START_LEARN_RPC then 1297 1298 local data = {} 1299 -- RORG - 1 byte 1300 table.insert( data, TELEGRAM_TYPES["ADT"] ) 1301 -- Encapsulate a SYS_EX telegram - 9 bytes 1302 -- Meaning of 1: EEP=0, Flag=1 (start learn) 1303 AppendArray( data, CreateSysExTelegram( RPC.REMOTE_LEARN, 1 )) 1304 -- Destination ID - 4 bytes 1305 AppendArray( data, { 0xFF, 0xFF, 0xFF, 0xFF } ) 1306 -- Sender ID - 4 bytes 1307 AppendArray( data, GetBaseIdArray() ) 1308 -- Status - 1 byte 1309 table.insert( data, 0xF ) 1310 -- Compose and send the packet. 1311 AddToSendQueue( PACKET_TYPES.RADIO, data ) 1312 -- Wait a few seconds before moving on to the next stage. 1313 log( "(EnOceanPlugin::TeachIn) 'Remote learn' command added in the send queue,".. 1314 " wait ".. DELAYS.TEACHIN_STAGES .." seconds and move to the next stage" ) 1315 luup.call_delay( "TeachIn", DELAYS.TEACHIN_STAGES, TEACHIN_STAGES.SEND_TEACHIN_TELEGRAM ) 1316 ------------------------------------------------------------------------------------------------ 1317 -- Stage 5: Send Teach-in RPS telegram 1318 ------------------------------------------------------------------------------------------------ 1319 elseif g_teachInStage == TEACHIN_STAGES.SEND_TEACHIN_TELEGRAM then 1320 1321 local data = {} 1322 -- RORG - 1 byte 1323 table.insert( data, TELEGRAM_TYPES["RPS"] ) 1324 -- Data byte 1325 -- Meaning of 0x10: button AI pressed, no 2nd action 1326 table.insert( data, 0x10 ) 1327 -- Sender ID - 4 bytes 1328 AppendArray( data, GetBaseIdArray() ) 1329 -- Status - 1 byte 1330 -- Meaning of 0x30: T21=1, NU=1 1331 table.insert( data, 0x30 ) 1332 -- Compose and send the packet. 1333 AddToSendQueue( PACKET_TYPES.RADIO, data ) 1334 -- Wait a few seconds before moving on to the next stage. 1335 log( "(EnOceanPlugin::TeachIn) RPS telegram for teach-in added in the send queue,".. 1336 " wait ".. DELAYS.TEACHIN_STAGES .." seconds and move to the next stage" ) 1337 luup.call_delay( "TeachIn", DELAYS.TEACHIN_STAGES, TEACHIN_STAGES.SEND_STOP_LEARN_RPC ) 1338 ------------------------------------------------------------------------------------------------ 1339 -- Stage 6: Send 'Stop learn' RPC 1340 ------------------------------------------------------------------------------------------------ 1341 elseif g_teachInStage == TEACHIN_STAGES.SEND_STOP_LEARN_RPC then 1342 1343 local data = {} 1344 -- RORG - 1 byte 1345 table.insert( data, TELEGRAM_TYPES["ADT"] ) 1346 -- Encapsulate a SYS_EX telegram - 9 bytes 1347 -- Meaning of 1: EEP=0, Flag=3 (stop learn) 1348 AppendArray( data, CreateSysExTelegram( RPC.REMOTE_LEARN, 3 )) 1349 -- Destination ID - 4 bytes 1350 AppendArray( data, { 0xFF, 0xFF, 0xFF, 0xFF } ) 1351 -- Sender ID - 4 bytes 1352 AppendArray( data, GetBaseIdArray() ) 1353 -- Status - 1 byte 1354 table.insert( data, 0xF ) 1355 -- Compose and send the packet. 1356 AddToSendQueue( PACKET_TYPES.RADIO, data ) 1357 -- Wait a few seconds before moving on to the next stage. 1358 log( "(EnOceanPlugin::TeachIn) 'Remote learn' command added in the send queue,".. 1359 " wait ".. DELAYS.TEACHIN_STAGES .." seconds and move to the next stage" ) 1360 luup.call_delay( "TeachIn", DELAYS.TEACHIN_STAGES, TEACHIN_STAGES.SEND_LOCK_RMCC ) 1361 ------------------------------------------------------------------------------------------------ 1362 -- Stage 7: Send 'Lock' RMCC 1363 ------------------------------------------------------------------------------------------------ 1364 elseif g_teachInStage == TEACHIN_STAGES.SEND_LOCK_RMCC then 1365 1366 local pinCode = luup.variable_get( SID.ENOCEAN, "PinCode", g_parentDevice ) 1367 pinCode = tonumber( pinCode, 10 ) or 0 1368 if pinCode == 0 then 1369 luup.variable_set( SID.ENOCEAN, "TeachInStage", TEACHIN_STAGES.NONE, g_parentDevice ) 1370 ShowSysMessage( "ERROR: missing or invalid PIN code", SYS_MESSAGE_TYPES.ERROR ) 1371 return 1372 end 1373 -- Clear the PinCode variable. 1374 luup.variable_set( SID.ENOCEAN, "PinCode", "", g_parentDevice ) 1375 ------------------------------------------------------------------------ 1376 -- Compose the 'Lock' command. 1377 ------------------------------------------------------------------------ 1378 local data = {} 1379 -- RORG - 1 byte 1380 table.insert( data, TELEGRAM_TYPES["ADT"] ) 1381 -- Encapsulate a SYS_EX telegram - 9 bytes 1382 AppendArray( data, CreateSysExTelegram( RMCC.LOCK, pinCode )) 1383 -- Destination ID - 4 bytes 1384 AppendArray( data, { 0xFF, 0xFF, 0xFF, 0xFF } ) 1385 -- Sender ID - 4 bytes 1386 AppendArray( data, GetBaseIdArray() ) 1387 -- Status - 1 byte 1388 table.insert( data, 0xF ) 1389 -- Compose and send the packet. 1390 AddToSendQueue( PACKET_TYPES.RADIO, data ) 1391 -- This was the last stage. 1392 log( "(EnOceanPlugin::TeachIn) 'Lock' command added in the send queue. Set stage to NONE" ) 1393 luup.variable_set( SID.ENOCEAN, "TeachInStage", TEACHIN_STAGES.NONE, g_parentDevice ) 1394 ------------------------------------------------------------------------------------------------ 1395 else 1396 log( "(EnOceanPlugin::TeachIn) ERROR: invalid stage" ) 1397 luup.variable_set( SID.ENOCEAN, "TeachInStage", stage, g_parentDevice ) 1398 luup.variable_set( SID.ENOCEAN, "PinCode", "", g_parentDevice ) 1399 end 1400 end 1401 1402 1403 -- Send a RPS radio telegram (F6-xx profile). 1404 function SetTarget (newTargetValue, device) 1405 -- Get the destination ID. 1406 local destId = luup.devices[device].id:match("^%x%x%-%x%x%-%x%x%-%x%x") 1407 if not destId then 1408 ShowSysMessage( "ERROR: invalid destination ID", SYS_MESSAGE_TYPES.ERROR ) 1409 return 1410 end 1411 local destIdArray = SplitString( destId, '-' ) 1412 -- Convert the elements of the destIdArray to numbers. 1413 for i = 1, #destIdArray do 1414 destIdArray[i] = tonumber( destIdArray[i], 16 ) or 0 1415 end 1416 1417 ---------------------------------------------------------------------------- 1418 -- Compose the data. 1419 ---------------------------------------------------------------------------- 1420 local data = {} 1421 -- RORG - 1 byte 1422 table.insert( data, TELEGRAM_TYPES["RPS"] ) 1423 -- Data byte 1424 if tostring( newTargetValue ) == "1" then 1425 -- 0x10: button AI pressed, no 2nd action 1426 table.insert( data, 0x10 ) 1427 else 1428 -- 0x10: button A0 pressed, no 2nd action 1429 table.insert( data, 0x30 ) 1430 end 1431 -- Sender ID - 4 bytes 1432 AppendArray( data, GetBaseIdArray() ) 1433 -- Status - 1 byte 1434 -- Meaning of 0x30: T21=1, NU=1 1435 table.insert( data, 0x30 ) 1436 ---------------------------------------------------------------------------- 1437 -- Compose the optional data. 1438 ---------------------------------------------------------------------------- 1439 local optData = {} 1440 -- The sub-telegram number (apparently it should be 3) 1441 table.insert( optData, 0x3 ) 1442 -- The destination ID 1443 AppendArray( optData, destIdArray ) 1444 -- The dBm 1445 table.insert( optData, 0xFF ) 1446 -- The security level 1447 table.insert( optData, 0x0 ) 1448 -- Compose and send the packet. 1449 AddToSendQueue( PACKET_TYPES.RADIO, data, optData ) 986 1450 end 987 1451 … … 990 1454 -- Startup functions 991 1455 ------------------------------------------------------------------------------------------------------------------------ 1456 1457 local function GenerateBaseId() 1458 -- Generate a random number in interval [0 - 65535]. 1459 -- 65536 is the number of possible base IDs as of current date (20-03-2013). 1460 math.randomseed( os.time() ) 1461 local multiplier = math.random( (MAX_BASE_ID - MIN_BASE_ID) / BASE_ID_GAP + 1 ) - 1 1462 -- The base ID is a number between MIN_BASE_ID and MAX_BASE_ID, and a multiplier of BASE_ID_GAP. 1463 return MIN_BASE_ID + BASE_ID_GAP * multiplier 1464 end 1465 992 1466 993 1467 local function GetChildDevices() 994 1468 -- Get the number of learned EnOcean devices. 995 g_ numDevices = luup.variable_get( SID.ENOCEAN, "NumDevices", g_parentDevice )996 g_ numDevices = tonumber( g_numDevices, 10 ) or 01469 g_maxId = luup.variable_get( SID.ENOCEAN, "MaxID", g_parentDevice ) 1470 g_maxId = tonumber( g_maxId, 10 ) or 0 997 1471 -- Get a list with all our child devices. 998 1472 for k, v in pairs( luup.devices ) do … … 1033 1507 luup.variable_set( SID.ENOCEAN, "LearnMode", "0", lul_device ) 1034 1508 1509 -- Check if we have a base ID. Generate one if we don't. 1510 g_baseId = luup.variable_get( SID.ENOCEAN, "BaseID", lul_device ) or "" 1511 if g_eoDeviceId == "" then 1512 g_baseId = GenerateBaseId() 1513 luup.variable_set( SID.ENOCEAN, "BaseID", g_baseId, lul_device ) 1514 end 1515 1516 -- Get the list of the child devices. 1035 1517 GetChildDevices() 1036 1518 1037 -- Get the sender ID of the TCM300. 1038 --AddToSendQueue( PACKET_TYPES.COMMON_COMMAND, COMMON_COMMAND.CO_RD_IDBASE, "", true ) 1519 -- Get the TeachIn-in stage. 1520 local teachInStage = luup.variable_get( SID.ENOCEAN, "TeachInStage", lul_device ) 1521 teachInStage = tonumber( teachInStage, 10 ) or 0 1522 if teachInStage > 0 then 1523 if teachInStage == TEACHIN_STAGES.RECV_QUERY_ID_ANSWER then 1524 ShowSysMessage( "Teach-in stage ".. teachInStage .."/".. NUM_TEACHIN_STAGES, SYS_MESSAGE_TYPES.BUSY, true ) 1525 else 1526 TeachIn( teachInStage ) 1527 end 1528 end 1039 1529 1040 1530 debug( "(EnOceanPlugin::Init) Success: startup successful" ) -
/trunk/D_EnOceanGateway1.json
r20 r30 57 57 "left": "1", 58 58 "Label": { 59 "lang_tag": " cmd_learn_mode_on",59 "lang_tag": "button_learn_mode_on", 60 60 "text": "Learn" 61 61 }, … … 87 87 "left": "0", 88 88 "Label": { 89 "lang_tag": " cmd_learn_mode_off",89 "lang_tag": "button_learn_mode_off", 90 90 "text": "Normal" 91 91 }, … … 110 110 }, 111 111 "ControlCode": "learn_mode_off" 112 }, 113 { 114 "ControlType": "label", 115 "Label": { 116 "lang_tag": "label_pin_code", 117 "text": "PIN Code:" 118 }, 119 "Display": { 120 "Top": 105, 121 "Left": 50, 122 "Width": 75, 123 "Height": 20 124 } 125 }, 126 { 127 "ControlType": "input", 128 "ID": "pinCode", 129 "Display": { 130 "Top": 125, 131 "Left": 50, 132 "Width": 75, 133 "Height": 20 134 }, 135 "ControlCode": "teach_in_input" 136 }, 137 { 138 "ControlType": "button", 139 "Label": { 140 "lang_tag": "button_teach_in", 141 "text": "Teach-in" 142 }, 143 "Display": { 144 "Top": 125, 145 "Left": 145, 146 "Width": 75, 147 "Height": 20 148 }, 149 "Command": { 150 "Service": "urn:micasaverde-com:serviceId:EnOceanGateway1", 151 "Action": "TeachIn", 152 "Parameters": [ 153 { 154 "Name": "pinCode", 155 "ID": "pinCode" 156 } 157 ] 158 }, 159 "ControlCode": "teach_in" 112 160 } 113 161 ]
Note: See TracChangeset
for help on using the changeset viewer.