Is anyone else aggravated with the DSC plugin? It just seems bulky and like it’s trying to do way more than I need. I only wanted to be able to say Alexa, goodbye
and have my House Mode switch to Away - triggering scenes to adjust lights, A/C, and set the alarm. Then when I get home, I say Alexa, <previously unspoken sentence in human history>
and the House Mode switches to Home - again triggering scenes to adjust lights, A/C, and disarming the alarm.
After days of fighting with the DSC plugin and even trying to find a way around the encrypted files just so I could see how it works, I gave up and found a copy of the Envisalink TIP Guide. The module I came up with has been reliable and so I decided to share in the hopes that someone else may find it does what they want without the need for the plugin:
You call it from a scene, passing parameters as a table. It has only one public function go()
which requires only one argument provided you have hardcoded your defaults into the evl.lua
file:
local evl = require "evl"
evl.go( { mode="away", } )
Here’s the module:
module("evl", package.seeall)
local luup = _G["luup"] or { log = print } -- for running module off-vera
local _debug = false
-- Resolve a hostname
--
local function getipaddr (s)
local socket = require "socket"
local ipaddr = socket.dns.toip(s)
return ipaddr
end
-- Calculate the checksum of a string and return the
-- lower 8 bits as a ASCII hex uppercase string
--
local function checksum (s)
local c = 0
for i = 1, #s do
c = c + string.byte(s, i)
end
return(string.upper(string.sub(string.format("%x", c), -2)))
end
-- Format and send a command to the EVL
--
local function send (conn, cmd, dat)
local s = cmd .. (dat or "")
if _debug then luup.log("EVL < " .. s) end
return conn:send(s .. checksum(s) .. "\r\n")
end
-- Read data from EVL, discarding the checksum,
-- then return a table with command and data
--
local function recv (conn)
local s, err = conn:receive()
if err then return nil, err end
if _debug then luup.log("EVL : " .. s) end
return { cmd = string.sub(s, 1, 3), dat = string.sub(s, 4, -3) }
end
-- Send command and check for acknowledge reply from EVL
--
local function sendack (conn, cmd, dat)
if not send(conn, cmd, dat) then return nil end
local t, err = recv(conn)
if err then return nil, err end
return (t.cmd == "500") and (t.dat == cmd)
end
-- Do the login loop and return nil on success
--
local function login (conn, args)
repeat
local t, err = recv(conn)
if err then return "connection " .. err end
if t.cmd == "505" then
if t.dat == "0" then return "password \"" .. args.evlpw .. "\" rejected"
elseif t.dat == "1" then return nil
elseif t.dat == "2" then return "login timeout"
elseif t.dat == "3" then sendack(conn, "005", args.evlpw)
end
elseif t.cmd == "502" then
return "evl system error " .. t.dat
end
until nil
end
-- Open session with EVL and set requested mode
--
-- Attempts to connect and login to the EVL using supplied args table.
-- Any args not passed will be set to defaults.
--
-- Notes: evlpw is default 'user' on EVL3/4 and can be changed in the
-- EVL web interface. Max length is 6 ASCII characters on the
-- EVL3 and 10 on the EVL4.
--
-- If you haven't set a static IP for the EVL (either via it's
-- web interface or your router's DHCP), and it is on the same
-- subnet as your Vera, then "envisalink" should resolve OK.
--
-- Arm codes used here do not require an alarm code and report
-- as "Special Closing" from the panel
--
-- I have only tested this on an EVL4 but according to the TIP
-- guide, everything should be the same
--
function go (args)
args = args or {}
local modes = { away="030", stay="031", zedl="032", disarm="040", }
-- You can hardcode your defaults here to
-- avoid passing them from your scene
--
args.acode = args.acode or "1234"
args.evlpw = args.evlpw or "user"
args.host = args.host or getipaddr("envisalink")
args.part = args.part or "1"
args.port = args.port or 4025
args.timeout = args.timeout or 0.5
if not args.host then
luup.log("EVL: no host specified or failed to resolve");
return false
elseif not args.mode then
luup.log("EVL: mode not specified")
return false
elseif not modes[args.mode] then
luup.log("EVL: invalid mode specified: " .. args.mode)
return false
end
-- Establish and verify a connection
local sock = require "socket"
local conn = assert(sock.tcp())
conn:settimeout(args.timeout)
local _, err = conn:connect(args.host, args.port)
if err then
luup.log("EVL: failed to connect to " .. args.host .. ":" .. args.port .. " - " .. err)
return false
end
-- Login
err = login(conn, args)
if err then
luup.log("EVL: failed to login: " .. err)
return false
end
-- If requesting disarm, append alarm code to partition number
local dat = args.part .. (args.mode=="disarm" and args.acode or "")
if sendack(conn, modes[args.mode], dat) then
luup.log("EVL: \"" .. args.mode .. "\" command acknowledged")
else
luup.log("EVL: \"" .. args.mode .. "\" command not acknowledged")
end
conn:close()
end