Skip to content

Commit

Permalink
Streamline pulse logic; detect duplicate masters
Browse files Browse the repository at this point in the history
  • Loading branch information
toggledbits committed Jul 6, 2020
1 parent d95b6d6 commit 42a4b2d
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 37 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ NOTE TO OPENLUUP USERS: All current versions of Reactor REQUIRE openLuup 2020.04

**DEPRECATION NOTICE:** The expression functions `arraypush()`, `arraypop()`, `arrayunshift()` and `arrayshift()` have been made first-class functions in the LuaXP module under the names `push()`, `pop()`, `unshift()` and `shift()` respectively. The `array...()` versions are now deprecated, and will be removed from a future release. Please convert to the new functions, which for all practical purposes are identical (so you just need to change the names in your expressions and it's done).

## Version 3.7 (20185)
## Version 3.7 (20186)

* Enhancement: Tools tab now has a "Device Repair" section that appears when missing/suspect devices are detected. This tool should be especially handy for migrating ReactorSensors between systems, or after device replacements, etc.
* Added `RepairDevice` action (in master service ID) to repair broken device IDs on any device.
* Detect duplicate master devices (prevent startup of duplicates).
* Fix an issue with backup/restore not marking success on ALTUI (apparent ALTUI oddness, same as 20049-02).
* Hotfix 20185-01: Fix crash generating Logic Summary when activity delay uses variable reference.
* Hotfix 20169-01: (openLuup only) fix error logged when cleaning state variables on RS (non-critical).
Expand Down
2 changes: 1 addition & 1 deletion J_ReactorSensor_UI7.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ var ReactorSensor = (function(api, $) {
/* unique identifier for this plugin... */
var uuid = '21b5725a-6dcd-11e8-8342-74d4351650de';

var pluginVersion = '3.7-20185';
var pluginVersion = '3.7-20186';

var DEVINFO_MINSERIAL = 482;

Expand Down
2 changes: 1 addition & 1 deletion J_Reactor_UI7.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ var Reactor = (function(api, $) {
/* unique identifier for this plugin... */
var uuid = '72acc6ea-f24d-11e8-bd87-74d4351650de';

var pluginVersion = '3.7-20185';
var pluginVersion = '3.7-20186';

var _UIVERSION = 20130; /* must coincide with Lua core */

Expand Down
85 changes: 51 additions & 34 deletions L_Reactor.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ local debugMode = false

local _PLUGIN_ID = 9086
local _PLUGIN_NAME = "Reactor"
local _PLUGIN_VERSION = "3.7-20185"
local _PLUGIN_VERSION = "3.7-20186"
local _PLUGIN_URL = "https://www.toggledbits.com/reactor"
local _DOC_URL = "https://www.toggledbits.com/static/reactor/docs/3.6/"

Expand Down Expand Up @@ -3847,7 +3847,8 @@ local function processCondition( cond, grp, cdata, tdev )

-- Preserve the result of the condition eval. We are edge-triggered,
-- so only save changes, with timestamp.
if state ~= cs.laststate then
cs.statechanged = state ~= cs.laststate
if cs.statechanged then
D("processCondition() recording %1 state change", cond.id)
-- ??? At certain times, Vera gets a time that is in the future, or so it appears. It looks like the TZ offset isn't applied, randomly.
-- Maybe if call is during ntp update, don't know. Investigating... This log message helps detection and analysis.
Expand Down Expand Up @@ -4003,39 +4004,49 @@ local function processCondition( cond, grp, cdata, tdev )

-- Output control below this comment; restrictions above.

-- Pulsed output (timed reset). Pulse is held even if underlying drops out.
-- Pulsed output (timed reset). Pulse is held even if underlying test drops false.
if ( condopt.pulsetime or 0 ) > 0 then
D("processCondition() pulse time %1 state %2 evalstate %3", condopt.pulsetime, state, cs.evalstate)
local pulseend
if state and not cs.evalstate then
-- Starting new pulse... or are we...
pulseend = cs.pulseuntil or ( now + condopt.pulsetime )
if not cs.pulseuntil then cs.pulsecount = 1 end
if state and not cs.pulseuntil then
-- No pulse train running, but there should be...
D("processCondition() starting new pulse")
cs.pulseuntil = now + condopt.pulsetime
cs.pulsecount = 1
end
if not cs.pulseuntil then
-- Not pulsing
D("processCondition() not pulsing")
cs.pulsecount = nil
else
-- Continuing from last true edge (even if state false)
pulseend = ( (cs.evaledge or {}).t or 0 ) + condopt.pulsetime
end
D("processCondition() pulseend is %1 (pulsing %2), cs.pulseuntil is %3", pulseend, now < pulseend, cs.pulseuntil)
if now < pulseend then
D("processCondition() continue pulse until %1", pulseend)
state = true -- hold up unconditionally
cs.pulseuntil = pulseend
scheduleDelay( tostring(tdev), pulseend - now )
addEvent{ dev=tdev,
msg="%(cname)s timing output pulse, %(delay)s seconds remain",
cname=(cond.type or "group")=="group" and ("Group "..(cond.name or cond.id)) or ("Condition "..cond.id),
cond=cond.id, delay=pulseend-now }
else
-- Passed, but keep pulseuntil around until (real) test state goes false
D("processCondition() pulse off phase (%1)", state)
if state then
-- Pulsing
D("processCondition() cs.pulseuntil is %1 (pulsing %2)", cs.pulseuntil, now < cs.pulseuntil )
if now < cs.pulseuntil then
D("processCondition() continue pulse until %1", cs.pulseuntil)
state = true -- hold up unconditionally
scheduleDelay( tostring(tdev), cs.pulseuntil - now )
addEvent{ dev=tdev,
msg="%(cname)s end of pulse",
msg="%(cname)s timing output pulse, %(delay)s seconds remain",
cname=(cond.type or "group")=="group" and ("Group "..(cond.name or cond.id)) or ("Condition "..cond.id),
cond=cond.id, delay=cs.pulseuntil-now }
elseif not state then
-- Pulse high timer has expired and underlying test now false, end.
D("processCondition() end of pulse train after %1 pulses", cs.pulsecount)
addEvent{ dev=tdev,
msg="%(cname)s end of pulse with reset",
cname=(cond.type or "group")=="group" and ("Group "..(cond.name or cond.id)) or ("Condition "..cond.id),
cond=cond.id }
if cs.pulseuntil and (condopt.pulsebreak or 0) > 0 then
local holdoff = pulseend + condopt.pulsebreak
cs.pulseuntil = nil
cs.pulsecount = nil
else
-- Pulse high timer expired, test still true. If repeat in effect, delay through off period
D("processCondition() pulse off phase (%1)", state)
if (condopt.pulsebreak or 0) > 0 then
local holdoff = cs.pulseuntil + condopt.pulsebreak
D("processCondition() pulse repeat, break until %1", holdoff)
addEvent{ dev=tdev,
msg="%(cname)s end of pulse, in repeat break, %(dly)s more",
cname=(cond.type or "group")=="group" and ("Group "..(cond.name or cond.id)) or ("Condition "..cond.id),
cond=cond.id, delay=holdoff-now }
if now >= holdoff then
local pulselim = condopt.pulsecount or 0
if pulselim == 0 or ( cs.pulsecount or 1 ) < pulselim then
Expand All @@ -4057,17 +4068,18 @@ local function processCondition( cond, grp, cdata, tdev )
state = false -- override
end
else
-- One-shot pulse.
cs.pulseuntil = state and pulseend or nil
-- One-shot pulse (no repeat).
addEvent{ dev=tdev,
msg="%(cname)s end of pulse, one-shot hold",
cname=(cond.type or "group")=="group" and ("Group "..(cond.name or cond.id)) or ("Condition "..cond.id),
cond=cond.id }
cs.pulseuntil = state and cs.pulseuntil or nil
cs.pulsecount = nil
state = false -- override
end
else
cs.pulseuntil = nil
cs.pulsecount = nil
end
D("processCondition() pulse state is %1, until %2", state, cs.pulseuntil)
end
D("processCondition() pulse state is %1, until %2", state, cs.pulseuntil)
else
cs.pulseuntil = nil
cs.pulsecount = nil
Expand Down Expand Up @@ -5241,6 +5253,11 @@ function startPlugin( pdev, ptask ) -- N.B. can be run as task
L("Detected VeraAlerts (%1)", k)
elseif v.device_num_parent == pdev and v.device_type == RSTYPE then
addEvent{ dev=k, msg="Reactor startup (Luup reload)" }
elseif v.device_type == MYTYPE and k < pdev then
-- Found another master device w/lower dev# than me, no go.
luup.variable_set( MYSID, "Message", "Duplicate master device", pdev )
luup.set_failure( 1, pdev )
return false, "Duplicate master device", _PLUGIN_NAME
end
end
if failmsg then
Expand Down

0 comments on commit 42a4b2d

Please sign in to comment.