Changeset 56
- Timestamp:
- 2013-01-27 16:19:15 (12 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
TabularUnified trunk/L_Powermax.lua ¶
r55 r56 12 12 local Queue = {} 13 13 function Queue.new() 14 14 return {first = 0, last = -1} 15 15 end 16 16 17 17 function Queue.pushright(list, value) 18 19 20 18 local last = list.last + 1 19 list.last = last 20 list[last] = value 21 21 end 22 22 … … 71 71 local pmEventCnt 72 72 -- POWERLINK -- 73 local pmStarting = true 73 74 local pmDownloadCode = "VP" -- Vera Powerlink 74 75 local pmPowerlinkMode = false … … 83 84 84 85 local pmPanelInit_t = { 85 86 { "PluginVersion", PLUGIN_VERSION, false }, -- false: update 86 87 { "MotionOffDelay", pmMotionOffDelay, true }, -- true: create only 87 88 { "PluginLanguage", pmLang, true }, 88 89 { "PluginDebug", "0", true }, 89 90 { "ForceStandard", "0", true }, 90 91 { "AutoCreate", "1", true }, … … 139 140 local pmSendMsg_t = { 140 141 MSG_INIT = { string.char(0xAB, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43), nil }, 142 MSG_CLEAR = { string.char(0xAB, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43), 0xA5 }, 141 143 MSG_RESTORE = { string.char(0xAB, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43), 0xA5 }, 142 144 MSG_ENROLL = { string.char(0xAB, 0x0A, 0x00, 0x00) .. pmDownloadCode .. string.char(0x00, 0x00, 0x00, 0x00, 0x00, 0x43), nil }, … … 404 406 -- Update a system variable only if the value will change 405 407 function updateIfNeeded(sid, var, newVal, id, createOnly) 406 408 local curVal = luup.variable_get(sid, var, id) 407 409 local valUpdate = (curVal == nil) or ((createOnly ~= true) and (curVal ~= tostring(newVal)) or false) 408 409 410 411 412 410 if (valUpdate == true) then 411 luup.variable_set(sid, var, newVal, id) 412 return true 413 end 414 return false 413 415 end 414 416 415 417 -- Find child device (Taken from GE Caddx Panel but comes originally from guessed) 416 418 function findChild(deviceId, label) 417 418 419 420 421 419 for k, v in pairs(luup.devices) do 420 if (v.device_num_parent == deviceId and v.id == label) then 421 return k 422 end 423 end 422 424 end 423 425 … … 445 447 -- Control debug to /var/log/cmh/LuaUPnP.log (taken from GE Caddx Panel) 446 448 function debug(s) 447 448 449 449 if (pmLogDebug) then 450 luup.log("POWERMAX: " .. s) 451 end 450 452 end 451 453 … … 512 514 -- Calculate CRC for PDU send to PowerMax 513 515 function pmCalcCRC(pdu) 514 515 516 517 518 519 520 521 522 523 524 525 516 local checksum = 0 517 local newPDU 518 local len = string.len(pdu) 519 520 assert(len >= 2) 521 for i = 2, (len - 2) do 522 checksum = checksum + tonumber(string.byte(pdu, i)) 523 end 524 checksum = 0xFF - (checksum % 0xFF) 525 if (checksum == 0xFF) then checksum = 0x00 end 526 newPDU = string.sub(pdu, 1, len - 2) .. string.char(checksum, 0x0A) 527 return newPDU 526 528 end 527 529 528 530 -- Check CRC in PDU received from PowerMax 529 531 function pmCheckCRC(pdu) 530 531 532 533 534 535 536 537 538 532 local checksum = 0 533 local len = string.len(pdu) 534 535 assert(len >= 2) 536 for i = 2, (len - 2) do 537 checksum = checksum + tonumber(string.byte(pdu, i)) 538 end 539 checksum = 0xFF - (checksum % 0xFF) 540 if (checksum == 0xFF) then checksum = 0x00 end 539 541 local got = tonumber(string.byte(pdu, len - 1)) 540 542 if (checksum == got) then … … 553 555 -- Convert PDU bytes to a string for logging 554 556 function pmPduToString(pdu) 555 556 557 558 559 557 local PDUstr = "" 558 for i = 1, string.len(pdu) do 559 PDUstr = PDUstr .. string.format("%02X ", string.byte(pdu, i)) 560 end 561 return PDUstr 560 562 end 561 563 … … 630 632 -- Check if a tripped motion sensor has timed out 631 633 for i = 1, 30 do 632 634 local sensor = pmSensorDev_t[i] 633 635 if (sensor ~= nil) then 634 636 local child = sensor['id'] … … 639 641 end 640 642 end 641 643 end 642 644 -- Check Siren 643 645 if (pmSirenActive ~= nil) and (now > pmSirenActive) then … … 647 649 end 648 650 end 649 651 luup.call_timer("pmIntervalCheck", 1, "20s", "", "") 650 652 end 651 653 … … 687 689 -- Initialises the panel, handlers etc. 688 690 function pmStartup(lul_device) 689 691 pmPanelDev = lul_device 690 692 691 693 local exceptions = luup.variable_get(PANEL_SID, "CommExceptions", pmPanelDev) … … 705 707 end 706 708 707 709 luup.log("POWERMAX: starting ... device #" .. tostring(pmPanelDev), 10) 708 710 709 711 for i = 1, #pmPanelInit_t do 710 712 updateIfNeeded(PANEL_SID, pmPanelInit_t[i][1], pmPanelInit_t[i][2], pmPanelDev, pmPanelInit_t[i][3]) 711 713 end 712 714 … … 718 720 pmMotionOffDelay = tonumber(luup.variable_get(PANEL_SID, "MotionOffDelay", pmPanelDev), 10) 719 721 720 721 722 723 722 if (luup.io.is_connected(pmPanelDev) == false) then 723 pmMessage("Please select the Serial Port for the PowerMax", 2) 724 return false 725 end 724 726 725 727 -- Register all message handlers … … 740 742 end 741 743 742 pmSendMessage("MSG_ INIT")744 pmSendMessage("MSG_CLEAR") 743 745 if (forceStandard == false) then 744 746 pmStartDownload() … … 748 750 end 749 751 750 752 luup.call_timer("pmIntervalCheck", 1, "20s", "", "") 751 753 end 752 754 753 755 -- pmGetPin: Convert a PIN given as string in the PIN PDU format as used in messages to powermax 754 756 function pmGetPin(pin) 755 756 757 757 local pinPDU 758 759 if (pin == "") or (pin == nil) or (string.len(pin) ~= 4) then 758 760 if (pmPowerlinkMode == true) then 759 761 return pmPincode_t[1], true … … 763 765 return pinPDU, false 764 766 end 765 766 767 768 767 end 768 pinPDU = string.char(tonumber(string.sub(pin, 1, 2), 16), tonumber(string.sub(pin, 3, 4), 16)) 769 770 return pinPDU, false 769 771 end 770 772 … … 969 971 end 970 972 971 973 pmMessage("Adding zone devices", 4) 972 974 -- Use variables if AutoCreate is false or not in Powerlink mode, otherwise use what we read in the settings 973 975 local doorZones = doorZoneStr … … 1020 1022 end 1021 1023 end 1022 1024 1023 1025 -- Add PGM and X10 devices 1024 1025 1026 1026 for i = 0, 15 do 1027 if (i == 0) then 1028 if (string.find(devices, "PGM")) then 1027 1029 pmCreateDevice(childDevices, "PGM", "Switch", "PGM") 1028 1029 1030 1030 end 1031 else 1032 s = string.format("X%02d", i) 1031 1033 local pos = string.find(devices, s) 1032 1034 if (pos ~= nil) then 1033 1035 local devInit 1034 1036 if (string.sub(devices, pos + 3, pos + 3) == "d") then … … 1037 1039 pmCreateDevice(childDevices, s, "Switch", x10_t[i], s) 1038 1040 end 1039 1040 1041 1042 1041 end 1042 end 1043 local x10_device = findChild(pmPanelDev, s) 1044 if (x10_device ~= nil) then 1043 1045 x10_dev_nr = string.format("%04x", 2 ^ i) 1044 1046 updateIfNeeded(PANEL_SID, "X10DeviceNr", x10_dev_nr, x10_device, true) 1045 1046 1047 end 1048 end 1047 1049 1048 1050 -- Add sirens … … 1073 1075 luup.chdev.sync(pmPanelDev, childDevices) 1074 1076 pmSendMessage("MSG_STATUS") 1077 pmStarting = false 1075 1078 pmMessage("Ready for use", 2) 1076 1079 end … … 1104 1107 -- pmStartDownload (exposed as called with delay) 1105 1108 function pmStartDownload(stuff) 1106 pmSendMessage("MSG_DOWNLOAD") -- If we get a NACK, then the download code is not valid (not enrolled) 1109 pmStarting = true 1110 pmSendMessage("MSG_DOWNLOAD") -- If we get a NACK, then the download code is not valid (not enrolled) 1107 1111 pmLastKeepAlive = os.time() 1108 1112 end … … 1148 1152 end 1149 1153 1150 1154 if (timeout == true) or (pmExpectedResponse == "") then -- we are ready to send 1151 1155 -- Always expect an ACK (ACK send messages don't pass through here) 1152 1156 pmExpectedResponse = string.char(0x02) .. response … … 1161 1165 end 1162 1166 pmWaitingForResponse = os.time() 1163 1164 1165 1166 1167 else -- queue message 1168 Queue.pushright(pmOutgoingQueue, { outPdu, response }) 1169 end 1170 return true 1167 1171 end 1168 1172 … … 1173 1177 local success 1174 1178 1175 1176 1177 1178 1179 if (pduLen == 0) then 1180 if (string.byte(data) == 0x0D) then -- preamble 1181 debug("start of new PDU detected") 1182 pmIncomingPdu = data 1179 1183 end 1180 1184 elseif (pduLen == 1) then … … 1183 1187 debug(string.format("Message %02X; pmIncomingPduLen = %d", string.byte(data), pmIncomingPduLen)) 1184 1188 pmIncomingPdu = pmIncomingPdu .. data 1185 1186 1187 1189 elseif (pmIncomingPduLen == 0 and string.byte(data) == 0x0A) or (pduLen + 1 == pmIncomingPduLen) then -- postamble 1190 pmIncomingPdu = pmIncomingPdu .. data 1191 if (pmCheckCRC(pmIncomingPdu) == true) then 1188 1192 pmLastPDU = pmIncomingPdu 1189 1193 local PDUstr = pmPduToString(pmIncomingPdu) 1190 1194 local msgType = string.byte(pmIncomingPdu, 2) 1191 1195 local msgType_t = pmReceiveMsg_t[msgType] 1192 1193 1196 debug(string.format("PDU received %s", PDUstr)) 1197 pmLogPdu(PDUstr, "<-PM-") 1194 1198 pmWaitingForResponse = os.time() 1195 1199 … … 1235 1239 if (pmExpectedResponse == "") then 1236 1240 pmSendMessage(nil) 1237 1238 1241 end 1242 pmIncomingPdu = "" 1239 1243 else -- CRC check failed 1240 1244 if (pmIncomingPduLen > 0) then … … 1252 1256 end 1253 1257 end 1254 1255 1256 1257 1258 else 1259 pmIncomingPdu = pmIncomingPdu .. data 1260 end 1261 return true 1258 1262 end 1259 1263 … … 1290 1294 luup.variable_set(PANEL_SID, "PowerlinkMode", "Standard", pmPanelDev) 1291 1295 pmMessage("Vera Powerlink not enrolled.", 2) 1292 end 1293 pmExpectedResponse = string.char(0x08) .. string.sub(pmExpectedResponse, 2) 1296 pmProcessSettings() 1297 end 1298 pmExpectedResponse = string.char(0x08) 1294 1299 return true 1295 1300 end … … 1490 1495 updateIfNeeded(PARTITION_SID, "VendorStatusCode", string.format("%02X%02X", sysStatus, sysFlags), pmPartitionDev_t[1]) 1491 1496 updateIfNeeded(PARTITION_SID, "VendorStatusData", s, pmPartitionDev_t[1]) 1492 1493 1497 updateIfNeeded(PARTITION_SID, "ArmMode", armMode, pmPartitionDev_t[1]) 1498 updateIfNeeded(PARTITION_SID, "ArmModeNum", armModeNum, pmPartitionDev_t[1]) 1494 1499 updateIfNeeded(PARTITION_SID, "DetailedArmMode", (pmDetailedArmMode_t[sysStatus + 1] or "UNKNOWN"), pmPartitionDev_t[1]) 1495 1500 if (pmSensorShowBypass == false) then … … 1564 1569 if (subType == 0x03) then -- keepalive message 1565 1570 pmSendAck(0x02) 1571 if (pmStarting == false) and (pmPowerlinkMode == false) then 1572 debug("Got alive message while not in Powerlink mode; starting download") 1573 pmStartDownload() 1574 end 1566 1575 pmLastKeepAlive = os.time() 1567 1576 pmPowerlinkRetry = 0 … … 1575 1584 pmSendAck(0x02) 1576 1585 elseif (subType == 0x0A and string.byte(pmIncomingPdu, 5) == 0x01) then 1577 debug("Enrolling PowerLink") 1586 debug("Enrolling Powerlink") 1587 pmWaitingForResponse = 0 -- make sure the message is sent right away; generate a timeout 1578 1588 pmSendMessage("MSG_ENROLL") 1579 1589 pmExpectedResponse = string.char(0xAB) .. pmExpectedResponse … … 1613 1623 -- RequestQuickArmMode 1614 1624 function RequestQuickArmMode(DetailedArmMode) 1615 1625 debug("RequestQuickArmMode " .. (state or "N/A")) 1616 1626 1617 1627 if (DetailedArmMode == "Stay") or (DetailedArmMode == "Armed") then … … 1623 1633 else 1624 1634 pmMessage("RequestQuickArmMode only possible for Arm or Stay.", 2) 1625 1635 end 1626 1636 end 1627 1637 … … 1652 1662 -- SetTarget 1653 1663 function SetTarget(device, DeviceID, newTargetValue) 1654 1655 1656 1664 -- Two ways to call this function 1665 -- 1. Using lul_device (don't set 'DeviceID') 1666 -- 2. Setting 'DeviceID' to the X10 nr (e.g. 7 for X07) 1657 1667 if (DeviceID ~= nil) then 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 local s = (DeviceID == 0) and "PGM" or string.format("X%02d", DeviceID) 1669 local child = findChild(pmPanelDev, s) 1670 if (child == nil) then 1671 debug(string.format("Unable to locate device X%02d", DeviceID)) 1672 return false 1673 else 1674 device = child 1675 end 1676 end 1677 local device_nr = luup.variable_get(PANEL_SID, "X10DeviceNr", device) 1668 1678 1669 1670 1679 if (device_nr ~= nil) then 1680 local onoff = newTargetValue or 0 1671 1681 local x10dev = string.char(tonumber(string.sub(device_nr, 3, 4),16)) .. string.char(tonumber(string.sub(device_nr, 1, 2),16)) 1672 1673 1682 debug("Turn X10 device on/off (" .. onoff .. "/" .. device_nr ..")") 1683 luup.variable_set(X10DEVICE_SID, "Target", onoff, device) 1674 1684 pmSendMessage("MSG_X10PGM", { cmd = string.char(onoff), device = x10dev }) 1675 1676 1685 -- If it went OK, we will receive an A5 status from the PowerMax, which will update the X10 status 1686 end 1677 1687 end 1678 1688 … … 1689 1699 -- SetLoadLevelTarget 1690 1700 function SetLoadLevelTarget(device, newLoadlevelTarget) 1691 1701 local device_nr = luup.variable_get(PANEL_SID, "X10DeviceNr", device) 1692 1702 1693 1703 if (device_nr ~= nil) then 1694 1704 local current = luup.variable_get(X10DEVICEDIM_SID, "LoadLevelTarget", device) or 0 1695 1705 debug("Dimming X10 device (" .. newLoadlevelTarget .. "/" .. device_nr ..")") 1696 1706 1697 1707 local cmd = nil … … 1734 1744 -- ToggleState 1735 1745 function ToggleState(device) 1736 1737 1738 1739 1746 local lul_arguments = {} 1747 local onoff = luup.variable_get(X10DEVICE_SID, "Status", device) or 0 1748 lul_arguments["newTargetValue"] = 1 - onoff 1749 lul_resultcode, lul_resultstring, lul_job, lul_returnarguments = luup.call_action(X10DEVICE_SID, "SetTarget", lul_arguments, device) 1740 1750 end 1741 1751 1742 1752 -- GetEventLog 1743 1753 function GetEventLog(device, PINCode) 1744 1745 1754 debug("GetEventLog") 1755 luup.variable_set(PANEL_SID, "EventLog", "", device) 1746 1756 pmSendMessage("MSG_EVENTLOG", { pin = pmGetPin(PINCode) }) 1747 1757 end
Note: See TracChangeset
for help on using the changeset viewer.