Skip to content

Instantly share code, notes, and snippets.

@ssalonen
Last active July 15, 2022 13:09
Show Gist options
  • Save ssalonen/257078e7310c4629e9983c9ff315dc9e to your computer and use it in GitHub Desktop.
Save ssalonen/257078e7310c4629e9983c9ff315dc9e to your computer and use it in GitHub Desktop.
uid: ssalonen:device-online-monitor
label: Monitor device online status
description: Monitor device online status using combination of thing status and item state
configDescriptions:
- name: INPUT_ITEM
type: TEXT
context: item
label: Input item to monitor
required: true
description: Item to monitor for UNDEF changes
- name: INPUT_THING
type: TEXT
context: thing
label: Input thing to monitor
required: true
description: Thing to monitor for OFFLINE/ONLINE
- name: ONLINE_STATUS_ITEM
type: TEXT
context: item
label: Item to output the detected online status
required: true
description: Item to output the detected online status
- name: GRACE_PERIOD_SECS
type: INTEGER
label: Grace period after startup, in seconds
required: false
defaultValue: 0
description: Grace period after startp before deeming the device status offline
- name: CRON
type: TEXT
label: Cron expression specifying period for checking inital status
required: false
defaultValue: 0 0 0/2 * * ? *
description: Cron expression specifying period for checking inital status
triggers:
- id: "1"
configuration:
startlevel: 100
type: core.SystemStartlevelTrigger
- id: "2"
configuration:
cronExpression: "{{CRON}}"
type: timer.GenericCronTrigger
- id: "3"
configuration:
thingUID: "{{INPUT_THING}}"
type: core.ThingStatusChangeTrigger
- id: "4"
configuration:
itemName: "{{INPUT_ITEM}}"
type: core.ItemStateUpdateTrigger
conditions: []
actions:
- inputs: {}
id: "6"
configuration:
type: application/javascript;version=ECMAScript-2021
script: >-
/* global Java, event, items, actions */
(function () {
const THING_ID = '{{INPUT_THING}}'
const INPUT_ITEM_NAME = '{{INPUT_ITEM}}'
const RAW_ITEM_NAME = '{{ONLINE_STATUS_ITEM}}'
const GRACE_PERIOD_SECS = parseInt('{{GRACE_PERIOD_SECS}}', 10)
function calculateUptimeSeconds () {
const ManagementFactory = Java.type('java.lang.management.ManagementFactory')
const runtime = ManagementFactory.getRuntimeMXBean()
const startedSecs = runtime.getStartTime() / 1000
return startedSecs
}
const uptimeSecs = calculateUptimeSeconds()
// We know that the trigger must be time interval based (cron) or "system started" when
// we do not have any thing status info or item state information in the event
const cronOrStarted = typeof event === 'undefined' || (event.statusInfo === undefined && event.itemState === undefined)
const thingTurnedOffline = typeof event !== 'undefined' && event.statusInfo !== undefined && event.statusInfo.status.toString() === 'OFFLINE'
const thingTurnedOnline = typeof event !== 'undefined' && event.statusInfo !== undefined && event.statusInfo.status.toString() === 'ONLINE'
const itemUpdatedUndef = typeof event !== 'undefined' && event.itemState !== undefined && event.itemState.toString() === 'UNDEF'
const itemUpdatedNonUndef = typeof event !== 'undefined' && event.itemState !== undefined && event.itemState.toString() !== 'UNDEF'
// handle each case separately
if (cronOrStarted) {
if (((GRACE_PERIOD_SECS === 0) || (uptimeSecs > GRACE_PERIOD_SECS)) &&
(items.getItem(RAW_ITEM_NAME).state.toString() === 'NULL')) {
// Init with NULL on startup unless we can consider the thing/state clearly "offline".
// Further updates are based on thing status updates and state updates
const inputItemStateStr = items.getItem(INPUT_ITEM_NAME).state.toString()
const consideredNonAlive = (actions.Things.getThingStatusInfo(THING_ID).getStatus().toString() === 'OFFLINE'
|| inputItemStateStr === 'UNDEF' || inputItemStateStr === 'NULL')
const aliveState = consideredNonAlive ? 'OFF' : 'NULL'
console.debug(INPUT_ITEM_NAME + ' / ' + THING_ID + ' initialization (' +
(consideredNonAlive ? 'considered non-alive' : 'considered as indeterminate state') +
') -> alive state = ' + aliveState + ' (' + RAW_ITEM_NAME + ')')
items.getItem(RAW_ITEM_NAME).postUpdate(aliveState)
} else {
console.debug(INPUT_ITEM_NAME + ' / ' + THING_ID + ' initialization: completed. Not updating active status ('
+ RAW_ITEM_NAME + ')')
}
} else if (thingTurnedOffline) {
console.debug(THING_ID + ' turned OFFLINE (' + event.statusInfo.description.toString()
+ ') -> Considering non-alive (' + RAW_ITEM_NAME + ')')
items.getItem(RAW_ITEM_NAME).postUpdate('OFF')
} else if (thingTurnedOnline) {
const inputItemStateStr = items.getItem(INPUT_ITEM_NAME).state.toString()
const inputItemValid = inputItemStateStr !== 'UNDEF' && inputItemStateStr !== 'NULL'
console.debug(THING_ID + ' turned ONLINE and input item state (' + inputItemStateStr + ') is ' +
(inputItemValid ? 'valid' : 'non-valid') +
' -> Considering ' + (inputItemValid ? 'alive' : 'non-alive') + ' (' + RAW_ITEM_NAME + ')')
const aliveState = inputItemValid ? 'ON' : 'OFF'
items.getItem(RAW_ITEM_NAME).postUpdate(aliveState)
} else if (itemUpdatedUndef) {
console.debug(INPUT_ITEM_NAME + ' updated with UNDEF -> Considering non-alive' + ' (' + RAW_ITEM_NAME + ')')
items.getItem(RAW_ITEM_NAME).postUpdate('OFF')
} else if (itemUpdatedNonUndef) {
console.debug(INPUT_ITEM_NAME + ' updated with non-UNDEF state -> Considering alive' + ' (' + RAW_ITEM_NAME + ')')
items.getItem(RAW_ITEM_NAME).postUpdate('ON')
} else {
console.error('Unexpected condition')
}
})()
type: script.ScriptAction
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment