Skip to content

Commit

Permalink
sysvar timestamp handling, output config (implements #13)
Browse files Browse the repository at this point in the history
  • Loading branch information
hobbyquaker committed Aug 3, 2018
1 parent e9a3316 commit 402cc24
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 90 deletions.
160 changes: 79 additions & 81 deletions nodes/ccu-connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -596,26 +596,13 @@ module.exports = function (RED) {
newValue = value;
break;
}
const ts = now();
const script = `dom.GetObject(${sysvar.id}).State(${value});`;
this.logger.debug('rega setVariable', name, script);
this.logger.trace('setVariable', name, script);
this.rega.exec(script + '\n', err => {
if (err) {
reject(err);
} else {
const valueEnum = sysvar.enum && sysvar.enum[Number(newValue)];
Object.assign(sysvar, {
ts,
tsPrevious: sysvar.ts,
lc: sysvar.value === newValue ? sysvar.lc : ts,
lcPrevious: sysvar.lc,
value: newValue,
valuePrevious: sysvar.value,
payload: newValue,
valueEnum,
change: sysvar.value !== newValue
});

this.regaPoll();
resolve(sysvar);
}
});
Expand All @@ -636,9 +623,9 @@ module.exports = function (RED) {
this.regaPollPending = true;
clearTimeout(this.regaPollTimeout);
this.getRegaVariables()
.catch(err => this.logger.error('rega getVariables', err))
.catch(err => this.logger.error('getRegaVariables', err))
.then(() => this.getRegaPrograms())
.catch(err => this.logger.error('rega getPrograms', err))
.catch(err => this.logger.error('getRegaPrograms', err))
.then(() => {
if (this.regaInterval && !this.cancelRegaPoll) {
this.logger.trace('rega next poll in', this.regaInterval, 'seconds');
Expand All @@ -661,81 +648,93 @@ module.exports = function (RED) {
return found;
}

updateRegaVariable(sysvar) {
this.logger.trace('updateRegaVariable', JSON.stringify(sysvar));
let isNew = false;
if (!this.sysvar[sysvar.name]) {
isNew = true;
this.sysvar[sysvar.name] = {
topic: '',
payload: sysvar.value,
ccu: this.host,
iface: 'ReGaHSS',
type: 'SYSVAR',
name: sysvar.name,
value: sysvar.value,
valueType: sysvar.type,
valueEnum: sysvar.enum[Number(sysvar.val)],
ts: sysvar.ts,
enum: sysvar.enum,
id: sysvar.id,
cache: isNew
};
if (sysvar.channel) {
const channel = this.regaIdChannel[sysvar.channel];
const iface = this.findIface(channel);
const device = this.metadata.devices[iface] && this.metadata.devices[iface][channel] && this.metadata.devices[iface][channel].PARENT;
Object.assign(this.sysvar[sysvar.name], {
device,
deviceName: this.channelNames[device],
deviceType: this.metadata.devices[iface] && this.metadata.devices[iface][device] && this.metadata.devices[iface][device].TYPE,
channel,
channelName: this.channelNames[channel],
channelType: this.metadata.devices[iface] && this.metadata.devices[iface][channel] && this.metadata.devices[iface][channel].TYPE,
channelIndex: channel && parseInt(channel.split(':')[1], 10),
rooms: this.channelRooms[channel],
room: this.channelRooms[channel] && this.channelRooms[channel].length === 1 ? this.channelRooms[channel][0] : undefined,
functions: this.channelFunctions[channel],
function: this.channelFunctions[channel] && this.channelFunctions[channel].length === 1 ? this.channelFunctions[channel][0] : undefined
});
}
}
if (isNew || this.sysvar[sysvar.name].ts !== sysvar.ts) {
Object.assign(this.sysvar[sysvar.name], {
payload: sysvar.val,
value: sysvar.val,
valueEnum: this.sysvar[sysvar.name].enum[Number(sysvar.val)],
valuePrevious: this.sysvar[sysvar.name].value,
valueEnumPrevious: this.sysvar[sysvar.name].valueEnum,
ts: sysvar.ts,
tsPrevious: this.sysvar[sysvar.name].ts,
lc: sysvar.ts,
lcPrevious: this.sysvar[sysvar.name].lc,
change: isNew ? false : this.sysvar[sysvar.name].value !== sysvar.val,
cache: isNew
});

Object.keys(this.sysvarCallbacks).forEach(key => {
const {filter, callback} = this.sysvarCallbacks[key];
let match = filter.name === sysvar.name;
if (this.sysvar[sysvar.name].cache && !filter.cache) {
match = false;
} else if (filter.change && !this.sysvar[sysvar.name].change) {
match = false;
}
this.logger.trace('match', match, JSON.stringify(filter), 'name:' + sysvar.name + ' ' + 'cache:' + this.sysvar[sysvar.name].cache + ' ' + 'change:' + this.sysvar[sysvar.name].change + ' ');
if (match) {
callback(this.sysvar[sysvar.name]);
}
});
}
}

/**
* Poll ReGaHSS variables and call subscription callbacks
* @returns {Promise}
*/
getRegaVariables() {
return new Promise((resolve, reject) => {
this.logger.debug('rega getVariables');
this.logger.trace('getRegaVariables');
this.rega.getVariables((err, res) => {
if (err) {
reject(err);
this.setIfaceStatus('ReGaHSS', false);
} else {
const d = new Date();
res.forEach(sysvar => {
let isNew = false;
if (!this.sysvar[sysvar.name]) {
isNew = true;
this.sysvar[sysvar.name] = {
topic: '',
payload: sysvar.value,
ccu: this.host,
iface: 'ReGaHSS',
type: 'SYSVAR',
name: sysvar.name,
value: sysvar.value,
valueType: sysvar.type,
valueEnum: sysvar.enum[Number(sysvar.val)],
ts: d.getTime(),
lc: (new Date(sysvar.ts + ' UTC+' + (d.getTimezoneOffset() / -60))).getTime(),
enum: sysvar.enum,
id: sysvar.id
};
if (sysvar.channel) {
const channel = this.regaIdChannel[sysvar.channel];
const iface = this.findIface(channel);
const device = this.metadata.devices[iface] && this.metadata.devices[iface][channel] && this.metadata.devices[iface][channel].PARENT;
Object.assign(this.sysvar[sysvar.name], {
device,
deviceName: this.channelNames[device],
deviceType: this.metadata.devices[iface] && this.metadata.devices[iface][device] && this.metadata.devices[iface][device].TYPE,
channel,
channelName: this.channelNames[channel],
channelType: this.metadata.devices[iface] && this.metadata.devices[iface][channel] && this.metadata.devices[iface][channel].TYPE,
channelIndex: channel && parseInt(channel.split(':')[1], 10),
rooms: this.channelRooms[channel],
room: this.channelRooms[channel] && this.channelRooms[channel].length === 1 ? this.channelRooms[channel][0] : undefined,
functions: this.channelFunctions[channel],
function: this.channelFunctions[channel] && this.channelFunctions[channel].length === 1 ? this.channelFunctions[channel][0] : undefined
});
}
}
if (this.sysvar[sysvar.name].value === sysvar.val) {
Object.assign(this.sysvar[sysvar.name], {
change: false,
tsPrevious: this.sysvar[sysvar.name].ts,
ts: d.getTime()
});
} else {
Object.assign(this.sysvar[sysvar.name], {
payload: sysvar.val,
value: sysvar.val,
valuePrevious: this.sysvar[sysvar.name].value,
ts: d.getTime(),
tsPrevious: this.sysvar[sysvar.name].ts,
lc: (new Date(sysvar.ts + ' UTC+' + (d.getTimezoneOffset() / -60))).getTime(),
lcPrevious: isNew ? undefined : this.sysvar[sysvar.name].lc,
change: !isNew
});
Object.keys(this.sysvarCallbacks).forEach(key => {
const {filter, callback} = this.sysvarCallbacks[key];
if (filter.name === sysvar.name) {
callback(this.sysvar[sysvar.name]);
}
});
}
this.logger.trace(JSON.stringify(sysvar));
sysvar.ts = sysvar.ts ? (new Date(sysvar.ts + ' UTC+' + (d.getTimezoneOffset() / -60))).getTime() : d.getTime();
this.updateRegaVariable(sysvar);
});
resolve();
this.setIfaceStatus('ReGaHSS', true);
Expand Down Expand Up @@ -1201,11 +1200,10 @@ module.exports = function (RED) {
* @param {function} callback
* @returns {number|null} subscription id
*/
subscribeSysvar(name, callback) {
subscribeSysvar(filter, callback) {
if (typeof callback === 'function') {
const id = this.idSysvarCallback;
this.idSysvarCallback += 1;
const filter = {name};
this.logger.debug('subscribeSysvar', id, JSON.stringify(filter));
this.sysvarCallbacks[id] = {filter, callback};
return id;
Expand Down
36 changes: 35 additions & 1 deletion nodes/ccu-sysvar.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
defaults: {
name: {value: ''},
ccuConfig: {value: 'localhost', type: 'ccu-connection', required: true},
topic: {value: 'ReGaHSS/${Name}'} // eslint-disable-line no-template-curly-in-string
topic: {value: 'ReGaHSS/${Name}'}, // eslint-disable-line no-template-curly-in-string
change: {value: true},
cache: {value: true}
},
inputs: 1,
outputs: 1,
Expand All @@ -18,6 +20,15 @@
},
oneditprepare() {
const cname = this.name;

// Migration
if (typeof this.change === 'undefined') {
$('#node-input-change').attr('checked', true)
}
if (typeof this.cache === 'undefined') {
$('#node-input-cache').attr('checked', true)
}

function getConf(nodeId) {
$('#select-input-name').html('').hide();
$('#node-input-name').val('').show();
Expand Down Expand Up @@ -69,8 +80,31 @@
<select id="select-input-name" style="display:none">
</select>
</div>
<div class="form-row">
<label for="node-input-change"><i class="icon-bookmark"></i> </label>
<div style="width: 70%; display: inline-block; vertical-align: text-top;">
<label class="ccu-checkbox" for="node-input-change">
<input type="checkbox" id="node-input-change">
<span data-i18n="ccu-sysvar.change"></span>
</label>

<label class="ccu-checkbox">
<input type="checkbox" id="node-input-cache">
<span data-i18n="ccu-sysvar.cache"></span>
</label>
</div>
</div>
<div class="form-tips" data-i18n="ccu-sysvar.tip"></div>
<style>
.ccu-checkbox {
width: auto !important;
display: block !important;
}
.ccu-checkbox input {
width: 24px;
margin-top: -3px;
}
</style>
</script>

<script type="text/x-red" data-help-name="ccu-sysvar" lang="de-DE">
Expand Down
18 changes: 12 additions & 6 deletions nodes/ccu-sysvar.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,19 @@ module.exports = function (RED) {

this.ccu.register(this);

// Migration
if (typeof config.change === 'undefined') {
config.change = true;
}
if (typeof config.cache === 'undefined') {
config.cache = true;
}

this.name = config.name;
this.topic = config.topic;

if (this.name) {
this.idSubscription = this.ccu.subscribeSysvar(this.name, msg => {
this.idSubscription = this.ccu.subscribeSysvar({name: this.name, cache: config.cache, change: config.change}, msg => {
msg.topic = this.ccu.topicReplace(config.topic, msg);
this.send(msg);
});
Expand All @@ -32,11 +40,9 @@ module.exports = function (RED) {

_input(msg) {
const name = this.name || msg.topic;
const value = msg.payload;
this.ccu.setVariable(name, value)
.then(msg => {
msg.topic = this.ccu.topicReplace(this.topic, msg);
this.send(msg);
const val = msg.payload;
this.ccu.setVariable(name, val)
.then(() => {
this.status({fill: 'green', shape: 'dot', text: 'connected'});
})
.catch(err => {
Expand Down
4 changes: 3 additions & 1 deletion nodes/locales/de-DE/ccu-sysvar.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"ccu-sysvar": {
"tip": "Wird keine Variable ausgewählt kann der Variablen-Name über msg.topic übergeben werden."
"tip": "Wird keine Variable ausgewählt kann der Variablen-Name über msg.topic übergeben werden.",
"change": "Nur geänderte Werte ausgeben",
"cache": "Beim Start aktuellen Wert ausgeben"
}
}
4 changes: 3 additions & 1 deletion nodes/locales/en-US/ccu-sysvar.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"ccu-sysvar": {
"tip": "Omit the variable name to supply it via msg.payload"
"tip": "Omit the variable name to supply it via msg.payload",
"change": "Emit changed values only",
"cache": "Emit value on start"
}
}

0 comments on commit 402cc24

Please sign in to comment.