[nikohomecontrol] Alarm systems (#16834)

* alarm system

Signed-off-by: Mark Herwege <mark.herwege@telenet.be>
This commit is contained in:
Mark Herwege 2024-06-17 21:50:18 +02:00 committed by GitHub
parent 2bd4e11dd8
commit 663b2ad857
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 763 additions and 45 deletions

View File

@ -30,7 +30,7 @@ Niko Home Control alarm and notice messages are retrieved and made available in
## Supported Things
The Niko Home Control Controller is represented as a bridge in the binding.
Connected to a bridge, the Niko Home Control Binding supports all off actions, on/off actions (e.g. for lights or groups of lights), dimmers, rollershutters or blinds, thermostats, energy meters (Niko Home Control I only) and access control devices (Niko Home Control II only).
Connected to a bridge, the Niko Home Control Binding supports all off actions, on/off actions (e.g. for lights or groups of lights), dimmers, rollershutters or blinds, thermostats, energy meters, access control devices (Niko Home Control II only) and alarm systems (Niko Home Control II only).
The following thing types are available in the binding:
@ -47,6 +47,7 @@ The following thing types are available in the binding:
| waterMeter | x | | water meter, aggregates readings with 10 min intervals |
| access | | x | door with bell button and lock |
| accessRingAndComeIn | | x | door with bell button, lock and ring and come in functionality |
| alarm | | x | alarm system |
## Binding Configuration
@ -139,6 +140,7 @@ The Thing configurations for **Niko Home Control actions, thermostats, energy me
| meterId | x | x | x | energyMeterLive, energyMeter, gasMeter, waterMeter | unique ID for the energy meter in the controller |
| refresh | x | x | | energyMeterLive, energyMeter, gasMeter, waterMeter | refresh interval for meter reading in minutes, default 10 minutes. The value should not be lower than 5 minutes to avoid too many meter data retrieval calls |
| accessId | | x | x | access, accessRingAndComeIn | unique ID for the access device in the controller |
| alarmId | | x | x | alarm | unique ID for the alarm system in the controller |
For Niko Home Control I, the `actionId`, `thermostatId` or `meterId` parameter are the unique IP Interface Object ID (`ipInterfaceObjectId`) as automatically assigned in the Niko Home Control Controller when programming the Niko Home Control system using the Niko Home Control I programming software.
It is not directly visible in the Niko Home Control programming or user software, but will be detected and automatically set by openHAB discovery.
@ -153,7 +155,7 @@ Note down the `actionId` parameter from the thing, remove it before adding it ag
Alternatively the `actionId` can be retrieved from the configuration file.
The file contains a SQLLite database.
The database contains a table `Action` with column `FifthplayId` corresponding to the required `actionId` parameter.
The same applies applies for `thermostatId`, `meterId` and `accessId`.
The same applies applies for `thermostatId`, `meterId`, `accessId` and `alarmId`.
An example **action** textual configuration looks like:
@ -179,6 +181,11 @@ For **access devices**:
Thing nikohomecontrol:accessRingAndComeIn:mybridge:myaccess [ accessId="abcdef01-dcba-1234-ab98-012345abcdef" ]
```
For **alarm systems**:
```java
Thing nikohomecontrol:alarm:mybridge:myalarm [ alarmId="abcdef01-dcba-1234-ab98-012345abcdef" ]
```
## Channels
@ -206,10 +213,12 @@ Thing nikohomecontrol:accessRingAndComeIn:mybridge:myaccess [ accessId="abcdef01
| bellbutton | RW | | Switch | access, accessRingAndComeIn | bell button connected to access device, including buttons on video phone devices linked to an access device. The bell can also be triggered by an `ON` command, `autoupdate="false"` by default |
| ringandcomein | RW | | Switch | accessRingAndComeIn | provide state and turn automatic door unlocking at bell ring on/off |
| lock | RW | | Switch | access, accessRingAndComeIn | provide doorlock state and unlock the door by sending an `OFF` command. `autoupdate="false"` by default |
| arm | RW | | Switch | alarm | arm/disarm alarm, will change state (on/off) immediately. Note some integrations (Homekit, Google Home, ...) may require String states for an alarm system (ARMED/DISARMED). This can be achieved using an extra item and a rule updated by/commanding an item linked to this channel |
| armed | RW | | Switch | alarm | state of the alarm system (on/off), will only turn on after pre-armed period when arming |
| state | R | | String | alarm | state of the alarm system (DISARMED, PREARMED, ARMED, PREALARM, ALARM, DETECTOR PROBLEM) |
| alarm | | | | bridge, alarm | trigger channel with alarm event message, can be used in rules |
| notice | | | | bridge | trigger channel with notice event message, can be used in rules |
The bridge has two trigger channels `alarm` and `notice`.
It can be used as a trigger to rules.
The event message is the alarm or notice text coming from Niko Home Control.
## Limitations
@ -240,6 +249,7 @@ Bridge nikohomecontrol:bridge2:nhc2 [ addr="192.168.0.70", port=8884, password="
blind 4 [ actionId="abcdef01-abcd-1234-ab98-abcdefabcdef" ]
thermostat 5 [ thermostatId="abcdef01-abcd-1234-ab98-012345abcdef", overruleTime=10 ]
accessRingAndComeIn 6 [ accessId="abcdef01-abcd-1234-ab98-012345abcdef" ]
alarm 7 [ alarmId="abcdef01-abcd-1234-ab98-543210abcdef" ]
}
Bridge nikohomecontrol:bridge:nhc3 [ addr="192.168.0.110" ] {
@ -265,6 +275,9 @@ String ThermostatDemand {channel="nikohomecontrol:thermostat:nhc1:5:heatingdem
Number:Power CurPower "[%.0f W]" {channel="nikohomecontrol:energyMeterLive:nhc1:6:power"} # Get current power consumption
Number:Energy MyMeter "[%.0f kWh]" {channel="nikohomecontrol:energyMeter:nhc1:7:energy # Get energy meter reading
Number:Energy MyMeterDay "[%.0f kWh]" {channel="nikohomecontrol:energyMeter:nhc1:7:energyday # Get energy meter day reading
Switch AlarmControl {channel="nikohomecontrol:onOff:nhc2:7:arm"} # Switch to arm/disarm alarm
Switch AlarmSwitch {channel="nikohomecontrol:onOff:nhc2:7:armed"} # Switch to arm/disarm alarm, on state delayed until after pre-arm phase
String AlarmState {channel="nikohomecontrol:onOff:nhc2:7:state"} # State of the alarm system
```
.sitemap:

View File

@ -52,6 +52,7 @@ public class NikoHomeControlBindingConstants {
public static final ThingTypeUID THING_TYPE_ACCESS = new ThingTypeUID(BINDING_ID, "access");
public static final ThingTypeUID THING_TYPE_ACCESS_RINGANDCOMEIN = new ThingTypeUID(BINDING_ID,
"accessRingAndComeIn");
public static final ThingTypeUID THING_TYPE_ALARM = new ThingTypeUID(BINDING_ID, "alarm");
// thing type sets
public static final Set<ThingTypeUID> BRIDGE_THING_TYPES_UIDS = Set.of(BRIDGEI_THING_TYPE, BRIDGEII_THING_TYPE);
@ -62,9 +63,12 @@ public class NikoHomeControlBindingConstants {
THING_TYPE_ENERGYMETER, THING_TYPE_GASMETER, THING_TYPE_WATERMETER);
public static final Set<ThingTypeUID> ACCESS_THING_TYPES_UIDS = Set.of(THING_TYPE_ACCESS,
THING_TYPE_ACCESS_RINGANDCOMEIN);
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream.of(BRIDGE_THING_TYPES_UIDS.stream(),
ACTION_THING_TYPES_UIDS.stream(), THERMOSTAT_THING_TYPES_UIDS.stream(), METER_THING_TYPES_UIDS.stream(),
ACCESS_THING_TYPES_UIDS.stream()).flatMap(i -> i).collect(Collectors.toSet());
public static final Set<ThingTypeUID> ALARM_THING_TYPES_UIDS = Set.of(THING_TYPE_ALARM);
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream
.of(BRIDGE_THING_TYPES_UIDS.stream(), ACTION_THING_TYPES_UIDS.stream(),
THERMOSTAT_THING_TYPES_UIDS.stream(), METER_THING_TYPES_UIDS.stream(),
ACCESS_THING_TYPES_UIDS.stream(), ALARM_THING_TYPES_UIDS.stream())
.flatMap(i -> i).collect(Collectors.toSet());
// List of all Channel ids
public static final String CHANNEL_BUTTON = "button";
@ -95,6 +99,10 @@ public class NikoHomeControlBindingConstants {
public static final String CHANNEL_RING_AND_COME_IN = "ringandcomein";
public static final String CHANNEL_LOCK = "lock";
public static final String CHANNEL_ARM = "arm";
public static final String CHANNEL_ARMED = "armed";
public static final String CHANNEL_STATE = "state";
public static final String CHANNEL_ALARM = "alarm";
public static final String CHANNEL_NOTICE = "notice";
@ -118,6 +126,8 @@ public class NikoHomeControlBindingConstants {
public static final String CONFIG_ACCESS_ID = "accessId";
public static final String CONFIG_ALARM_ID = "alarmId";
// Thing properties
public static final String PROPERTY_DEVICE_TYPE = "deviceType";
public static final String PROPERTY_DEVICE_TECHNOLOGY = "deviceTechnology";

View File

@ -18,6 +18,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlAccessHandler;
import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlActionHandler;
import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlAlarmHandler;
import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlBridgeHandler1;
import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlBridgeHandler2;
import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlMeterHandler;
@ -77,6 +78,8 @@ public class NikoHomeControlHandlerFactory extends BaseThingHandlerFactory {
return new NikoHomeControlMeterHandler(thing);
} else if (ACCESS_THING_TYPES_UIDS.contains(thing.getThingTypeUID())) {
return new NikoHomeControlAccessHandler(thing);
} else if (ALARM_THING_TYPES_UIDS.contains(thing.getThingTypeUID())) {
return new NikoHomeControlAlarmHandler(thing);
}
return null;

View File

@ -22,9 +22,9 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlBridgeHandler;
import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlBridgeHandler2;
import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAccess;
import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAction;
import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAlarm;
import org.openhab.binding.nikohomecontrol.internal.protocol.NhcMeter;
import org.openhab.binding.nikohomecontrol.internal.protocol.NhcThermostat;
import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication;
@ -84,6 +84,7 @@ public class NikoHomeControlDiscoveryService
discoverThermostatDevices(thingHandler, nhcComm);
discoverMeterDevices(thingHandler, nhcComm);
discoverAccessDevices(thingHandler, nhcComm);
discoverAlarmDevices(thingHandler, nhcComm);
}
private void discoverActionDevices(NikoHomeControlBridgeHandler bridgeHandler,
@ -131,12 +132,6 @@ public class NikoHomeControlDiscoveryService
private void discoverMeterDevices(NikoHomeControlBridgeHandler bridgeHandler,
NikoHomeControlCommunication nhcComm) {
if (bridgeHandler instanceof NikoHomeControlBridgeHandler2) {
// disable discovery of NHC II energy meters to avoid overload in Niko Home Control cloud, can be removed
// when Niko solves their issue with the controller sending all live power data to their cloud
return;
}
Map<String, NhcMeter> meters = nhcComm.getMeters();
meters.forEach((deviceId, nhcMeter) -> {
@ -191,6 +186,18 @@ public class NikoHomeControlDiscoveryService
});
}
private void discoverAlarmDevices(NikoHomeControlBridgeHandler bridgeHandler,
NikoHomeControlCommunication nhcComm) {
Map<String, NhcAlarm> alarmDevices = nhcComm.getAlarmDevices();
alarmDevices.forEach((deviceId, nhcAlarm) -> {
String thingName = nhcAlarm.getName();
String thingLocation = nhcAlarm.getLocation();
addDevice(new ThingUID(THING_TYPE_ALARM, bridgeHandler.getThing().getUID(), deviceId), CONFIG_ALARM_ID,
deviceId, thingName, thingLocation);
});
}
private void addDevice(ThingUID uid, String deviceIdKey, String deviceId, String thingName,
@Nullable String thingLocation) {
DiscoveryResultBuilder discoveryResultBuilder = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID)

View File

@ -96,8 +96,7 @@ public class NikoHomeControlAccessHandler extends NikoHomeControlBaseHandler imp
return;
}
if (command instanceof OnOffType) {
OnOffType s = (OnOffType) command;
if (command instanceof OnOffType s) {
if (OnOffType.ON.equals(s)) {
nhcAccess.executeBell();
}
@ -111,8 +110,7 @@ public class NikoHomeControlAccessHandler extends NikoHomeControlBaseHandler imp
return;
}
if (command instanceof OnOffType) {
OnOffType s = (OnOffType) command;
if (command instanceof OnOffType s) {
nhcAccess.executeRingAndComeIn(OnOffType.ON.equals(s));
}
}
@ -124,8 +122,7 @@ public class NikoHomeControlAccessHandler extends NikoHomeControlBaseHandler imp
return;
}
if (command instanceof OnOffType) {
OnOffType s = (OnOffType) command;
if (command instanceof OnOffType s) {
if (OnOffType.OFF.equals(s)) {
nhcAccess.executeUnlock();
}
@ -220,8 +217,7 @@ public class NikoHomeControlAccessHandler extends NikoHomeControlBaseHandler imp
private void updateProperties(NhcAccess nhcAccess) {
Map<String, String> properties = new HashMap<>();
if (nhcAccess instanceof NhcAccess2) {
NhcAccess2 access = (NhcAccess2) nhcAccess;
if (nhcAccess instanceof NhcAccess2 access) {
properties.put(PROPERTY_DEVICE_TYPE, access.getDeviceType());
properties.put(PROPERTY_DEVICE_TECHNOLOGY, access.getDeviceTechnology());
properties.put(PROPERTY_DEVICE_MODEL, access.getDeviceModel());

View File

@ -0,0 +1,25 @@
/**
* Copyright (c) 2010-2024 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.nikohomecontrol.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* {@link NikoHomeControlAlarmConfig} is the general config class for Niko Home Control Alarm systems.
*
* @author Mark Herwege - Initial Contribution
*/
@NonNullByDefault
public class NikoHomeControlAlarmConfig {
public String alarmId = "";
}

View File

@ -0,0 +1,204 @@
/**
* Copyright (c) 2010-2024 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.nikohomecontrol.internal.handler;
import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.*;
import static org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.*;
import static org.openhab.core.types.RefreshType.REFRESH;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAlarm;
import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAlarmEvent;
import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication;
import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NhcAlarm2;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link NikoHomeControlAlarmHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Mark Herwege - Initial Contribution
*/
@NonNullByDefault
public class NikoHomeControlAlarmHandler extends NikoHomeControlBaseHandler implements NhcAlarmEvent {
private final Logger logger = LoggerFactory.getLogger(NikoHomeControlAlarmHandler.class);
private volatile @Nullable NhcAlarm nhcAlarm;
public NikoHomeControlAlarmHandler(Thing thing) {
super(thing);
}
@Override
void handleCommandSelection(ChannelUID channelUID, Command command) {
NhcAlarm nhcAlarm = this.nhcAlarm;
if (nhcAlarm == null) {
logger.debug("alarm device with ID {} not initialized", deviceId);
return;
}
logger.debug("handle command {} for {}", command, channelUID);
if (REFRESH.equals(command)) {
alarmEvent(nhcAlarm.getState());
return;
}
if ((CHANNEL_ARM.equals(channelUID.getId()) || CHANNEL_ARMED.equals(channelUID.getId()))
&& command instanceof OnOffType s) {
if (OnOffType.ON.equals(s)) {
nhcAlarm.executeArm();
} else {
nhcAlarm.executeDisarm();
}
} else {
logger.debug("unexpected command for channel {}", channelUID.getId());
}
}
@Override
public void initialize() {
initialized = false;
NikoHomeControlAlarmConfig config = getConfig().as(NikoHomeControlAlarmConfig.class);
deviceId = config.alarmId;
NikoHomeControlBridgeHandler bridgeHandler = getBridgeHandler();
if (bridgeHandler == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.configuration-error.invalid-bridge-handler");
return;
}
updateStatus(ThingStatus.UNKNOWN);
Bridge bridge = getBridge();
if ((bridge != null) && ThingStatus.ONLINE.equals(bridge.getStatus())) {
// We need to do this in a separate thread because we may have to wait for the
// communication to become active
commStartThread = scheduler.submit(this::startCommunication);
}
}
@Override
synchronized void startCommunication() {
NikoHomeControlCommunication nhcComm = getCommunication(getBridgeHandler());
if (nhcComm == null) {
return;
}
if (!nhcComm.communicationActive()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.communication-error");
return;
}
NhcAlarm nhcAlarm = nhcComm.getAlarmDevices().get(deviceId);
if (nhcAlarm == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.configuration-error.deviceId");
return;
}
nhcAlarm.setEventHandler(this);
updateProperties(nhcAlarm);
String location = nhcAlarm.getLocation();
if (thing.getLocation() == null) {
thing.setLocation(location);
}
this.nhcAlarm = nhcAlarm;
initialized = true;
deviceInitialized();
}
@Override
void refresh() {
NhcAlarm alarm = nhcAlarm;
if (alarm != null) {
alarmEvent(alarm.getState());
}
}
@Override
public void dispose() {
NikoHomeControlCommunication nhcComm = getCommunication(getBridgeHandler());
if (nhcComm != null) {
NhcAlarm alarm = nhcComm.getAlarmDevices().get(deviceId);
if (alarm != null) {
alarm.unsetEventHandler();
}
}
nhcAlarm = null;
super.dispose();
}
private void updateProperties(NhcAlarm nhcAlarm) {
Map<String, String> properties = new HashMap<>();
if (nhcAlarm instanceof NhcAlarm2 alarm) {
properties.put(PROPERTY_DEVICE_TYPE, alarm.getDeviceType());
properties.put(PROPERTY_DEVICE_TECHNOLOGY, alarm.getDeviceTechnology());
properties.put(PROPERTY_DEVICE_MODEL, alarm.getDeviceModel());
}
thing.setProperties(properties);
}
@Override
public void alarmEvent(String state) {
NhcAlarm nhcAlarm = this.nhcAlarm;
if (nhcAlarm == null) {
logger.debug(" alarm device with ID {} not initialized", deviceId);
return;
}
updateState(CHANNEL_ARM,
NHCOFF.equals(state) || NHCDETECTORPROBLEM.equals(state) ? OnOffType.OFF : OnOffType.ON);
updateState(CHANNEL_ARMED,
NHCOFF.equals(state) || NHCPREARMED.equals(state) || NHCDETECTORPROBLEM.equals(state) ? OnOffType.OFF
: OnOffType.ON);
updateState(CHANNEL_STATE, StringType.valueOf(ALARMSTATES.get(state)));
updateStatus(ThingStatus.ONLINE);
}
@Override
public void alarmTriggerEvent() {
NhcAlarm nhcAlarm = this.nhcAlarm;
if (nhcAlarm == null) {
logger.debug(" alarm device with ID {} not initialized", deviceId);
return;
}
triggerChannel(CHANNEL_ALARM);
updateStatus(ThingStatus.ONLINE);
}
}

View File

@ -196,15 +196,13 @@ public class NikoHomeControlMeterHandler extends NikoHomeControlBaseHandler impl
private void updateProperties(NhcMeter nhcMeter) {
Map<String, String> properties = new HashMap<>();
if (nhcMeter instanceof NhcMeter1) {
NhcMeter1 meter = (NhcMeter1) nhcMeter;
if (nhcMeter instanceof NhcMeter1 meter) {
properties.put("type", meter.getMeterType());
LocalDateTime referenceDate = meter.getReferenceDate();
if (referenceDate != null) {
properties.put("startdateUTC", referenceDate.format(DATE_TIME_FORMAT));
}
} else if (nhcMeter instanceof NhcMeter2) {
NhcMeter2 meter = (NhcMeter2) nhcMeter;
} else if (nhcMeter instanceof NhcMeter2 meter) {
properties.put(PROPERTY_DEVICE_TYPE, meter.getDeviceType());
properties.put(PROPERTY_DEVICE_TECHNOLOGY, meter.getDeviceTechnology());
properties.put(PROPERTY_DEVICE_MODEL, meter.getDeviceModel());

View File

@ -0,0 +1,176 @@
/**
* Copyright (c) 2010-2024 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.nikohomecontrol.internal.protocol;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link NhcAlarm} class represents the alarm control Niko Home Control communication object. It contains all
* fields representing a Niko Home Control alarm control device and has methods to arm/disarm in Niko Home Control
* and receive alarms. A specific implementation is {@link
* org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NhcAlarm2}.
*
* @author Mark Herwege - Initial Contribution
*/
@NonNullByDefault
public abstract class NhcAlarm {
private final Logger logger = LoggerFactory.getLogger(NhcAlarm.class);
protected NikoHomeControlCommunication nhcComm;
protected final String id;
protected String name;
protected @Nullable String location;
protected volatile String state = "";
@Nullable
private NhcAlarmEvent eventHandler;
protected NhcAlarm(String id, String name, @Nullable String location, NikoHomeControlCommunication nhcComm) {
this.id = id;
this.name = name;
this.location = location;
this.nhcComm = nhcComm;
}
/**
* This method should be called when an object implementing the {@NhcAlarmEvent} interface is initialized.
* It keeps a record of the event handler in that object so it can be updated when the alarm control device
* receives an update from the Niko Home Control IP-interface.
*
* @param eventHandler
*/
public void setEventHandler(NhcAlarmEvent eventHandler) {
this.eventHandler = eventHandler;
}
/**
* This method should be called when an object implementing the {@NhcAlarmEvent} interface is disposed.
* It resets the reference, so no updates go to the handler anymore.
*/
public void unsetEventHandler() {
this.eventHandler = null;
}
/**
* Get the id of the alarm control device.
*
* @return the id
*/
public String getId() {
return id;
}
/**
* Get name of the alarm control device.
*
* @return alarm control name
*/
public String getName() {
return name;
}
/**
* Set name of the alarm control device.
*
* @param name alarm control name
*/
public void setName(String name) {
this.name = name;
}
/**
* Get location name of alarm control device.
*
* @return location name
*/
public @Nullable String getLocation() {
return location;
}
/**
* Set location of the alarm control device.
*
* @param location alarm control location
*/
public void setLocation(@Nullable String location) {
this.location = location;
}
/**
* Get state of the alarm control device.
*
* @return action state
*/
public String getState() {
return state;
}
/**
* Sets state of alarm.
*
* @param state: Off, PreArmed, DetectorProblem, Armed, PreAlarm or Alarm
*/
public void setState(String state) {
this.state = state;
updateState();
}
/**
* Send update of alarm state through event handler to subscribers.
*/
public void updateState() {
NhcAlarmEvent eventHandler = this.eventHandler;
if (eventHandler != null) {
logger.debug("update channel state for {} with {}", id, state);
eventHandler.alarmEvent(state);
}
}
/**
* Send alarm trigger through event handler to subscribers.
*/
public void triggerAlarm() {
NhcAlarmEvent eventHandler = this.eventHandler;
if (eventHandler != null) {
logger.debug("trigger alarm for {}", id);
eventHandler.alarmTriggerEvent();
}
}
/**
* Method called when alarm control device is removed from the Niko Home Control Controller.
*/
public void alarmDeviceRemoved() {
logger.debug("alarm device removed {}, {}", id, name);
NhcAlarmEvent eventHandler = this.eventHandler;
if (eventHandler != null) {
eventHandler.deviceRemoved();
unsetEventHandler();
}
}
public void executeArm() {
logger.debug("arm the alarm with id {}", id);
nhcComm.executeArm(id);
}
public void executeDisarm() {
logger.debug("disarm the alarm with id {}", id);
nhcComm.executeDisarm(id);
}
}

View File

@ -0,0 +1,39 @@
/**
* Copyright (c) 2010-2024 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.nikohomecontrol.internal.protocol;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link NhcAlarmEvent} interface is used to pass alarm events received from the Niko Home Control controller to
* the consuming client. It is designed to pass events to openHAB handlers that implement this interface. Because of
* the design, the org.openhab.binding.nikohomecontrol.internal.protocol package can be extracted and used independent
* of openHAB.
*
* @author Mark Herwege - Initial Contribution
*/
@NonNullByDefault
public interface NhcAlarmEvent extends NhcBaseEvent {
/**
* This method is called when an alarm event is received from the Niko Home Control controller.
*
* @param state
*/
void alarmEvent(String state);
/**
* This method is called when an alarm trigger event is received from the Niko Home Control controller.
*/
void alarmTriggerEvent();
}

View File

@ -49,6 +49,7 @@ public abstract class NikoHomeControlCommunication {
protected final Map<String, NhcMeter> meters = new ConcurrentHashMap<>();
protected final Map<String, NhcAccess> accessDevices = new ConcurrentHashMap<>();
protected final Map<String, NhcVideo> videoDevices = new ConcurrentHashMap<>();
protected final Map<String, NhcAlarm> alarmDevices = new ConcurrentHashMap<>();
protected final NhcControllerEvent handler;
@ -192,6 +193,15 @@ public abstract class NikoHomeControlCommunication {
return videoDevices;
}
/**
* Return all alarm devices in the Niko Home Control Controller.
*
* @return <code>Map&lt;String, {@link NhcAlarm}></code>
*/
public Map<String, NhcAlarm> getAlarmDevices() {
return alarmDevices;
}
/**
* Execute an action command by sending it to Niko Home Control.
*
@ -327,4 +337,20 @@ public abstract class NikoHomeControlCommunication {
*/
public void executeAccessUnlock(String accessId) {
}
/**
* Execute an arm command on an alarm control device by sending it to Niko Home Control.
*
* @param accessId
*/
public void executeArm(String alarmId) {
}
/**
* Execute an disarm command on an alarm control device by sending it to Niko Home Control.
*
* @param accessId
*/
public void executeDisarm(String alarmId) {
}
}

View File

@ -12,6 +12,8 @@
*/
package org.openhab.binding.nikohomecontrol.internal.protocol;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
@ -23,7 +25,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
public class NikoHomeControlConstants {
// Action types abstracted from NhcI and NhcII action types
public enum ActionType {
public static enum ActionType {
TRIGGER,
RELAY,
DIMMER,
@ -72,4 +74,16 @@ public class NikoHomeControlConstants {
// NhcII thermostat modes
public static final String[] THERMOSTATMODES = { "Day", "Night", "Eco", "Off", "Cool", "Prog1", "Prog2", "Prog3" };
public static final String[] THERMOSTATDEMAND = { "Cooling", "None", "Heating" };
// NhcII alarm states
public static final String NHCINTERMEDIATE = "Intermediate";
public static final String NHCARM = "Activate";
public static final String NHCDISARM = "Deactivate";
public static final String NHCPREARMED = "PreArmed";
public static final String NHCDETECTORPROBLEM = "DetectorProblem";
public static final String NHCARMED = "Armed";
public static final String NHCPREALARM = "PreAlarm";
public static final String NHCALARM = "Alarm";
public static final Map<String, String> ALARMSTATES = Map.of(NHCOFF, "DISARMED", NHCPREARMED, "PREARMED",
NHCDETECTORPROBLEM, "DETECTOR PROBLEM", NHCARMED, "ARMED", NHCPREALARM, "PREALARM", NHCALARM, "ALARM");
}

View File

@ -0,0 +1,62 @@
/**
* Copyright (c) 2010-2024 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.nikohomecontrol.internal.protocol.nhc2;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAlarm;
import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication;
/**
* The {@link NhcAlarm2} class represents the alarm Niko Home Control II communication object. It contains all fields
* representing a Niko Home Control alarm and has methods to arm/disarm the alarm in Niko Home Control and receive
* alarms.
*
* @author Mark Herwege - Initial Contribution
*/
@NonNullByDefault
public class NhcAlarm2 extends NhcAlarm {
private final String deviceType;
private final String deviceTechnology;
private final String deviceModel;
NhcAlarm2(String id, String name, String deviceType, String deviceTechnology, String deviceModel,
@Nullable String location, NikoHomeControlCommunication nhcComm) {
super(id, name, location, nhcComm);
this.deviceType = deviceType;
this.deviceTechnology = deviceTechnology;
this.deviceModel = deviceModel;
}
/**
* @return type as returned from Niko Home Control
*/
public String getDeviceType() {
return deviceType;
}
/**
* @return technology as returned from Niko Home Control
*/
public String getDeviceTechnology() {
return deviceTechnology;
}
/**
* @return model as returned from Niko Home Control
*/
public String getDeviceModel() {
return deviceModel;
}
}

View File

@ -103,6 +103,15 @@ class NhcDevice2 {
String callStatus03;
@Nullable
String callStatus04;
// fields for alarms
@Nullable
String internalState;
@Nullable
String alarmActive;
@Nullable
String alarmTriggered;
@Nullable
String control;
}
static class NhcTrait {

View File

@ -63,7 +63,7 @@ public class NhcMqttConnection2 implements MqttActionCallback {
private final MqttConnectionObserver connectionObserver;
private TrustManager[] trustManagers;
private String clientId;
private final String clientId;
private volatile String cocoAddress = "";
private volatile int port;

View File

@ -35,6 +35,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAccess;
import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAction;
import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAlarm;
import org.openhab.binding.nikohomecontrol.internal.protocol.NhcControllerEvent;
import org.openhab.binding.nikohomecontrol.internal.protocol.NhcMeter;
import org.openhab.binding.nikohomecontrol.internal.protocol.NhcThermostat;
@ -381,6 +382,8 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
addVideoDevice(device);
} else if ("accesscontrol".equals(device.model) || "bellbutton".equals(device.model)) {
addAccessDevice(device, location);
} else if ("alarms".equals(device.model)) {
addAlarmDevice(device, location);
} else if ("action".equals(device.type) || "virtual".equals(device.type)) {
addActionDevice(device, location);
} else if ("thermostat".equals(device.type)) {
@ -400,7 +403,6 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
case "pir":
case "simulation":
case "comfort":
case "alarms":
case "alloff":
case "overallcomfort":
case "garagedoor":
@ -564,12 +566,26 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
videoDevices.put(device.uuid, nhcVideo);
}
private void addAlarmDevice(NhcDevice2 device, @Nullable String location) {
NhcAlarm nhcAlarm = alarmDevices.get(device.uuid);
if (nhcAlarm != null) {
nhcAlarm.setName(device.name);
nhcAlarm.setLocation(location);
} else {
logger.debug("adding alarm device {} model {}, {}", device.uuid, device.model, device.name);
nhcAlarm = new NhcAlarm2(device.uuid, device.name, device.type, device.technology, device.model, location,
this);
}
alarmDevices.put(device.uuid, nhcAlarm);
}
private void removeDevice(NhcDevice2 device) {
NhcAction action = actions.get(device.uuid);
NhcThermostat thermostat = thermostats.get(device.uuid);
NhcMeter meter = meters.get(device.uuid);
NhcAccess access = accessDevices.get(device.uuid);
NhcVideo video = videoDevices.get(device.uuid);
NhcAlarm alarm = alarmDevices.get(device.uuid);
if (action != null) {
action.actionRemoved();
actions.remove(device.uuid);
@ -585,6 +601,9 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
} else if (video != null) {
video.videoDeviceRemoved();
videoDevices.remove(device.uuid);
} else if (alarm != null) {
alarm.alarmDeviceRemoved();
alarmDevices.remove(device.uuid);
}
}
@ -600,6 +619,7 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
NhcMeter meter = meters.get(device.uuid);
NhcAccess accessDevice = accessDevices.get(device.uuid);
NhcVideo videoDevice = videoDevices.get(device.uuid);
NhcAlarm alarm = alarmDevices.get(device.uuid);
if (action != null) {
updateActionState((NhcAction2) action, deviceProperties);
@ -611,6 +631,8 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
updateAccessState((NhcAccess2) accessDevice, deviceProperties);
} else if (videoDevice != null) {
updateVideoState((NhcVideo2) videoDevice, deviceProperties);
} else if (alarm != null) {
updateAlarmState((NhcAlarm2) alarm, deviceProperties);
} else {
logger.trace("No known device for {}", device.uuid);
}
@ -807,21 +829,36 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
videoDevice.updateState(callStatus01, callStatus02, callStatus03, callStatus04);
}
private void updateAlarmState(NhcAlarm2 alarmDevice, List<NhcProperty> deviceProperties) {
String state = deviceProperties.stream().map(p -> p.internalState).filter(Objects::nonNull).findFirst()
.orElse(null);
if (state != null) {
logger.debug("setting alarm device {} state to {}", alarmDevice.getId(), state);
alarmDevice.setState(state);
}
String triggered = deviceProperties.stream().map(p -> p.alarmTriggered).filter(Objects::nonNull).findFirst()
.orElse(null);
if (Boolean.valueOf(triggered)) {
logger.debug("triggering alarm device {}", alarmDevice.getId());
alarmDevice.triggerAlarm();
}
}
@Override
public void executeAction(String actionId, String value) {
NhcMessage2 message = new NhcMessage2();
message.method = "devices.control";
ArrayList<NhcMessageParam> params = new ArrayList<>();
List<NhcMessageParam> params = new ArrayList<>();
NhcMessageParam param = new NhcMessageParam();
params.add(param);
message.params = params;
ArrayList<NhcDevice2> devices = new ArrayList<>();
List<NhcDevice2> devices = new ArrayList<>();
NhcDevice2 device = new NhcDevice2();
devices.add(device);
param.devices = devices;
device.uuid = actionId;
ArrayList<NhcProperty> deviceProperties = new ArrayList<>();
List<NhcProperty> deviceProperties = new ArrayList<>();
NhcProperty property = new NhcProperty();
deviceProperties.add(property);
device.properties = deviceProperties;
@ -888,16 +925,16 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
NhcMessage2 message = new NhcMessage2();
message.method = "devices.control";
ArrayList<NhcMessageParam> params = new ArrayList<>();
List<NhcMessageParam> params = new ArrayList<>();
NhcMessageParam param = new NhcMessageParam();
params.add(param);
message.params = params;
ArrayList<NhcDevice2> devices = new ArrayList<>();
List<NhcDevice2> devices = new ArrayList<>();
NhcDevice2 device = new NhcDevice2();
devices.add(device);
param.devices = devices;
device.uuid = thermostatId;
ArrayList<NhcProperty> deviceProperties = new ArrayList<>();
List<NhcProperty> deviceProperties = new ArrayList<>();
NhcProperty overruleActiveProp = new NhcProperty();
deviceProperties.add(overruleActiveProp);
@ -919,16 +956,16 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
NhcMessage2 message = new NhcMessage2();
message.method = "devices.control";
ArrayList<NhcMessageParam> params = new ArrayList<>();
List<NhcMessageParam> params = new ArrayList<>();
NhcMessageParam param = new NhcMessageParam();
params.add(param);
message.params = params;
ArrayList<NhcDevice2> devices = new ArrayList<>();
List<NhcDevice2> devices = new ArrayList<>();
NhcDevice2 device = new NhcDevice2();
devices.add(device);
param.devices = devices;
device.uuid = thermostatId;
ArrayList<NhcProperty> deviceProperties = new ArrayList<>();
List<NhcProperty> deviceProperties = new ArrayList<>();
if (overruleTime > 0) {
NhcProperty overruleActiveProp = new NhcProperty();
@ -956,7 +993,7 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
@Override
public void executeMeter(String meterId) {
// Nothing to do, meter readings not supported in NHC II at this point in time
// Nothing to do, individual meter readings not supported in NHC II at this point in time
}
@Override
@ -964,16 +1001,16 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
NhcMessage2 message = new NhcMessage2();
message.method = "devices.control";
ArrayList<NhcMessageParam> params = new ArrayList<>();
List<NhcMessageParam> params = new ArrayList<>();
NhcMessageParam param = new NhcMessageParam();
params.add(param);
message.params = params;
ArrayList<NhcDevice2> devices = new ArrayList<>();
List<NhcDevice2> devices = new ArrayList<>();
NhcDevice2 device = new NhcDevice2();
devices.add(device);
param.devices = devices;
device.uuid = meterId;
ArrayList<NhcProperty> deviceProperties = new ArrayList<>();
List<NhcProperty> deviceProperties = new ArrayList<>();
NhcProperty reportInstantUsageProp = new NhcProperty();
deviceProperties.add(reportInstantUsageProp);
@ -1091,12 +1128,12 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
NhcMessageParam param = new NhcMessageParam();
params.add(param);
message.params = params;
ArrayList<NhcDevice2> devices = new ArrayList<>();
List<NhcDevice2> devices = new ArrayList<>();
NhcDevice2 device = new NhcDevice2();
devices.add(device);
param.devices = devices;
device.uuid = accessId;
ArrayList<NhcProperty> deviceProperties = new ArrayList<>();
List<NhcProperty> deviceProperties = new ArrayList<>();
NhcProperty property = new NhcProperty();
deviceProperties.add(property);
device.properties = deviceProperties;
@ -1113,6 +1150,46 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
sendDeviceMessage(topic, gsonMessage);
}
@Override
public void executeArm(String alarmId) {
executeAlarm(alarmId, NHCARM);
}
@Override
public void executeDisarm(String alarmId) {
executeAlarm(alarmId, NHCDISARM);
}
private void executeAlarm(String alarmId, String state) {
NhcMessage2 message = new NhcMessage2();
message.method = "devices.control";
List<NhcMessageParam> params = new ArrayList<>();
NhcMessageParam param = new NhcMessageParam();
params.add(param);
message.params = params;
List<NhcDevice2> devices = new ArrayList<>();
NhcDevice2 device = new NhcDevice2();
devices.add(device);
param.devices = devices;
device.uuid = alarmId;
List<NhcProperty> deviceProperties = new ArrayList<>();
NhcProperty property = new NhcProperty();
deviceProperties.add(property);
device.properties = deviceProperties;
NhcAlarm2 alarmDevice = (NhcAlarm2) alarmDevices.get(alarmId);
if (alarmDevice == null) {
return;
}
property.control = state;
String topic = profile + "/control/devices/cmd";
String gsonMessage = gson.toJson(message);
sendDeviceMessage(topic, gsonMessage);
}
private void sendDeviceMessage(String topic, String gsonMessage) {
try {
mqttConnection.connectionPublish(topic, gsonMessage);

View File

@ -61,6 +61,9 @@ accessDescription = Basic Access Control in the Niko Home Control system
accessRingAndComeInLabel = Ring And Come In Access Control
accessRingAndComeInDescription = Access Control with ring and come in function in the Niko Home Control system
alarmLabel = Alarm
alarmDescription = Alarm system in the Niko Home Control system
deviceConfigDeviceIdLabel = Device ID
deviceConfigDeviceIdDescription = Niko Home Control device ID
@ -147,6 +150,19 @@ channelRingAndComeInDescription = Ring and come in status and turn on/off
channelLockLabel = Lock
channelLockDescription = Doorlock status and unlock door
channelAlarmArmedLabel = Alarm State
channelAlarmArmedDescription = State of the alarm system (on/off)
channelAlarmStateLabel = Alarm State
channelAlarmStateDescription = State of the alarm system
channelAlarmTargetStateLabel = Alarm Target State
channelAlarmTargetStateDescription = Target state of the alarm system
channelOptionAlarmStateDisarmed = Disarmed
channelOptionAlarmStatePrearmed = Pre-armed
channelOptionAlarmStateDetectorProblem = Detector problem
channelOptionAlarmStateArmed = Armed
channelOptionAlarmStatePreAlarm = Pre-alarm
channelOptionAlarmStateAlarm = Alarm
channelAlarmLabel = Alarm
channelAlarmDescription = Alarm from Niko Home Control

View File

@ -336,6 +336,25 @@
</parameter>
</config-description>
</thing-type>
<thing-type id="alarm">
<supported-bridge-type-refs>
<bridge-type-ref id="bridge2"/>
</supported-bridge-type-refs>
<label>@text/alarmLabel</label>
<description>@text/alarmDescription</description>
<channels>
<channel id="arm" typeId="system.power"/>
<channel id="armed" typeId="armed"/>
<channel id="state" typeId="alarmstate"/>
<channel id="alarm" typeId="alarm"/>
</channels>
<config-description>
<parameter name="alarmId" type="text" required="true">
<label>@text/deviceConfigDeviceIdLabel</label>
<description>@text/deviceConfigDeviceIdDescription</description>
</parameter>
</config-description>
</thing-type>
<channel-type id="button">
<item-type>Switch</item-type>
@ -508,6 +527,30 @@
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="armed">
<item-type>Switch</item-type>
<label>@text/channelAlarmArmedLabel</label>
<description>@text/channelAlarmArmedDescription</description>
<category>Alarm</category>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
<channel-type id="alarmstate">
<item-type>String</item-type>
<label>@text/channelAlarmStateLabel</label>
<description>@text/channelAlarmStateDescription</description>
<category>Alarm</category>
<state readOnly="true">
<options>
<option value="DISARMED">@text/channelOptionAlarmStateDisarmed</option>
<option value="PREARMED">@text/channelOptionAlarmStatePrearmed</option>
<option value="DETECTOR PROBLEM">@text/channelOptionAlarmStateDetectorProblem</option>
<option value="ARMED">@text/channelOptionAlarmStateArmed</option>
<option value="PREALARM">@text/channelOptionAlarmStatePreAlarm</option>
<option value="ALARM">@text/channelOptionAlarmStateAlarm</option>
</options>
</state>
</channel-type>
<channel-type id="alarm">
<kind>trigger</kind>
<label>@text/channelAlarmLabel</label>