Skip to content

Commit

Permalink
[satel] Action for reading the event log added (openhab#7282)
Browse files Browse the repository at this point in the history
Signed-off-by: Krzysztof Goworek <[email protected]>
Signed-off-by: Eugen Freiter <[email protected]>
  • Loading branch information
druciak authored and Eugen Freiter committed Apr 27, 2020
1 parent f61b0de commit 1eba620
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 44 deletions.
86 changes: 54 additions & 32 deletions bundles/org.openhab.binding.satel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ Example:
Bridge satel:ethm-1:home [ host="192.168.0.2", refresh=1000, userCode="1234", encryptionKey="abcdefgh" ]
```

**NOTE:** There can be only one client connected to ETHM-1 module.
It does not accept new connections if there is already a connection established.
In case you have troubles connecting to the system using this module, please make sure there is no other client (for example installed 1.x version of the binding) already connected to it.

### int-rs bridge

You can configure the following settings for this bridge:
Expand Down Expand Up @@ -218,6 +222,8 @@ Thing atd-100 KitchenTemp [ id=10, refresh=30 ]

### output

**NOTE:** You can change state of mono/bistable outputs only.

| Name | Type | Description |
|---------------|--------|-----------------------------------------------------------|
| state | Switch | State of the output |
Expand All @@ -243,7 +249,9 @@ Thing atd-100 KitchenTemp [ id=10, refresh=30 ]
| grade23_set | Switch | Active when Grade2/Grade3 option is set in the system |
| user_code | String | Accepts string commands that override configured user code. Send empty string to revert user code to the one in the configuration. |

### event-log
### event-log (deprecated)

These channels and the thing will be removed in the future release of the binding. Please use `readEvent` rule action instead.

| Name | Type | Description |
|-------------|----------|----------------------------------------------------------------------------------------|
Expand All @@ -261,6 +269,31 @@ Thing atd-100 KitchenTemp [ id=10, refresh=30 ]
| device_lobatt | Switch | Indicates low battery level in the wireless device |
| device_nocomm | Switch | Indicates communication troubles with the wireless device |

## Rule Actions

### readEvent

This action allows you to read one record from the event log placed at index given by input parameter.
The result of this action is compatible with channels of `event-log` thing and contains following values:

| Name | Type | Description |
|-------------|---------------|----------------------------------------------------------------------------------------|
| index | Number | Index of this record in the event log. |
| prev_index | Number | Index of the previous record in the event log. Use this value to iterate over the log. |
| timestamp | ZonedDateTime | Date and time when the event happened. |
| description | String | Textual description of the event. |
| details | String | Details about the event, usually list of objects related to the event. |

Usage:

```
val actions = getActions("satel", "satel:event-log:home:EventLog")
val eventRec = actions.readEvent(-1)
logInfo("EventLog", eventRec.get("description"))
```

**NOTE:** To have this action available, you must have `event-log` thing configured in openHAB.

## Full Example

### satel.things
Expand Down Expand Up @@ -295,11 +328,6 @@ Rollershutter KITCHEN_BLIND "Kitchen blind" (Satel) { channel="satel:shutter:hom
Switch SYSTEM_TROUBLES "Troubles in the system" (Satel) { channel="satel:system:home:System:troubles" }
String KEYPAD_CHAR ">" <none> (Satel)
String USER_CODE "User code" (Satel) { channel="satel:system:home:System:user_code" }
Number EVENT_LOG_IDX "Event log - current index [%d]" (Satel) { channel="satel:event-log:home:EventLog:index" }
Number EVENT_LOG_PREV "Event log - previous index [%d]" (Satel) { channel="satel:event-log:home:EventLog:prev_index" }
DateTime EVENT_LOG_TIME "Event log - time [%1$tF %1$tR]" (Satel) { channel="satel:event-log:home:EventLog:timestamp" }
String EVENT_LOG_DESCR "Event log - description [%s]" (Satel) { channel="satel:event-log:home:EventLog:description" }
String EVENT_LOG_DET "Event log - details [%s]" (Satel) { channel="satel:event-log:home:EventLog:details" }
Switch SIREN_LOBATT "Siren: low battery level" (Satel) { channel="satel:output:home:Siren:device_lobatt" }
Switch SIREN_NOCOMM "Siren: no communication" (Satel) { channel="satel:output:home:Siren:device_nocomm" }
Number:Temperature KITCHEN_TEMP "Kitchen temperature [%.1f °C]" <temperature> (Satel) { channel="satel:atd-100:home:KitchenTemp:temperature" }
Expand Down Expand Up @@ -344,8 +372,6 @@ Frame label="Alarm system" {
var String userCode = ""
var Timer keypadTimer = null
var Timer userCodeTimer = null
var int eventLogCounter = 0
var String eventLogMsgBody = ""
rule "Keypad char entered"
when
Expand Down Expand Up @@ -381,34 +407,30 @@ then
]
end
rule "Send event log start"
rule "Send event log"
when
Item Alarms changed to ON
then
eventLogCounter = 0
eventLogMsgBody = ""
EVENT_LOG_PREV.postUpdate(NULL)
EVENT_LOG_IDX.sendCommand(-1)
end
rule "Send event log next"
when
Item EVENT_LOG_PREV changed
then
if (EVENT_LOG_PREV.state == NULL) {
} else if (eventLogCounter == 30) {
sendMail("[email protected]", "Alarm system log", eventLogMsgBody)
// prevent initiating reading when index item is restored during OH startup
EVENT_LOG_IDX.postUpdate(NULL)
} else {
eventLogMsgBody += "\n" + (EVENT_LOG_TIME.state as DateTimeType).format("%1$tF %1$tR") + ": " + EVENT_LOG_DESCR.state
if (EVENT_LOG_DET.state != NULL && EVENT_LOG_DET.state != "") {
eventLogMsgBody += " - " + EVENT_LOG_DET.state
}
eventLogCounter += 1
EVENT_LOG_IDX.sendCommand(EVENT_LOG_PREV.state)
val actions = getActions("satel", "satel:event-log:home:EventLog")
if (null === actions) {
logInfo("EventLog", "Actions not found, check thing ID")
return
}
logInfo("EventLog", "Start")
var msgBody = ""
var eventIdx = -1
(1..10).forEach[
val eventRec = actions.readEvent(eventIdx)
val details = eventRec.get("details")
msgBody += "\n" + String::format("%1$tF %1$tR", eventRec.get("timestamp")) + ": " + eventRec.get("description")
if (details != NULL && details != "") {
msgBody += " - " + details
}
eventIdx = eventRec.get("prev_index")
]
logInfo("EventLog", "End")
// sending notifications via mail requires the mail binding
getActions("mail","mail:smtp:local").sendMail("[email protected]", "Event log", msgBody)
end
```

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.satel.action;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.thing.binding.ThingActions;
import org.eclipse.smarthome.core.thing.binding.ThingActionsScope;
import org.eclipse.smarthome.core.thing.binding.ThingHandler;
import org.openhab.binding.satel.internal.handler.SatelEventLogHandler;
import org.openhab.core.automation.annotation.ActionInput;
import org.openhab.core.automation.annotation.ActionOutput;
import org.openhab.core.automation.annotation.RuleAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Automation action handler service for reading Satel event log.
*
* @author Krzysztof Goworek - Initial contribution
* @see SatelEventLogHandler
*/
@ThingActionsScope(name = "satel")
@NonNullByDefault
public class SatelEventLogActions implements ThingActions {

private final Logger logger = LoggerFactory.getLogger(getClass());

private @Nullable SatelEventLogHandler handler;

@Override
public void setThingHandler(@Nullable ThingHandler handler) {
if (handler instanceof SatelEventLogHandler) {
this.handler = (SatelEventLogHandler) handler;
}
}

@Override
public @Nullable ThingHandler getThingHandler() {
return handler;
}

@RuleAction(label = "@text/actionReadEventLabel", description = "@text/actionReadEventDesc")
public @ActionOutput(name = "index", type = "java.lang.Integer", label = "@text/actionOutputIndexLabel", description = "@text/actionOutputIndexDesc") @ActionOutput(name = "prev_index", type = "java.lang.Integer", label = "@text/actionOutputPrevIndexLabel", description = "@text/actionOutputPrevIndexDesc") @ActionOutput(name = "timestamp", type = "java.time.ZonedDateTime", label = "@text/actionOutputTimestampLabel", description = "@text/actionOutputTimestampDesc") @ActionOutput(name = "description", type = "java.lang.String", label = "@text/actionOutputDescriptionLabel", description = "@text/actionOutputDescriptionDesc") @ActionOutput(name = "details", type = "java.lang.String", label = "@text/actionOutputDetailsLabel", description = "@text/actionOutputDetailsDesc") Map<String, Object> readEvent(
@ActionInput(name = "index", label = "@text/actionInputIndexLabel", description = "@text/actionInputIndexDesc") @Nullable Number index) {
logger.debug("satel.readEvent called with input: index={}", index);

Map<String, Object> result = new HashMap<>();
if (handler != null) {
handler.readEvent(index == null ? -1 : index.intValue()).ifPresent(event -> {
result.put("index", event.getIndex());
result.put("prev_index", event.getPrevIndex());
result.put("timestamp", event.getTimestamp());
result.put("description", event.getDescription());
result.put("details", event.getDetails());
});
}
return result;
}

public static Map<String, Object> readEvent(@Nullable ThingActions actions, @Nullable Number index) {
if (actions instanceof SatelEventLogActions) {
return ((SatelEventLogActions) actions).readEvent(index);
} else {
throw new IllegalArgumentException("Instance is not a SatelEventLogActions class.");
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import static org.openhab.binding.satel.internal.SatelBindingConstants.*;

import java.nio.charset.Charset;
import java.time.ZonedDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
Expand All @@ -32,7 +34,9 @@
import org.eclipse.smarthome.core.thing.Thing;
import org.eclipse.smarthome.core.thing.ThingStatus;
import org.eclipse.smarthome.core.thing.ThingTypeUID;
import org.eclipse.smarthome.core.thing.binding.ThingHandlerService;
import org.eclipse.smarthome.core.types.Command;
import org.openhab.binding.satel.action.SatelEventLogActions;
import org.openhab.binding.satel.internal.command.ReadDeviceInfoCommand;
import org.openhab.binding.satel.internal.command.ReadDeviceInfoCommand.DeviceType;
import org.openhab.binding.satel.internal.command.ReadEventCommand;
Expand Down Expand Up @@ -63,6 +67,71 @@ public class SatelEventLogHandler extends SatelThingHandler {
private @Nullable ScheduledFuture<?> cacheExpirationJob;
private Charset encoding = Charset.defaultCharset();

/**
* Represents single record of the event log.
*
* @author Krzysztof Goworek
*
*/
public static class EventLogEntry {

private final int index;
private final int prevIndex;
private final ZonedDateTime timestamp;
private final String description;
private final String details;

private EventLogEntry(int index, int prevIndex, ZonedDateTime timestamp, String description, String details) {
this.index = index;
this.prevIndex = prevIndex;
this.timestamp = timestamp;
this.description = description;
this.details = details;
}

/**
* @return index of this record entry
*/
public int getIndex() {
return index;
}

/**
* @return index of the previous record entry in the log
*/
public int getPrevIndex() {
return prevIndex;
}

/**
* @return date and time when the event occurred
*/
public ZonedDateTime getTimestamp() {
return timestamp;
}

/**
* @return description of the event
*/
public String getDescription() {
return description;
}

/**
* @return details about zones, partitions, users, etc
*/
public String getDetails() {
return details;
}

@Override
public String toString() {
return "EventLogEntry [index=" + index + ", prevIndex=" + prevIndex + ", timestamp=" + timestamp
+ ", description=" + description + ", details=" + details + "]";
}

}

public SatelEventLogHandler(Thing thing) {
super(thing);
}
Expand Down Expand Up @@ -100,7 +169,14 @@ public void handleCommand(ChannelUID channelUID, Command command) {

if (CHANNEL_INDEX.equals(channelUID.getId()) && command instanceof DecimalType) {
int eventIndex = ((DecimalType) command).intValue();
withBridgeHandlerPresent(bridgeHandler -> readEvent(eventIndex));
withBridgeHandlerPresent(bridgeHandler -> readEvent(eventIndex).ifPresent(entry -> {
// update items
updateState(CHANNEL_INDEX, new DecimalType(entry.getIndex()));
updateState(CHANNEL_PREV_INDEX, new DecimalType(entry.getPrevIndex()));
updateState(CHANNEL_TIMESTAMP, new DateTimeType(entry.getTimestamp()));
updateState(CHANNEL_DESCRIPTION, new StringType(entry.getDescription()));
updateState(CHANNEL_DETAILS, new StringType(entry.getDetails()));
}));
}
}

Expand All @@ -113,8 +189,19 @@ public void incomingEvent(ConnectionStatusEvent event) {
}
}

private void readEvent(int eventIndex) {
getEventDescription(eventIndex).ifPresent(eventDesc -> {
@Override
public Collection<Class<? extends ThingHandlerService>> getServices() {
return Collections.singleton(SatelEventLogActions.class);
}

/**
* Reads one record from the event log.
*
* @param eventIndex record index
* @return record data or {@linkplain Optional#empty()} if there is no record under given index
*/
public Optional<EventLogEntry> readEvent(int eventIndex) {
return getEventDescription(eventIndex).flatMap(eventDesc -> {
ReadEventCommand readEventCmd = eventDesc.readEventCmd;
int currentIndex = readEventCmd.getCurrentIndex();
String eventText = eventDesc.getText();
Expand Down Expand Up @@ -191,13 +278,13 @@ private void readEvent(int eventIndex) {
+ (readEventCmd.getObject() * 32 + readEventCmd.getUserControlNumber());
Optional<EventDescription> eventDescNext = getEventDescription(readEventCmd.getNextIndex());
if (!eventDescNext.isPresent()) {
return;
return Optional.empty();
}
final EventDescription eventDescNextItem = eventDescNext.get();
if (eventDescNextItem.getKind() != 30) {
logger.info("Unexpected event record kind {} at index {}", eventDescNextItem.getKind(),
readEventCmd.getNextIndex());
return;
return Optional.empty();
}
readEventCmd = eventDescNextItem.readEventCmd;
eventText = eventDescNextItem.getText();
Expand All @@ -213,13 +300,8 @@ private void readEvent(int eventIndex) {
"object=" + readEventCmd.getObject(), "ucn=" + readEventCmd.getUserControlNumber());
}

// update items
updateState(CHANNEL_INDEX, new DecimalType(currentIndex));
updateState(CHANNEL_PREV_INDEX, new DecimalType(readEventCmd.getNextIndex()));
updateState(CHANNEL_TIMESTAMP,
new DateTimeType(readEventCmd.getTimestamp().atZone(getBridgeHandler().getZoneId())));
updateState(CHANNEL_DESCRIPTION, new StringType(eventText));
updateState(CHANNEL_DETAILS, new StringType(eventDetails));
return Optional.of(new EventLogEntry(currentIndex, readEventCmd.getNextIndex(),
readEventCmd.getTimestamp().atZone(getBridgeHandler().getZoneId()), eventText, eventDetails));
});
}

Expand Down
Loading

0 comments on commit 1eba620

Please sign in to comment.