mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
Compare commits
16 Commits
8e19f2c19b
...
2bcfae7958
Author | SHA1 | Date | |
---|---|---|---|
|
2bcfae7958 | ||
|
111312abc7 | ||
|
e32eda1c08 | ||
|
da807f9633 | ||
|
a3d3feec7f | ||
|
7dd809b8ab | ||
|
0959932148 | ||
|
3320dd39fc | ||
|
e12876ba11 | ||
|
ed2a6d5700 | ||
|
c399be7a43 | ||
|
e4b432bb6b | ||
|
563caa0762 | ||
|
705feffd54 | ||
|
6af04fc31f | ||
|
22c7ca99ad |
@ -87,19 +87,22 @@ In this example file-based using Rule Builder:
|
||||
rules.when()
|
||||
.channel('energidataservice:service:energidataservice:electricity#event').triggered('DAY_AHEAD_AVAILABLE')
|
||||
.then(event => {
|
||||
var timeSeries = new items.TimeSeries('REPLACE');
|
||||
var start = time.LocalDate.now().atStartOfDay().atZone(time.ZoneId.systemDefault());
|
||||
var spotPrices = items.SpotPrice.persistence.getAllStatesBetween(start, start.plusDays(2));
|
||||
for (var spotPrice of spotPrices) {
|
||||
var totalPrice = spotPrice.quantityState
|
||||
.add(items.GridTariff.persistence.persistedState(spotPrice.timestamp).quantityState)
|
||||
.add(items.SystemTariff.persistence.persistedState(spotPrice.timestamp).quantityState)
|
||||
.add(items.TransmissionGridTariff.persistence.persistedState(spotPrice.timestamp).quantityState)
|
||||
.add(items.ElectricityTax.persistence.persistedState(spotPrice.timestamp).quantityState);
|
||||
// Short delay because persistence is asynchronous.
|
||||
setTimeout(() => {
|
||||
var timeSeries = new items.TimeSeries('REPLACE');
|
||||
var start = time.LocalDate.now().atStartOfDay().atZone(time.ZoneId.systemDefault());
|
||||
var spotPrices = items.SpotPrice.persistence.getAllStatesBetween(start, start.plusDays(2));
|
||||
for (var spotPrice of spotPrices) {
|
||||
var totalPrice = spotPrice.quantityState
|
||||
.add(items.GridTariff.persistence.persistedState(spotPrice.timestamp).quantityState)
|
||||
.add(items.SystemTariff.persistence.persistedState(spotPrice.timestamp).quantityState)
|
||||
.add(items.TransmissionGridTariff.persistence.persistedState(spotPrice.timestamp).quantityState)
|
||||
.add(items.ElectricityTax.persistence.persistedState(spotPrice.timestamp).quantityState);
|
||||
|
||||
timeSeries.add(spotPrice.timestamp, totalPrice);
|
||||
}
|
||||
items.TotalPrice.persistence.persist(timeSeries);
|
||||
timeSeries.add(spotPrice.timestamp, totalPrice);
|
||||
}
|
||||
items.TotalPrice.persistence.persist(timeSeries);
|
||||
}, 5000);
|
||||
})
|
||||
.build("Calculate total price");
|
||||
```
|
||||
@ -112,23 +115,25 @@ rules.when()
|
||||
rule "Calculate total price" do
|
||||
channel "energidataservice:service:energidataservice:electricity#event", triggered: "DAY_AHEAD_AVAILABLE"
|
||||
run do
|
||||
# Persistence methods will call LocalDate#to_zoned_date_time which converts it
|
||||
# to a ZonedDateTime in the default system zone, with 00:00 as its time portion
|
||||
start = LocalDate.now
|
||||
spot_prices = SpotPrice.all_states_between(start, start + 2.days)
|
||||
after 5.seconds do # Short delay because persistence is asynchronous.
|
||||
# Persistence methods will call LocalDate#to_zoned_date_time which converts it
|
||||
# to a ZonedDateTime in the default system zone, with 00:00 as its time portion
|
||||
start = LocalDate.now
|
||||
spot_prices = SpotPrice.all_states_between(start, start + 2.days)
|
||||
|
||||
next unless spot_prices # don't proceed if the persistence result is nil
|
||||
next unless spot_prices # don't proceed if the persistence result is nil
|
||||
|
||||
time_series = TimeSeries.new # the default policy is replace
|
||||
spot_prices.each do |spot_price|
|
||||
total_price = spot_price +
|
||||
GridTariff.persisted_state(spot_price.timestamp) +
|
||||
SystemTariff.persisted_state(spot_price.timestamp) +
|
||||
TransmissionGridTariff.persisted_state(spot_price.timestamp) +
|
||||
ElectricityTax.persisted_state(spot_price.timestamp)
|
||||
time_series.add(spot_price.timestamp, total_price)
|
||||
time_series = TimeSeries.new # the default policy is replace
|
||||
spot_prices.each do |spot_price|
|
||||
total_price = spot_price +
|
||||
GridTariff.persisted_state(spot_price.timestamp) +
|
||||
SystemTariff.persisted_state(spot_price.timestamp) +
|
||||
TransmissionGridTariff.persisted_state(spot_price.timestamp) +
|
||||
ElectricityTax.persisted_state(spot_price.timestamp)
|
||||
time_series.add(spot_price.timestamp, total_price)
|
||||
end
|
||||
TotalPrice.persist(time_series)
|
||||
end
|
||||
TotalPrice.persist(time_series)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
@ -58,8 +58,7 @@ public class Ipx800Actions implements ThingActions {
|
||||
public void resetCounter(
|
||||
@ActionInput(name = "counter", label = "Counter", required = true, description = "Id of the counter", type = "java.lang.Integer") Integer counter) {
|
||||
logger.debug("IPX800 action 'resetCounter' called");
|
||||
Ipx800v3Handler theHandler = this.handler;
|
||||
if (theHandler != null) {
|
||||
if (handler instanceof Ipx800v3Handler theHandler) {
|
||||
theHandler.resetCounter(counter);
|
||||
} else {
|
||||
logger.warn("Method call resetCounter failed because IPX800 action service ThingHandler is null!");
|
||||
@ -70,8 +69,7 @@ public class Ipx800Actions implements ThingActions {
|
||||
public void reset(
|
||||
@ActionInput(name = "placeholder", label = "Placeholder", required = false, description = "This parameter is not used", type = "java.lang.Integer") @Nullable Integer placeholder) {
|
||||
logger.debug("IPX800 action 'reset' called");
|
||||
Ipx800v3Handler theHandler = this.handler;
|
||||
if (theHandler != null) {
|
||||
if (handler instanceof Ipx800v3Handler theHandler) {
|
||||
theHandler.reset();
|
||||
} else {
|
||||
logger.warn("Method call reset failed because IPX800 action service ThingHandler is null!");
|
||||
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
@ -18,13 +18,18 @@ import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.Optional;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Random;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.gce.internal.model.M2MMessageParser;
|
||||
import org.openhab.binding.gce.internal.model.PortDefinition;
|
||||
import org.openhab.binding.gce.internal.model.StatusFile;
|
||||
import org.openhab.binding.gce.internal.model.StatusFileAccessor;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* The {@link Ipx800DeviceConnector} is responsible for connecting,
|
||||
@ -35,156 +40,161 @@ import org.slf4j.LoggerFactory;
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Ipx800DeviceConnector extends Thread {
|
||||
private static final int DEFAULT_SOCKET_TIMEOUT_MS = 5000;
|
||||
private static final int DEFAULT_RECONNECT_TIMEOUT_MS = 5000;
|
||||
private static final int DEFAULT_SOCKET_TIMEOUT_MS = 10000;
|
||||
private static final int MAX_KEEPALIVE_FAILURE = 3;
|
||||
private static final String ENDL = "\r\n";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(Ipx800DeviceConnector.class);
|
||||
private final Random randomizer = new Random();
|
||||
|
||||
private final String hostname;
|
||||
private final int portNumber;
|
||||
|
||||
private Optional<M2MMessageParser> messageParser = Optional.empty();
|
||||
private Optional<Socket> socket = Optional.empty();
|
||||
private Optional<BufferedReader> input = Optional.empty();
|
||||
private Optional<PrintWriter> output = Optional.empty();
|
||||
private final M2MMessageParser parser;
|
||||
private final StatusFileAccessor statusAccessor;
|
||||
private final Ipx800EventListener listener;
|
||||
private final Socket socket;
|
||||
private final BufferedReader input;
|
||||
private final PrintWriter output;
|
||||
|
||||
private int failedKeepalive = 0;
|
||||
private boolean waitingKeepaliveResponse = false;
|
||||
private boolean interrupted = false;
|
||||
|
||||
public Ipx800DeviceConnector(String hostname, int portNumber, ThingUID uid) {
|
||||
public Ipx800DeviceConnector(String hostname, int portNumber, ThingUID uid, Ipx800EventListener listener)
|
||||
throws UnknownHostException, IOException {
|
||||
super("OH-binding-" + uid);
|
||||
this.hostname = hostname;
|
||||
this.portNumber = portNumber;
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
public synchronized void send(String message) {
|
||||
output.ifPresentOrElse(out -> {
|
||||
logger.debug("Sending '{}' to Ipx800", message);
|
||||
out.write(message + ENDL);
|
||||
out.flush();
|
||||
}, () -> logger.warn("Trying to send '{}' while the output stream is closed.", message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the ipx800
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void connect() throws IOException {
|
||||
disconnect();
|
||||
this.listener = listener;
|
||||
|
||||
logger.debug("Connecting to {}:{}...", hostname, portNumber);
|
||||
Socket socket = new Socket(hostname, portNumber);
|
||||
socket.setSoTimeout(DEFAULT_SOCKET_TIMEOUT_MS);
|
||||
socket.getInputStream().skip(socket.getInputStream().available());
|
||||
this.socket = Optional.of(socket);
|
||||
this.socket = socket;
|
||||
|
||||
input = Optional.of(new BufferedReader(new InputStreamReader(socket.getInputStream())));
|
||||
output = Optional.of(new PrintWriter(socket.getOutputStream(), true));
|
||||
output = new PrintWriter(socket.getOutputStream(), true);
|
||||
input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
parser = new M2MMessageParser(listener);
|
||||
statusAccessor = new StatusFileAccessor(hostname);
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect the device
|
||||
*
|
||||
* Stop the
|
||||
* device thread
|
||||
*/
|
||||
private void disconnect() {
|
||||
logger.debug("Disconnecting");
|
||||
|
||||
input.ifPresent(in -> {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
input = Optional.empty();
|
||||
});
|
||||
|
||||
output.ifPresent(PrintWriter::close);
|
||||
output = Optional.empty();
|
||||
|
||||
socket.ifPresent(client -> {
|
||||
try {
|
||||
client.close();
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
socket = Optional.empty();
|
||||
});
|
||||
|
||||
logger.debug("Disconnected");
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the device thread
|
||||
*/
|
||||
public void dispose() {
|
||||
interrupt();
|
||||
disconnect();
|
||||
interrupted = true;
|
||||
}
|
||||
|
||||
public synchronized void send(String message) {
|
||||
logger.debug("Sending '{}' to Ipx800", message);
|
||||
output.println(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an arbitrary keepalive command which cause the IPX to send an update.
|
||||
* If we don't receive the update maxKeepAliveFailure time, the connection is closed and reopened
|
||||
*
|
||||
* Send a
|
||||
* random keepalive
|
||||
* command which
|
||||
* cause the
|
||||
* IPX to
|
||||
* send an update.*
|
||||
* If we don't
|
||||
* receive the
|
||||
* update maxKeepAliveFailure time,
|
||||
* the connection
|
||||
* is closed
|
||||
*/
|
||||
|
||||
private void sendKeepalive() {
|
||||
output.ifPresent(out -> {
|
||||
if (waitingKeepaliveResponse) {
|
||||
failedKeepalive++;
|
||||
logger.debug("Sending keepalive, attempt {}", failedKeepalive);
|
||||
} else {
|
||||
failedKeepalive = 0;
|
||||
logger.debug("Sending keepalive");
|
||||
}
|
||||
out.println("GetIn01");
|
||||
out.flush();
|
||||
waitingKeepaliveResponse = true;
|
||||
});
|
||||
PortDefinition pd = PortDefinition.values()[randomizer.nextInt(PortDefinition.AS_SET.size())];
|
||||
String command = "%s%d".formatted(pd.m2mCommand, randomizer.nextInt(pd.quantity) + 1);
|
||||
|
||||
if (waitingKeepaliveResponse) {
|
||||
failedKeepalive++;
|
||||
logger.debug("Sending keepalive {}, attempt {}", command, failedKeepalive);
|
||||
} else {
|
||||
failedKeepalive = 0;
|
||||
logger.debug("Sending keepalive {}", command);
|
||||
}
|
||||
|
||||
output.println(command);
|
||||
parser.setExpectedResponse(command);
|
||||
|
||||
waitingKeepaliveResponse = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
waitingKeepaliveResponse = false;
|
||||
failedKeepalive = 0;
|
||||
connect();
|
||||
while (!interrupted()) {
|
||||
if (failedKeepalive > MAX_KEEPALIVE_FAILURE) {
|
||||
throw new IOException("Max keep alive attempts has been reached");
|
||||
}
|
||||
input.ifPresent(in -> {
|
||||
try {
|
||||
String command = in.readLine();
|
||||
waitingKeepaliveResponse = false;
|
||||
messageParser.ifPresent(parser -> parser.unsolicitedUpdate(command));
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
});
|
||||
while (!interrupted) {
|
||||
if (failedKeepalive > MAX_KEEPALIVE_FAILURE) {
|
||||
interrupted = true;
|
||||
listener.errorOccurred(new IOException("Max keep alive attempts has been reached"));
|
||||
}
|
||||
disconnect();
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
try {
|
||||
Thread.sleep(DEFAULT_RECONNECT_TIMEOUT_MS);
|
||||
} catch (InterruptedException e) {
|
||||
dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleException(Exception e) {
|
||||
if (!interrupted()) {
|
||||
if (e instanceof SocketTimeoutException) {
|
||||
try {
|
||||
String command = input.readLine();
|
||||
waitingKeepaliveResponse = false;
|
||||
parser.unsolicitedUpdate(command);
|
||||
} catch (SocketTimeoutException e) {
|
||||
sendKeepalive();
|
||||
return;
|
||||
} else if (e instanceof IOException) {
|
||||
logger.warn("Communication error: '{}'. Will retry in {} ms", e, DEFAULT_RECONNECT_TIMEOUT_MS);
|
||||
} catch (IOException e) {
|
||||
interrupted = true;
|
||||
listener.errorOccurred(e);
|
||||
}
|
||||
}
|
||||
if (output instanceof PrintWriter out) {
|
||||
out.close();
|
||||
}
|
||||
|
||||
if (input instanceof BufferedReader in) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
logger.warn("Exception input stream: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (socket instanceof Socket client) {
|
||||
try {
|
||||
logger.debug("Closing socket");
|
||||
client.close();
|
||||
} catch (IOException e) {
|
||||
logger.warn("Exception closing socket: {}", e.getMessage());
|
||||
}
|
||||
messageParser.ifPresent(parser -> parser.errorOccurred(e));
|
||||
}
|
||||
}
|
||||
|
||||
public void setParser(M2MMessageParser parser) {
|
||||
this.messageParser = Optional.of(parser);
|
||||
public StatusFile readStatusFile() throws SAXException, IOException {
|
||||
return statusAccessor.read();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Set output
|
||||
* of the
|
||||
* device sending
|
||||
* the corresponding command**
|
||||
*
|
||||
* @param targetPort
|
||||
* @param targetValue
|
||||
*/
|
||||
public void setOutput(String targetPort, int targetValue, boolean pulse) {
|
||||
logger.debug("Sending {} to {}", targetValue, targetPort);
|
||||
String command = "Set%02d%s%s".formatted(Integer.parseInt(targetPort), targetValue, pulse ? "p" : "");
|
||||
send(command);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Resets the
|
||||
* counter value to 0**
|
||||
*
|
||||
* @param targetCounter
|
||||
*/
|
||||
public void resetCounter(int targetCounter) {
|
||||
logger.debug("Resetting counter {} to 0", targetCounter);
|
||||
send("ResetCount%d".formatted(targetCounter));
|
||||
}
|
||||
|
||||
public void resetPLC() {
|
||||
send("Reset");
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
@ -14,30 +14,29 @@ package org.openhab.binding.gce.internal.handler;
|
||||
|
||||
import static org.openhab.binding.gce.internal.GCEBindingConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.time.Duration;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.gce.internal.action.Ipx800Actions;
|
||||
import org.openhab.binding.gce.internal.config.AnalogInputConfiguration;
|
||||
import org.openhab.binding.gce.internal.config.DigitalInputConfiguration;
|
||||
import org.openhab.binding.gce.internal.config.Ipx800Configuration;
|
||||
import org.openhab.binding.gce.internal.config.RelayOutputConfiguration;
|
||||
import org.openhab.binding.gce.internal.model.M2MMessageParser;
|
||||
import org.openhab.binding.gce.internal.model.PortData;
|
||||
import org.openhab.binding.gce.internal.model.PortDefinition;
|
||||
import org.openhab.binding.gce.internal.model.StatusFileInterpreter;
|
||||
import org.openhab.binding.gce.internal.model.StatusFileInterpreter.StatusEntry;
|
||||
import org.openhab.binding.gce.internal.model.StatusFile;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.CoreItemFactory;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
@ -58,9 +57,9 @@ import org.openhab.core.thing.type.ChannelKind;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* The {@link Ipx800v3Handler} is responsible for handling commands, which are
|
||||
@ -74,37 +73,13 @@ public class Ipx800v3Handler extends BaseThingHandler implements Ipx800EventList
|
||||
private static final double ANALOG_SAMPLING = 0.000050354;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(Ipx800v3Handler.class);
|
||||
private final Map<ChannelUID, PortData> portDatas = new HashMap<>();
|
||||
|
||||
private Optional<Ipx800DeviceConnector> connector = Optional.empty();
|
||||
private Optional<M2MMessageParser> parser = Optional.empty();
|
||||
private Optional<ScheduledFuture<?>> refreshJob = Optional.empty();
|
||||
|
||||
private final Map<String, PortData> portDatas = new HashMap<>();
|
||||
|
||||
private class LongPressEvaluator implements Runnable {
|
||||
private final ZonedDateTime referenceTime;
|
||||
private final String port;
|
||||
private final String eventChannelId;
|
||||
|
||||
public LongPressEvaluator(Channel channel, String port, PortData portData) {
|
||||
this.referenceTime = portData.getTimestamp();
|
||||
this.port = port;
|
||||
this.eventChannelId = channel.getUID().getId() + PROPERTY_SEPARATOR + TRIGGER_CONTACT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
PortData currentData = portDatas.get(port);
|
||||
if (currentData != null && currentData.getValue() == 1
|
||||
&& referenceTime.equals(currentData.getTimestamp())) {
|
||||
triggerChannel(eventChannelId, EVENT_LONG_PRESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
private @Nullable Ipx800DeviceConnector deviceConnector;
|
||||
private List<ScheduledFuture<?>> jobs = new ArrayList<>();
|
||||
|
||||
public Ipx800v3Handler(Thing thing) {
|
||||
super(thing);
|
||||
logger.debug("Create an IPX800 Handler for thing '{}'", getThing().getUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -112,47 +87,76 @@ public class Ipx800v3Handler extends BaseThingHandler implements Ipx800EventList
|
||||
logger.debug("Initializing IPX800 handler for uid '{}'", getThing().getUID());
|
||||
|
||||
Ipx800Configuration config = getConfigAs(Ipx800Configuration.class);
|
||||
StatusFileInterpreter statusFile = new StatusFileInterpreter(config.hostname, this);
|
||||
|
||||
if (thing.getProperties().isEmpty()) {
|
||||
updateProperties(Map.of(Thing.PROPERTY_VENDOR, "GCE Electronics", Thing.PROPERTY_FIRMWARE_VERSION,
|
||||
statusFile.getElement(StatusEntry.VERSION), Thing.PROPERTY_MAC_ADDRESS,
|
||||
statusFile.getElement(StatusEntry.CONFIG_MAC)));
|
||||
try {
|
||||
deviceConnector = new Ipx800DeviceConnector(config.hostname, config.portNumber, getThing().getUID(), this);
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
jobs.add(scheduler.scheduleWithFixedDelay(this::readStatusFile, 1500, config.pullInterval,
|
||||
TimeUnit.MILLISECONDS));
|
||||
} catch (UnknownHostException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
|
||||
} catch (IOException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void readStatusFile() {
|
||||
if (deviceConnector instanceof Ipx800DeviceConnector connector) {
|
||||
StatusFile status = null;
|
||||
try {
|
||||
status = connector.readStatusFile();
|
||||
} catch (SAXException | IOException e) {
|
||||
logger.warn("Unable to read status file for {}", thing.getUID());
|
||||
}
|
||||
|
||||
if (Thread.State.NEW.equals(connector.getState())) {
|
||||
setProperties(status);
|
||||
updateChannels(status);
|
||||
connector.start();
|
||||
}
|
||||
|
||||
if (status instanceof StatusFile statusFile) {
|
||||
PortDefinition.AS_SET.forEach(portDefinition -> statusFile.getPorts(portDefinition).forEach(
|
||||
(portNum, value) -> dataReceived("%s%d".formatted(portDefinition.portName, portNum), value)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateChannels(@Nullable StatusFile status) {
|
||||
List<Channel> channels = new ArrayList<>(getThing().getChannels());
|
||||
PortDefinition.asStream().forEach(portDefinition -> {
|
||||
int nbElements = statusFile.getMaxNumberofNodeType(portDefinition);
|
||||
PortDefinition.AS_SET.forEach(portDefinition -> {
|
||||
int nbElements = status != null ? status.getPorts(portDefinition).size() : portDefinition.quantity;
|
||||
for (int i = 0; i < nbElements; i++) {
|
||||
ChannelUID portChannelUID = createChannels(portDefinition, i, channels);
|
||||
portDatas.put(portChannelUID.getId(), new PortData());
|
||||
portDatas.put(portChannelUID, new PortData());
|
||||
}
|
||||
});
|
||||
|
||||
updateThing(editThing().withChannels(channels).build());
|
||||
}
|
||||
|
||||
connector = Optional.of(new Ipx800DeviceConnector(config.hostname, config.portNumber, getThing().getUID()));
|
||||
parser = Optional.of(new M2MMessageParser(connector.get(), this));
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
|
||||
refreshJob = Optional.of(
|
||||
scheduler.scheduleWithFixedDelay(statusFile::read, 3000, config.pullInterval, TimeUnit.MILLISECONDS));
|
||||
|
||||
connector.get().start();
|
||||
private void setProperties(@Nullable StatusFile status) {
|
||||
Map<String, String> properties = new HashMap<>(thing.getProperties());
|
||||
properties.put(Thing.PROPERTY_VENDOR, "GCE Electronics");
|
||||
if (status != null) {
|
||||
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, status.getVersion());
|
||||
properties.put(Thing.PROPERTY_MAC_ADDRESS, status.getMac());
|
||||
}
|
||||
updateProperties(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
refreshJob.ifPresent(job -> job.cancel(true));
|
||||
refreshJob = Optional.empty();
|
||||
jobs.forEach(job -> job.cancel(true));
|
||||
jobs.clear();
|
||||
|
||||
connector.ifPresent(Ipx800DeviceConnector::dispose);
|
||||
connector = Optional.empty();
|
||||
|
||||
parser = Optional.empty();
|
||||
if (deviceConnector instanceof Ipx800DeviceConnector connector) {
|
||||
connector.dispose();
|
||||
deviceConnector = null;
|
||||
}
|
||||
|
||||
portDatas.values().stream().forEach(PortData::dispose);
|
||||
portDatas.clear();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -171,29 +175,25 @@ public class Ipx800v3Handler extends BaseThingHandler implements Ipx800EventList
|
||||
ChannelUID mainChannelUID = new ChannelUID(groupUID, ndx);
|
||||
ChannelTypeUID channelType = new ChannelTypeUID(BINDING_ID, advancedChannelTypeName);
|
||||
switch (portDefinition) {
|
||||
case ANALOG:
|
||||
case ANALOG -> {
|
||||
addIfChannelAbsent(ChannelBuilder.create(mainChannelUID, CoreItemFactory.NUMBER)
|
||||
.withLabel("Analog Input " + ndx).withType(channelType), channels);
|
||||
addIfChannelAbsent(
|
||||
ChannelBuilder.create(new ChannelUID(groupUID, ndx + "-voltage"), "Number:ElectricPotential")
|
||||
.withType(new ChannelTypeUID(BINDING_ID, CHANNEL_VOLTAGE)).withLabel("Voltage " + ndx),
|
||||
channels);
|
||||
break;
|
||||
case CONTACT:
|
||||
}
|
||||
case CONTACT -> {
|
||||
addIfChannelAbsent(ChannelBuilder.create(mainChannelUID, CoreItemFactory.CONTACT)
|
||||
.withLabel("Contact " + ndx).withType(channelType), channels);
|
||||
addIfChannelAbsent(ChannelBuilder.create(new ChannelUID(groupUID, ndx + "-event"), null)
|
||||
.withType(new ChannelTypeUID(BINDING_ID, TRIGGER_CONTACT + (portIndex < 8 ? "" : "Advanced")))
|
||||
.withLabel("Contact " + ndx + " Event").withKind(ChannelKind.TRIGGER), channels);
|
||||
break;
|
||||
case COUNTER:
|
||||
addIfChannelAbsent(ChannelBuilder.create(mainChannelUID, CoreItemFactory.NUMBER)
|
||||
.withLabel("Counter " + ndx).withType(channelType), channels);
|
||||
break;
|
||||
case RELAY:
|
||||
addIfChannelAbsent(ChannelBuilder.create(mainChannelUID, CoreItemFactory.SWITCH)
|
||||
.withLabel("Relay " + ndx).withType(channelType), channels);
|
||||
break;
|
||||
}
|
||||
case COUNTER -> addIfChannelAbsent(ChannelBuilder.create(mainChannelUID, CoreItemFactory.NUMBER)
|
||||
.withLabel("Counter " + ndx).withType(channelType), channels);
|
||||
case RELAY -> addIfChannelAbsent(ChannelBuilder.create(mainChannelUID, CoreItemFactory.SWITCH)
|
||||
.withLabel("Relay " + ndx).withType(channelType), channels);
|
||||
}
|
||||
|
||||
addIfChannelAbsent(ChannelBuilder.create(new ChannelUID(groupUID, ndx + "-duration"), "Number:Time")
|
||||
@ -209,8 +209,8 @@ public class Ipx800v3Handler extends BaseThingHandler implements Ipx800EventList
|
||||
}
|
||||
|
||||
private boolean ignoreCondition(double newValue, PortData portData, Configuration configuration,
|
||||
PortDefinition portDefinition, ZonedDateTime now) {
|
||||
if (!portData.isInitializing()) { // Always accept if portData is not initialized
|
||||
PortDefinition portDefinition, Instant now) {
|
||||
if (portData.isInitialized()) { // Always accept if portData is not initialized
|
||||
double prevValue = portData.getValue();
|
||||
if (newValue == prevValue) { // Always reject if the value did not change
|
||||
return true;
|
||||
@ -231,68 +231,62 @@ public class Ipx800v3Handler extends BaseThingHandler implements Ipx800EventList
|
||||
@Override
|
||||
public void dataReceived(String port, double value) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
Channel channel = thing.getChannel(PortDefinition.asChannelId(port));
|
||||
if (channel != null) {
|
||||
String channelId = channel.getUID().getId();
|
||||
String groupId = channel.getUID().getGroupId();
|
||||
PortData portData = portDatas.get(channelId);
|
||||
if (portData != null && groupId != null) {
|
||||
ZonedDateTime now = ZonedDateTime.now(ZoneId.systemDefault());
|
||||
long sinceLastChange = Duration.between(portData.getTimestamp(), now).toMillis();
|
||||
if (thing.getChannel(PortDefinition.asChannelId(port)) instanceof Channel channel) {
|
||||
ChannelUID channelUID = channel.getUID();
|
||||
String channelId = channelUID.getId();
|
||||
|
||||
if (portDatas.get(channelUID) instanceof PortData portData
|
||||
&& channelUID.getGroupId() instanceof String groupId) {
|
||||
Instant now = Instant.now();
|
||||
Configuration configuration = channel.getConfiguration();
|
||||
PortDefinition portDefinition = PortDefinition.fromGroupId(groupId);
|
||||
if (ignoreCondition(value, portData, configuration, portDefinition, now)) {
|
||||
logger.debug("Ignore condition met for port '{}' with data '{}'", port, value);
|
||||
logger.trace("Ignore condition met for port '{}' with data '{}'", port, value);
|
||||
return;
|
||||
}
|
||||
logger.debug("About to update port '{}' with data '{}'", port, value);
|
||||
State state = UnDefType.NULL;
|
||||
switch (portDefinition) {
|
||||
case COUNTER:
|
||||
state = new DecimalType(value);
|
||||
break;
|
||||
case RELAY:
|
||||
state = OnOffType.from(value == 1);
|
||||
break;
|
||||
case ANALOG:
|
||||
state = new DecimalType(value);
|
||||
long sinceLastChange = Duration.between(portData.getTimestamp(), now).toMillis();
|
||||
State state = switch (portDefinition) {
|
||||
case COUNTER -> new DecimalType(value);
|
||||
case RELAY -> OnOffType.from(value == 1);
|
||||
case ANALOG -> {
|
||||
updateIfLinked(channelId + PROPERTY_SEPARATOR + CHANNEL_VOLTAGE,
|
||||
new QuantityType<>(value * ANALOG_SAMPLING, Units.VOLT));
|
||||
break;
|
||||
case CONTACT:
|
||||
DigitalInputConfiguration config = configuration.as(DigitalInputConfiguration.class);
|
||||
yield new DecimalType(value);
|
||||
}
|
||||
case CONTACT -> {
|
||||
portData.cancelPulsing();
|
||||
state = value == 1 ? OpenClosedType.CLOSED : OpenClosedType.OPEN;
|
||||
switch ((OpenClosedType) state) {
|
||||
case CLOSED:
|
||||
if (config.longPressTime != 0 && !portData.isInitializing()) {
|
||||
scheduler.schedule(new LongPressEvaluator(channel, port, portData),
|
||||
config.longPressTime, TimeUnit.MILLISECONDS);
|
||||
} else if (config.pulsePeriod != 0) {
|
||||
portData.setPulsing(scheduler.scheduleWithFixedDelay(() -> {
|
||||
triggerPushButtonChannel(channel, EVENT_PULSE);
|
||||
}, config.pulsePeriod, config.pulsePeriod, TimeUnit.MILLISECONDS));
|
||||
if (config.pulseTimeout != 0) {
|
||||
scheduler.schedule(portData::cancelPulsing, config.pulseTimeout,
|
||||
TimeUnit.MILLISECONDS);
|
||||
DigitalInputConfiguration config = configuration.as(DigitalInputConfiguration.class);
|
||||
|
||||
if (value == 1) { // CLOSED
|
||||
if (config.longPressTime != 0 && portData.isInitialized()) {
|
||||
jobs.add(scheduler.schedule(() -> {
|
||||
if (portData.getValue() == 1 && now.equals(portData.getTimestamp())) {
|
||||
String eventChannelId = "%s-%s".formatted(channelUID.getId(), TRIGGER_CONTACT);
|
||||
triggerChannel(eventChannelId, EVENT_LONG_PRESS);
|
||||
}
|
||||
}, config.longPressTime, TimeUnit.MILLISECONDS));
|
||||
} else if (config.pulsePeriod != 0) {
|
||||
portData.setPulsing(scheduler.scheduleWithFixedDelay(() -> {
|
||||
triggerPushButtonChannel(channel, EVENT_PULSE);
|
||||
}, config.pulsePeriod, config.pulsePeriod, TimeUnit.MILLISECONDS));
|
||||
if (config.pulseTimeout != 0) {
|
||||
portData.setPulseCanceler(scheduler.schedule(portData::cancelPulsing,
|
||||
config.pulseTimeout, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
break;
|
||||
case OPEN:
|
||||
if (!portData.isInitializing() && config.longPressTime != 0
|
||||
&& sinceLastChange < config.longPressTime) {
|
||||
triggerPushButtonChannel(channel, EVENT_SHORT_PRESS);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (portData.isInitialized() && sinceLastChange < config.longPressTime) {
|
||||
triggerPushButtonChannel(channel, EVENT_SHORT_PRESS);
|
||||
}
|
||||
if (!portData.isInitializing()) {
|
||||
if (portData.isInitialized()) {
|
||||
triggerPushButtonChannel(channel, value == 1 ? EVENT_PRESSED : EVENT_RELEASED);
|
||||
}
|
||||
break;
|
||||
}
|
||||
yield value == 1 ? OpenClosedType.CLOSED : OpenClosedType.OPEN;
|
||||
}
|
||||
};
|
||||
|
||||
updateIfLinked(channelId, state);
|
||||
if (!portData.isInitializing()) {
|
||||
if (portData.isInitialized()) {
|
||||
updateIfLinked(channelId + PROPERTY_SEPARATOR + CHANNEL_LAST_STATE_DURATION,
|
||||
new QuantityType<>(sinceLastChange / 1000, Units.SECOND));
|
||||
}
|
||||
@ -320,20 +314,18 @@ public class Ipx800v3Handler extends BaseThingHandler implements Ipx800EventList
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.debug("Received channel: {}, command: {}", channelUID, command);
|
||||
|
||||
Channel channel = thing.getChannel(channelUID.getId());
|
||||
String groupId = channelUID.getGroupId();
|
||||
|
||||
if (channel == null || groupId == null) {
|
||||
return;
|
||||
}
|
||||
if (command instanceof OnOffType onOffCommand && isValidPortId(channelUID)
|
||||
&& PortDefinition.fromGroupId(groupId) == PortDefinition.RELAY) {
|
||||
if (thing.getChannel(channelUID.getId()) instanceof Channel channel
|
||||
&& channelUID.getGroupId() instanceof String groupId //
|
||||
&& command instanceof OnOffType onOffCommand //
|
||||
&& isValidPortId(channelUID) //
|
||||
&& PortDefinition.RELAY.equals(PortDefinition.fromGroupId(groupId))
|
||||
&& deviceConnector instanceof Ipx800DeviceConnector connector) {
|
||||
RelayOutputConfiguration config = channel.getConfiguration().as(RelayOutputConfiguration.class);
|
||||
String id = channelUID.getIdWithoutGroup();
|
||||
parser.ifPresent(p -> p.setOutput(id, onOffCommand == OnOffType.ON ? 1 : 0, config.pulse));
|
||||
return;
|
||||
connector.setOutput(channelUID.getIdWithoutGroup(), OnOffType.ON.equals(onOffCommand) ? 1 : 0,
|
||||
config.pulse);
|
||||
} else {
|
||||
logger.debug("Can not handle command '{}' on channel '{}'", command, channelUID);
|
||||
}
|
||||
logger.debug("Can not handle command '{}' on channel '{}'", command, channelUID);
|
||||
}
|
||||
|
||||
private boolean isValidPortId(ChannelUID channelUID) {
|
||||
@ -341,11 +333,15 @@ public class Ipx800v3Handler extends BaseThingHandler implements Ipx800EventList
|
||||
}
|
||||
|
||||
public void resetCounter(int counter) {
|
||||
parser.ifPresent(p -> p.resetCounter(counter));
|
||||
if (deviceConnector instanceof Ipx800DeviceConnector connector) {
|
||||
connector.resetCounter(counter);
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
parser.ifPresent(M2MMessageParser::resetPLC);
|
||||
if (deviceConnector instanceof Ipx800DeviceConnector connector) {
|
||||
connector.resetPLC();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
@ -15,7 +15,6 @@ package org.openhab.binding.gce.internal.model;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.gce.internal.handler.Ipx800DeviceConnector;
|
||||
import org.openhab.binding.gce.internal.handler.Ipx800EventListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -33,23 +32,19 @@ public class M2MMessageParser {
|
||||
.compile("I=" + IO_DESCRIPTOR + "&O=" + IO_DESCRIPTOR + "&([AC]\\d{1,2}=\\d+&)*[^I]*");
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(M2MMessageParser.class);
|
||||
private final Ipx800DeviceConnector connector;
|
||||
private final Ipx800EventListener listener;
|
||||
|
||||
private String expectedResponse = "";
|
||||
|
||||
public M2MMessageParser(Ipx800DeviceConnector connector, Ipx800EventListener listener) {
|
||||
this.connector = connector;
|
||||
public M2MMessageParser(Ipx800EventListener listener) {
|
||||
this.listener = listener;
|
||||
connector.setParser(this);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
public void unsolicitedUpdate(String data) {
|
||||
if (IO_PATTERN.matcher(data).matches()) {
|
||||
if ("OK".equals(data)) { // If OK, do nothing special
|
||||
} else if ("? Bad command".equals(data)) {
|
||||
logger.warn("{}", data);
|
||||
} else if (IO_PATTERN.matcher(data).matches()) {
|
||||
PortDefinition portDefinition = PortDefinition.fromM2MCommand(expectedResponse);
|
||||
decodeDataLine(portDefinition, data);
|
||||
} else if (VALIDATION_PATTERN.matcher(data).matches()) {
|
||||
@ -67,65 +62,36 @@ public class M2MMessageParser {
|
||||
portNumShift = 0; // Align counters on 1 based array
|
||||
case ANALOG: {
|
||||
int portNumber = Integer.parseInt(statusPart[0].substring(1)) + portNumShift;
|
||||
setStatus(portDefinition.getPortName() + portNumber, Double.parseDouble(statusPart[1]));
|
||||
setStatus(portDefinition.portName + portNumber, Double.parseDouble(statusPart[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!expectedResponse.isEmpty()) {
|
||||
setStatus(expectedResponse, Double.parseDouble(data));
|
||||
} else {
|
||||
logger.warn("Unable to handle data received: {}", data);
|
||||
}
|
||||
|
||||
expectedResponse = "";
|
||||
}
|
||||
|
||||
private void decodeDataLine(PortDefinition portDefinition, String data) {
|
||||
for (int count = 0; count < data.length(); count++) {
|
||||
setStatus(portDefinition.getPortName() + (count + 1), (double) data.charAt(count) - '0');
|
||||
setStatus(portDefinition.portName + (count + 1), (double) data.charAt(count) - '0');
|
||||
}
|
||||
}
|
||||
|
||||
private void setStatus(String port, double value) {
|
||||
logger.debug("Received {} : {}", port, value);
|
||||
logger.debug("Received {} on port {}", value, port);
|
||||
listener.dataReceived(port, value);
|
||||
}
|
||||
|
||||
public void setExpectedResponse(String expectedResponse) {
|
||||
if (expectedResponse.endsWith("s")) { // GetInputs or GetOutputs
|
||||
this.expectedResponse = expectedResponse;
|
||||
} else { // GetAnx or GetCountx
|
||||
PortDefinition portType = PortDefinition.fromM2MCommand(expectedResponse);
|
||||
this.expectedResponse = expectedResponse.replaceAll(portType.getM2mCommand(), portType.getPortName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set output of the device sending the corresponding command
|
||||
*
|
||||
* @param targetPort
|
||||
* @param targetValue
|
||||
*/
|
||||
public void setOutput(String targetPort, int targetValue, boolean pulse) {
|
||||
logger.debug("Sending {} to {}", targetValue, targetPort);
|
||||
String command = String.format("Set%02d%s%s", Integer.parseInt(targetPort), targetValue, pulse ? "p" : "");
|
||||
connector.send(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the counter value to 0
|
||||
*
|
||||
* @param targetCounter
|
||||
*/
|
||||
public void resetCounter(int targetCounter) {
|
||||
logger.debug("Resetting counter {} to 0", targetCounter);
|
||||
connector.send(String.format("ResetCount%d", targetCounter));
|
||||
}
|
||||
|
||||
public void errorOccurred(Exception e) {
|
||||
logger.warn("Error received from connector : {}", e.getMessage());
|
||||
listener.errorOccurred(e);
|
||||
}
|
||||
|
||||
public void resetPLC() {
|
||||
connector.send("Reset");
|
||||
// GetAnx or GetCountx
|
||||
PortDefinition portType = PortDefinition.fromM2MCommand(expectedResponse);
|
||||
this.expectedResponse = expectedResponse.replaceAll(portType.m2mCommand, portType.portName);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
@ -12,11 +12,11 @@
|
||||
*/
|
||||
package org.openhab.binding.gce.internal.model;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Optional;
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link PortData} is responsible for holding data regarding current status of a port.
|
||||
@ -26,19 +26,31 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
@NonNullByDefault
|
||||
public class PortData {
|
||||
private double value = -1;
|
||||
private ZonedDateTime timestamp = ZonedDateTime.now();
|
||||
private Optional<ScheduledFuture<?>> pulsing = Optional.empty();
|
||||
private Instant timestamp = Instant.now();
|
||||
private @Nullable ScheduledFuture<?> pulsing;
|
||||
private @Nullable ScheduledFuture<?> pulseCanceler;
|
||||
|
||||
public void cancelPulsing() {
|
||||
pulsing.ifPresent(pulse -> pulse.cancel(true));
|
||||
pulsing = Optional.empty();
|
||||
if (pulsing instanceof ScheduledFuture job) {
|
||||
job.cancel(true);
|
||||
pulsing = null;
|
||||
}
|
||||
cancelCanceler();
|
||||
}
|
||||
|
||||
public void cancelCanceler() {
|
||||
if (pulseCanceler instanceof ScheduledFuture job) {
|
||||
job.cancel(true);
|
||||
pulseCanceler = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
cancelPulsing();
|
||||
cancelCanceler();
|
||||
}
|
||||
|
||||
public void setData(double value, ZonedDateTime timestamp) {
|
||||
public void setData(double value, Instant timestamp) {
|
||||
this.value = value;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
@ -47,15 +59,20 @@ public class PortData {
|
||||
return value;
|
||||
}
|
||||
|
||||
public ZonedDateTime getTimestamp() {
|
||||
public Instant getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setPulsing(ScheduledFuture<?> pulsing) {
|
||||
this.pulsing = Optional.of(pulsing);
|
||||
cancelPulsing();
|
||||
this.pulsing = pulsing;
|
||||
}
|
||||
|
||||
public boolean isInitializing() {
|
||||
return value == -1;
|
||||
public boolean isInitialized() {
|
||||
return value != -1;
|
||||
}
|
||||
|
||||
public void setPulseCanceler(ScheduledFuture<?> pulseCanceler) {
|
||||
this.pulseCanceler = pulseCanceler;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
@ -12,7 +12,7 @@
|
||||
*/
|
||||
package org.openhab.binding.gce.internal.model;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
@ -29,25 +29,19 @@ public enum PortDefinition {
|
||||
RELAY("led", "O", "GetOut", 8),
|
||||
CONTACT("btn", "I", "GetIn", 8);
|
||||
|
||||
private final String nodeName; // Name used in the status xml file
|
||||
private final String portName; // Name used by the M2M protocol
|
||||
private final String m2mCommand; // associated M2M command
|
||||
private final int quantity; // base number of ports
|
||||
public final String nodeName; // Name used in the status xml file
|
||||
public final String portName; // Name used by the M2M protocol
|
||||
public final String m2mCommand; // associated M2M command
|
||||
public final int quantity; // base number of ports
|
||||
|
||||
PortDefinition(String nodeName, String portName, String m2mCommand, int quantity) {
|
||||
private PortDefinition(String nodeName, String portName, String m2mCommand, int quantity) {
|
||||
this.nodeName = nodeName;
|
||||
this.portName = portName;
|
||||
this.m2mCommand = m2mCommand;
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public String getNodeName() {
|
||||
return nodeName;
|
||||
}
|
||||
|
||||
public String getPortName() {
|
||||
return portName;
|
||||
}
|
||||
public static final EnumSet<PortDefinition> AS_SET = EnumSet.allOf(PortDefinition.class);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
@ -58,20 +52,12 @@ public enum PortDefinition {
|
||||
return id >= quantity;
|
||||
}
|
||||
|
||||
public String getM2mCommand() {
|
||||
return m2mCommand;
|
||||
}
|
||||
|
||||
public static Stream<PortDefinition> asStream() {
|
||||
return Stream.of(PortDefinition.values());
|
||||
}
|
||||
|
||||
public static PortDefinition fromM2MCommand(String m2mCommand) {
|
||||
return asStream().filter(v -> m2mCommand.startsWith(v.m2mCommand)).findFirst().get();
|
||||
return AS_SET.stream().filter(v -> m2mCommand.startsWith(v.m2mCommand)).findFirst().get();
|
||||
}
|
||||
|
||||
public static PortDefinition fromPortName(String portName) {
|
||||
return asStream().filter(v -> portName.startsWith(v.portName)).findFirst().get();
|
||||
return AS_SET.stream().filter(v -> portName.startsWith(v.portName)).findFirst().get();
|
||||
}
|
||||
|
||||
public static PortDefinition fromGroupId(String groupId) {
|
||||
@ -80,7 +66,7 @@ public enum PortDefinition {
|
||||
|
||||
public static String asChannelId(String portDefinition) {
|
||||
String portKind = portDefinition.substring(0, 1);
|
||||
PortDefinition result = asStream().filter(v -> v.portName.startsWith(portKind)).findFirst().get();
|
||||
return result.toString() + "#" + portDefinition.substring(1);
|
||||
PortDefinition result = AS_SET.stream().filter(v -> v.portName.equals(portKind)).findFirst().get();
|
||||
return "%s#%s".formatted(result.toString(), portDefinition.substring(1));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.gce.internal.model;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
/**
|
||||
* This class takes care of interpreting the status.xml file
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class StatusFile {
|
||||
private final Logger logger = LoggerFactory.getLogger(StatusFile.class);
|
||||
private final Element root;
|
||||
private final NodeList childs;
|
||||
|
||||
public StatusFile(Document doc) {
|
||||
this.root = doc.getDocumentElement();
|
||||
root.normalize();
|
||||
this.childs = root.getChildNodes();
|
||||
}
|
||||
|
||||
public String getMac() {
|
||||
return root.getElementsByTagName("config_mac").item(0).getTextContent();
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return root.getElementsByTagName("version").item(0).getTextContent();
|
||||
}
|
||||
|
||||
public Map<Integer, Double> getPorts(PortDefinition portDefinition) {
|
||||
Map<Integer, Double> result = new HashMap<>();
|
||||
|
||||
String searched = portDefinition.nodeName;
|
||||
|
||||
IntStream.range(0, childs.getLength()).boxed().map(childs::item)
|
||||
.filter(node -> node.getNodeName().startsWith(searched)).forEach(node -> {
|
||||
try {
|
||||
result.put(Integer.parseInt(node.getNodeName().replace(searched, "")) + 1,
|
||||
Double.parseDouble(node.getTextContent().replace("dn", "1").replace("up", "0")));
|
||||
} catch (NumberFormatException e) {
|
||||
logger.warn("{}", e.getMessage());
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.gce.internal.model;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* This class takes care of providing the IPX status file
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class StatusFileAccessor {
|
||||
private static final String URL_TEMPLATE = "http://%s/globalstatus.xml";
|
||||
|
||||
private final DocumentBuilder builder;
|
||||
private final String url;
|
||||
|
||||
public StatusFileAccessor(String hostname) {
|
||||
this.url = URL_TEMPLATE.formatted(hostname);
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setXIncludeAware(false);
|
||||
factory.setExpandEntityReferences(false);
|
||||
// see https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
|
||||
try {
|
||||
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
||||
builder = factory.newDocumentBuilder();
|
||||
} catch (ParserConfigurationException e) {
|
||||
throw new IllegalArgumentException("Error initializing StatusFileAccessor", e);
|
||||
}
|
||||
}
|
||||
|
||||
public StatusFile read() throws SAXException, IOException {
|
||||
return new StatusFile(builder.parse(url));
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2010-2025 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
@ -19,9 +19,9 @@ import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import javax.ws.rs.HttpMethod;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
@ -78,7 +78,7 @@ public class StatusFileInterpreter {
|
||||
|
||||
public void read() {
|
||||
try {
|
||||
String statusPage = HttpUtil.executeUrl("GET", url, 5000);
|
||||
String statusPage = HttpUtil.executeUrl(HttpMethod.GET, url, 5000);
|
||||
InputStream inputStream = new ByteArrayInputStream(statusPage.getBytes());
|
||||
Document document = builder.parse(inputStream);
|
||||
document.getDocumentElement().normalize();
|
||||
@ -92,13 +92,13 @@ public class StatusFileInterpreter {
|
||||
|
||||
private void pushDatas() {
|
||||
getRoot().ifPresent(root -> {
|
||||
PortDefinition.asStream().forEach(portDefinition -> {
|
||||
List<Node> xmlNodes = getMatchingNodes(root.getChildNodes(), portDefinition.getNodeName());
|
||||
PortDefinition.AS_SET.forEach(portDefinition -> {
|
||||
List<Node> xmlNodes = getMatchingNodes(root.getChildNodes(), portDefinition.nodeName);
|
||||
xmlNodes.forEach(xmlNode -> {
|
||||
String sPortNum = xmlNode.getNodeName().replace(portDefinition.getNodeName(), "");
|
||||
String sPortNum = xmlNode.getNodeName().replace(portDefinition.nodeName, "");
|
||||
int portNum = Integer.parseInt(sPortNum) + 1;
|
||||
double value = Double.parseDouble(xmlNode.getTextContent().replace("dn", "1").replace("up", "0"));
|
||||
listener.dataReceived(String.format("%s%d", portDefinition.getPortName(), portNum), value);
|
||||
listener.dataReceived("%s%d".formatted(portDefinition.portName, portNum), value);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -113,12 +113,12 @@ public class StatusFileInterpreter {
|
||||
private List<Node> getMatchingNodes(NodeList nodeList, String criteria) {
|
||||
return IntStream.range(0, nodeList.getLength()).boxed().map(nodeList::item)
|
||||
.filter(node -> node.getNodeName().startsWith(criteria)).sorted(Comparator.comparing(Node::getNodeName))
|
||||
.collect(Collectors.toList());
|
||||
.toList();
|
||||
}
|
||||
|
||||
public int getMaxNumberofNodeType(PortDefinition portDefinition) {
|
||||
return getRoot().map(root -> getMatchingNodes(root.getChildNodes(), portDefinition.getNodeName()).size())
|
||||
.orElse(0);
|
||||
return Objects.requireNonNull(getRoot()
|
||||
.map(root -> getMatchingNodes(root.getChildNodes(), portDefinition.nodeName).size()).orElse(0));
|
||||
}
|
||||
|
||||
private Optional<Element> getRoot() {
|
||||
|
@ -289,7 +289,7 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
|
||||
*/
|
||||
public String getName() {
|
||||
String result = channelConfiguration.getName();
|
||||
if (result.isBlank()) {
|
||||
if (result != null && result.isBlank()) {
|
||||
result = null;
|
||||
}
|
||||
|
||||
|
@ -166,6 +166,54 @@ public class SwitchTests extends AbstractComponentTests {
|
||||
assertPublished("zigbee2mqtt/th1/set/auto_lock", "AUTO");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSwitchNoName() {
|
||||
var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC), """
|
||||
{
|
||||
"availability": [
|
||||
{
|
||||
"topic": "zigbee2mqtt/bridge/state",
|
||||
"value_template": "{{ value_json.state }}"
|
||||
},
|
||||
{
|
||||
"topic": "zigbee2mqtt/Master Bedroom Subwoofer/availability",
|
||||
"value_template": "{{ value_json.state }}"
|
||||
}
|
||||
],
|
||||
"availability_mode": "all",
|
||||
"command_topic": "zigbee2mqtt/Master Bedroom Subwoofer/set",
|
||||
"device": {
|
||||
"configuration_url": "http://z2m:8084/#/device/0x00124b0029e7388c/info",
|
||||
"identifiers": [
|
||||
"zigbee2mqtt_0x00124b0029e7388c"
|
||||
],
|
||||
"manufacturer": "SONOFF",
|
||||
"model": "15A Zigbee smart plug (S40ZBTPB)",
|
||||
"name": "Master Bedroom Subwoofer",
|
||||
"sw_version": "1.1.0",
|
||||
"via_device": "zigbee2mqtt_bridge_0xe0798dfffe882ce4"
|
||||
},
|
||||
"name": null,
|
||||
"object_id": "master_bedroom_subwoofer",
|
||||
"origin": {
|
||||
"name": "Zigbee2MQTT",
|
||||
"sw": "1.42.0-dev",
|
||||
"url": "https://www.zigbee2mqtt.io"
|
||||
},
|
||||
"payload_off": "OFF",
|
||||
"payload_on": "ON",
|
||||
"state_topic": "zigbee2mqtt/Master Bedroom Subwoofer",
|
||||
"unique_id": "0x00124b0029e7388c_switch_zigbee2mqtt",
|
||||
"value_template": "{{ value_json.state }}"
|
||||
}
|
||||
""");
|
||||
|
||||
assertThat(component.channels.size(), is(1));
|
||||
assertThat(component.getName(), is("Master Bedroom Subwoofer"));
|
||||
assertChannel(component, Switch.SWITCH_CHANNEL_ID, "zigbee2mqtt/Master Bedroom Subwoofer",
|
||||
"zigbee2mqtt/Master Bedroom Subwoofer/set", "Master Bedroom Subwoofer", OnOffValue.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<String> getConfigTopics() {
|
||||
return Set.of(CONFIG_TOPIC);
|
||||
|
@ -111,7 +111,7 @@ public class RoomActions implements ThingActions {
|
||||
return;
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.info("Invalid mode passed : {} - {}", mode, e.getMessage());
|
||||
logger.info("Invalid mode passed: {} - {}", mode, e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -55,6 +55,6 @@ public class AircareApi extends RestManager {
|
||||
return station;
|
||||
}
|
||||
}
|
||||
throw new NetatmoException("Unexpected answer querying device '%s' : not found.", deviceId);
|
||||
throw new NetatmoException("Unexpected answer querying device '%s': not found.", deviceId);
|
||||
}
|
||||
}
|
||||
|
@ -65,14 +65,14 @@ public abstract class RestManager {
|
||||
URI uri = uriBuilder.build();
|
||||
T response = apiBridge.executeUri(uri, method, clazz, payload, contentType, 3);
|
||||
if (response instanceof ApiResponse.Ok apiResponseOk && apiResponseOk.failed()) {
|
||||
throw new NetatmoException("Command failed : %s for uri : %s", response.getStatus(), uri.toString());
|
||||
throw new NetatmoException("Command failed: %s for uri: %s", response.getStatus(), uri.toString());
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
private static UriBuilder appendParams(UriBuilder builder, @Nullable Object... params) {
|
||||
if (params.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("appendParams : params count must be even");
|
||||
throw new IllegalArgumentException("appendParams: params count must be even");
|
||||
}
|
||||
for (int i = 0; i < params.length; i += 2) {
|
||||
Object param1 = params[i];
|
||||
@ -82,7 +82,7 @@ public abstract class RestManager {
|
||||
builder.queryParam(query, param2);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("appendParams : even parameters must be Strings");
|
||||
throw new IllegalArgumentException("appendParams: even parameters must be Strings");
|
||||
}
|
||||
}
|
||||
return builder;
|
||||
|
@ -112,7 +112,7 @@ public class SecurityApi extends RestManager {
|
||||
try {
|
||||
return get(uriBuilder, Ping.class).getStatus();
|
||||
} catch (NetatmoException e) {
|
||||
logger.debug("Pinging {} failed : {}", vpnUrl, e.getMessage());
|
||||
logger.debug("Pinging {} failed: {}", vpnUrl, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ public class WeatherApi extends RestManager {
|
||||
return station;
|
||||
}
|
||||
}
|
||||
throw new NetatmoException("Unexpected answer searching device '%s' : not found.", deviceId);
|
||||
throw new NetatmoException("Unexpected answer searching device '%s': not found.", deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -98,7 +98,7 @@ public class WeatherApi extends RestManager {
|
||||
return station;
|
||||
}
|
||||
}
|
||||
throw new NetatmoException("Unexpected answer searching device '%s' : not found.", deviceId);
|
||||
throw new NetatmoException("Unexpected answer searching device '%s': not found.", deviceId);
|
||||
}
|
||||
|
||||
public Collection<NAMain> getFavoriteAndGuestStationsData() throws NetatmoException {
|
||||
|
@ -248,8 +248,8 @@ public class NetatmoConstants {
|
||||
}
|
||||
|
||||
// Radio signal quality thresholds
|
||||
static final int[] WIFI_SIGNAL_LEVELS = new int[] { 99, 84, 69, 54 }; // Resp : bad, average, good, full
|
||||
static final int[] RADIO_SIGNAL_LEVELS = new int[] { 90, 80, 70, 60 }; // Resp : low, medium, high, full
|
||||
static final int[] WIFI_SIGNAL_LEVELS = new int[] { 99, 84, 69, 54 }; // Resp: bad, average, good, full
|
||||
static final int[] RADIO_SIGNAL_LEVELS = new int[] { 90, 80, 70, 60 }; // Resp: low, medium, high, full
|
||||
|
||||
// Thermostat definitions
|
||||
public enum SetpointMode {
|
||||
|
@ -59,7 +59,7 @@ class NAPushTypeDeserializer implements JsonDeserializer<NAPushType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param apiName : Netatmo Object name (NSD, NACamera...)
|
||||
* @param apiName Netatmo Object name (NSD, NACamera...)
|
||||
* @return moduletype value if found, or else Unknown
|
||||
*/
|
||||
public static ModuleType fromNetatmoObject(String apiName) {
|
||||
@ -68,7 +68,7 @@ class NAPushTypeDeserializer implements JsonDeserializer<NAPushType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param apiName : Netatmo Event name (hush, off, on ...)
|
||||
* @param apiName Netatmo Event name (hush, off, on ...)
|
||||
* @return eventType value if found, or else Unknown
|
||||
*/
|
||||
public static EventType fromEvent(String apiName) {
|
||||
|
@ -114,7 +114,7 @@ public class EnergyCapability extends RestCapability<EnergyApi> {
|
||||
api.setThermpoint(energyId, roomId, mode, endtime, temp);
|
||||
handler.expireData();
|
||||
} catch (NetatmoException e) {
|
||||
logger.warn("Error setting room thermostat mode '{}' : {}", mode, e.getMessage());
|
||||
logger.warn("Error setting room thermostat mode '{}': {}", mode, e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -154,7 +154,7 @@ public class EnergyCapability extends RestCapability<EnergyApi> {
|
||||
}
|
||||
handler.expireData();
|
||||
} catch (NetatmoException e) {
|
||||
logger.warn("Error handling command '{}' : {}", command, e.getMessage());
|
||||
logger.warn("Error handling command '{}': {}", command, e.getMessage());
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn("Command '{}' sent to channel '{}' is not a valid setpoint mode.", command, channelName);
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ class SecurityCapability extends RestCapability<SecurityApi> {
|
||||
}
|
||||
});
|
||||
} catch (NetatmoException e) {
|
||||
logger.warn("Error retrieving last events for home '{}' : {}", securityId, e.getMessage());
|
||||
logger.warn("Error retrieving last events for home '{}': {}", securityId, e.getMessage());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -182,7 +182,7 @@ class SecurityCapability extends RestCapability<SecurityApi> {
|
||||
try {
|
||||
return api.getDeviceEvents(securityId, moduleId, deviceType);
|
||||
} catch (NetatmoException e) {
|
||||
logger.warn("Error retrieving last events of camera '{}' : {}", moduleId, e.getMessage());
|
||||
logger.warn("Error retrieving last events of camera '{}': {}", moduleId, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}).orElse(List.of()));
|
||||
@ -193,7 +193,7 @@ class SecurityCapability extends RestCapability<SecurityApi> {
|
||||
try {
|
||||
return api.getPersonEvents(securityId, personId);
|
||||
} catch (NetatmoException e) {
|
||||
logger.warn("Error retrieving last events of person '{}' : {}", personId, e.getMessage());
|
||||
logger.warn("Error retrieving last events of person '{}': {}", personId, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}).orElse(List.of()));
|
||||
@ -205,7 +205,7 @@ class SecurityCapability extends RestCapability<SecurityApi> {
|
||||
api.setPersonAwayStatus(securityId, personId, away);
|
||||
handler.expireData();
|
||||
} catch (NetatmoException e) {
|
||||
logger.warn("Error setting person away/at home '{}' : {}", personId, e.getMessage());
|
||||
logger.warn("Error setting person away/at home '{}': {}", personId, e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -224,7 +224,7 @@ class SecurityCapability extends RestCapability<SecurityApi> {
|
||||
api.changeStatus(localURL, status);
|
||||
handler.expireData();
|
||||
} catch (NetatmoException e) {
|
||||
logger.warn("Error changing camera monitoring status '{}' : {}", status, e.getMessage());
|
||||
logger.warn("Error changing camera monitoring status '{}': {}", status, e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -235,7 +235,7 @@ class SecurityCapability extends RestCapability<SecurityApi> {
|
||||
api.changeFloodLightMode(securityId, cameraId, mode);
|
||||
handler.expireData();
|
||||
} catch (NetatmoException e) {
|
||||
logger.warn("Error changing Presence floodlight mode '{}' : {}", mode, e.getMessage());
|
||||
logger.warn("Error changing Presence floodlight mode '{}': {}", mode, e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ public class NetatmoThingTypeProvider implements ThingTypeProvider {
|
||||
return localizationService.createLocalizedThingType(bundle,
|
||||
moduleType.isABridge() ? thingTypeBuilder.buildBridge() : thingTypeBuilder.build(), locale);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn("Unable to define ModuleType for thingType {} : {}", thingTypeUID.getId(), e.getMessage());
|
||||
logger.warn("Unable to define ModuleType for thingType {}: {}", thingTypeUID.getId(), e.getMessage());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -93,7 +93,7 @@ public class GrantServlet extends NetatmoServlet {
|
||||
resp.getWriter().append(replaceKeysFromMap(accountTemplate, replaceMap));
|
||||
resp.getWriter().close();
|
||||
} else {
|
||||
logger.warn("Unexpected : requestUrl is null");
|
||||
logger.warn("Unexpected: requestUrl is null");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,9 +71,9 @@ public class WebhookServlet extends NetatmoServlet {
|
||||
logger.info("Setting up WebHook at Netatmo to {}", uri.toString());
|
||||
hookSet = securityApi.addwebhook(uri);
|
||||
} catch (UriBuilderException e) {
|
||||
logger.info("webhookUrl is not a valid URI '{}' : {}", uri, e.getMessage());
|
||||
logger.info("webhookUrl is not a valid URI '{}': {}", uri, e.getMessage());
|
||||
} catch (NetatmoException e) {
|
||||
logger.info("Error setting webhook : {}", e.getMessage());
|
||||
logger.info("Error setting webhook: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ public class WebhookServlet extends NetatmoServlet {
|
||||
securityApi.dropWebhook();
|
||||
hookSet = false;
|
||||
} catch (NetatmoException e) {
|
||||
logger.warn("Error releasing webhook : {}", e.getMessage());
|
||||
logger.warn("Error releasing webhook: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
super.dispose();
|
||||
@ -99,12 +99,12 @@ public class WebhookServlet extends NetatmoServlet {
|
||||
|
||||
private void processEvent(String data) throws IOException {
|
||||
if (!data.isEmpty()) {
|
||||
logger.debug("Event transmitted from restService : {}", data);
|
||||
logger.debug("Event transmitted from restService: {}", data);
|
||||
try {
|
||||
WebhookEvent event = deserializer.deserialize(WebhookEvent.class, data);
|
||||
notifyListeners(event);
|
||||
} catch (NetatmoException e) {
|
||||
logger.debug("Error deserializing webhook data received : {}. {}", data, e.getMessage());
|
||||
logger.debug("Error deserializing webhook data received: {}. {}", data, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,10 +47,6 @@ public enum AreaAlarm {
|
||||
return alarmBits.testBit(bit);
|
||||
}
|
||||
|
||||
public boolean isSet(int alarmBits) {
|
||||
return isSet(BigInteger.valueOf(alarmBits));
|
||||
}
|
||||
|
||||
public String getChannelUID() {
|
||||
return channelUID;
|
||||
}
|
||||
|
@ -162,7 +162,6 @@ public class OmnilinkBindingConstants {
|
||||
public static final String TRIGGER_CHANNEL_DCM_EVENT = "dcm_event";
|
||||
public static final String TRIGGER_CHANNEL_ENERGY_COST_EVENT = "energy_cost_event";
|
||||
public static final String TRIGGER_CHANNEL_CAMERA_TRIGGER_EVENT = "camera_trigger_event";
|
||||
public static final String TRIGGER_CHANNEL_ACCESS_CONTROL_READER_EVENT = "access_control_reader_event";
|
||||
public static final String TRIGGER_CHANNEL_AREA_ALL_ON_OFF_EVENT = "all_on_off_event";
|
||||
public static final String TRIGGER_CHANNEL_SWITCH_PRESS_EVENT = "switch_press_event";
|
||||
public static final String TRIGGER_CHANNEL_UPB_LINK_ACTIVATED_EVENT = "upb_link_activated_event";
|
||||
|
@ -51,7 +51,7 @@ public enum TemperatureFormat {
|
||||
|
||||
private final int formatNumber;
|
||||
|
||||
private TemperatureFormat(int formatNumber) {
|
||||
TemperatureFormat(int formatNumber) {
|
||||
this.formatNumber = formatNumber;
|
||||
}
|
||||
|
||||
|
@ -67,11 +67,8 @@ public class OmnilinkActions implements ThingActions {
|
||||
zdt = ZonedDateTime.now(ZoneId.of(zone));
|
||||
} else {
|
||||
logger.debug("Time zone provided invalid, using system default!");
|
||||
if (timeZoneProvider.isPresent()) {
|
||||
zdt = ZonedDateTime.now(timeZoneProvider.get().getTimeZone());
|
||||
} else {
|
||||
zdt = ZonedDateTime.now(ZoneId.systemDefault());
|
||||
}
|
||||
zdt = timeZoneProvider.map(zoneProvider -> ZonedDateTime.now(zoneProvider.getTimeZone()))
|
||||
.orElseGet(() -> ZonedDateTime.now(ZoneId.systemDefault()));
|
||||
}
|
||||
actionsHandler.synchronizeControllerTime(zdt);
|
||||
}
|
||||
|
@ -99,9 +99,9 @@ public class OmnilinkDiscoveryService extends AbstractThingHandlerDiscoveryServi
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the area filter the a supplied area
|
||||
* Calculate the area filter of a supplied area
|
||||
*
|
||||
* @param area Area to calculate filter for.
|
||||
* @param areaProperties Area to calculate filter for.
|
||||
* @return Calculated Bit Filter for the supplied area. Bit 0 is area 1, bit 2 is area 2 and so on.
|
||||
*/
|
||||
private static int bitFilterForArea(AreaProperties areaProperties) {
|
||||
@ -328,7 +328,7 @@ public class OmnilinkDiscoveryService extends AbstractThingHandlerDiscoveryServi
|
||||
/**
|
||||
* Discovers OmniLink areas
|
||||
*/
|
||||
private @Nullable List<AreaProperties> discoverAreas() {
|
||||
private List<AreaProperties> discoverAreas() {
|
||||
final ThingUID bridgeUID = thingHandler.getThing().getUID();
|
||||
List<AreaProperties> areas = new LinkedList<>();
|
||||
|
||||
@ -356,22 +356,18 @@ public class OmnilinkDiscoveryService extends AbstractThingHandlerDiscoveryServi
|
||||
|
||||
final String name = thingName;
|
||||
systemType.ifPresentOrElse(t -> {
|
||||
ThingUID thingUID = null;
|
||||
switch (t) {
|
||||
case LUMINA:
|
||||
thingUID = new ThingUID(THING_TYPE_LUMINA_AREA, bridgeUID, thingID);
|
||||
break;
|
||||
default:
|
||||
thingUID = new ThingUID(THING_TYPE_OMNI_AREA, bridgeUID, thingID);
|
||||
ThingUID thingUID;
|
||||
if (t == SystemType.LUMINA) {
|
||||
thingUID = new ThingUID(THING_TYPE_LUMINA_AREA, bridgeUID, thingID);
|
||||
} else {
|
||||
thingUID = new ThingUID(THING_TYPE_OMNI_AREA, bridgeUID, thingID);
|
||||
}
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
|
||||
.withProperty(THING_PROPERTIES_NUMBER, thingID)
|
||||
.withRepresentationProperty(THING_PROPERTIES_NUMBER).withBridge(bridgeUID).withLabel(name)
|
||||
.build();
|
||||
thingDiscovered(discoveryResult);
|
||||
}, () -> {
|
||||
logger.warn("Unknown System Type");
|
||||
});
|
||||
}, () -> logger.warn("Unknown System Type"));
|
||||
|
||||
areas.add(areaProperties);
|
||||
}
|
||||
@ -397,7 +393,7 @@ public class OmnilinkDiscoveryService extends AbstractThingHandlerDiscoveryServi
|
||||
int thingType = unitProperties.getUnitType();
|
||||
String thingName = unitProperties.getName();
|
||||
String thingID = Integer.toString(unitProperties.getNumber());
|
||||
ThingUID thingUID = null;
|
||||
ThingUID thingUID;
|
||||
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
properties.put(THING_PROPERTIES_NAME, thingName);
|
||||
|
@ -21,8 +21,9 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
* @author Craig Hamilton - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@SuppressWarnings("serial")
|
||||
public class BridgeOfflineException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public BridgeOfflineException(Exception e) {
|
||||
super(e);
|
||||
}
|
||||
|
@ -67,14 +67,14 @@ public abstract class AbstractAreaHandler extends AbstractOmnilinkStatusHandler<
|
||||
|
||||
super.initialize();
|
||||
if (bridgeHandler != null) {
|
||||
updateAreaProperties(bridgeHandler);
|
||||
updateAreaProperties();
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
"Received null bridge while initializing Area!");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAreaProperties(OmnilinkBridgeHandler bridgeHandler) {
|
||||
private void updateAreaProperties() {
|
||||
final List<AreaProperties> areas = getAreaProperties();
|
||||
if (areas != null) {
|
||||
for (AreaProperties areaProperties : areas) {
|
||||
@ -99,13 +99,10 @@ public abstract class AbstractAreaHandler extends AbstractOmnilinkStatusHandler<
|
||||
return;
|
||||
}
|
||||
|
||||
switch (channelUID.getId()) {
|
||||
case CHANNEL_AREA_ACTIVATE_KEYPAD_EMERGENCY:
|
||||
handleKeypadEmergency(channelUID, command);
|
||||
break;
|
||||
default:
|
||||
handleSecurityMode(channelUID, command);
|
||||
break;
|
||||
if (channelUID.getId().equals(CHANNEL_AREA_ACTIVATE_KEYPAD_EMERGENCY)) {
|
||||
handleKeypadEmergency(command);
|
||||
} else {
|
||||
handleSecurityMode(channelUID, command);
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,7 +173,7 @@ public abstract class AbstractAreaHandler extends AbstractOmnilinkStatusHandler<
|
||||
*/
|
||||
protected abstract EnumSet<AreaAlarm> getAlarms();
|
||||
|
||||
private void handleKeypadEmergency(ChannelUID channelUID, Command command) {
|
||||
private void handleKeypadEmergency(Command command) {
|
||||
if (command instanceof DecimalType decimalCommand) {
|
||||
try {
|
||||
final OmnilinkBridgeHandler bridge = getOmnilinkBridgeHandler();
|
||||
|
@ -71,7 +71,7 @@ public abstract class AbstractOmnilinkHandler extends BaseThingHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the area filter the a supplied area
|
||||
* Calculate the area filter of a supplied area
|
||||
*
|
||||
* @param areaProperties Area to calculate filter for.
|
||||
* @return Calculated Bit Filter for the supplied area. Bit 0 is area 1, bit 2 is area 2 and so on.
|
||||
@ -120,7 +120,7 @@ public abstract class AbstractOmnilinkHandler extends BaseThingHandler {
|
||||
protected int getAreaNumber() {
|
||||
String areaNumber = getThing().getProperties().get(THING_PROPERTIES_AREA);
|
||||
if (areaNumber != null) {
|
||||
return Integer.valueOf(areaNumber);
|
||||
return Integer.parseInt(areaNumber);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ import com.digitaldan.jomnilinkII.MessageTypes.statuses.Status;
|
||||
|
||||
/**
|
||||
* The {@link AbstractOmnilinkStatusHandler} defines some methods that can be used across
|
||||
* the many different units exposed by the OmniLink protocol to retrive updated status information.
|
||||
* the many different units exposed by the OmniLink protocol to retrieve updated status information.
|
||||
*
|
||||
* @author Craig Hamilton - Initial contribution
|
||||
* @author Ethan Dye - openHAB3 rewrite
|
||||
|
@ -50,7 +50,6 @@ import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
|
||||
@NonNullByDefault
|
||||
public class AudioSourceHandler extends AbstractOmnilinkHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(AudioSourceHandler.class);
|
||||
private final int pollDelaySeconds = 5;
|
||||
private final int thingID = getThingNumber();
|
||||
private @Nullable ScheduledFuture<?> scheduledPolling = null;
|
||||
public @Nullable String number;
|
||||
@ -64,7 +63,7 @@ public class AudioSourceHandler extends AbstractOmnilinkHandler {
|
||||
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
|
||||
if (bridgeHandler != null) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
if (((Boolean) getThing().getConfiguration().get(THING_PROPERTIES_AUTOSTART)).booleanValue()) {
|
||||
if ((Boolean) getThing().getConfiguration().get(THING_PROPERTIES_AUTOSTART)) {
|
||||
logger.debug("Autostart enabled, scheduling polling for Audio Source: {}", thingID);
|
||||
schedulePolling();
|
||||
} else {
|
||||
@ -104,6 +103,7 @@ public class AudioSourceHandler extends AbstractOmnilinkHandler {
|
||||
private synchronized void schedulePolling() {
|
||||
cancelPolling();
|
||||
logger.debug("Scheduling polling for Audio Source: {}", thingID);
|
||||
int pollDelaySeconds = 5;
|
||||
scheduledPolling = super.scheduler.scheduleWithFixedDelay(this::pollAudioSource, 0, pollDelaySeconds,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
@ -113,19 +113,17 @@ public class AudioSourceHandler extends AbstractOmnilinkHandler {
|
||||
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
|
||||
final ScheduledFuture<?> scheduledPolling = this.scheduledPolling;
|
||||
|
||||
switch (channelUID.getId()) {
|
||||
case CHANNEL_AUDIO_SOURCE_POLLING:
|
||||
if (command instanceof RefreshType) {
|
||||
updateState(CHANNEL_AUDIO_SOURCE_POLLING,
|
||||
OnOffType.from((scheduledPolling != null && !scheduledPolling.isDone())));
|
||||
} else if (command instanceof OnOffType onOffCommand) {
|
||||
handlePolling(channelUID, onOffCommand);
|
||||
} else {
|
||||
logger.debug("Invalid command: {}, must be RefreshType or OnOffType", command);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.warn("Unknown channel for Audio Source thing: {}", channelUID);
|
||||
if (CHANNEL_AUDIO_SOURCE_POLLING.equals(channelUID.getId())) {
|
||||
if (command instanceof RefreshType) {
|
||||
updateState(CHANNEL_AUDIO_SOURCE_POLLING,
|
||||
OnOffType.from((scheduledPolling != null && !scheduledPolling.isDone())));
|
||||
} else if (command instanceof OnOffType onOffCommand) {
|
||||
handlePolling(channelUID, onOffCommand);
|
||||
} else {
|
||||
logger.debug("Invalid command: {}, must be RefreshType or OnOffType", command);
|
||||
}
|
||||
} else {
|
||||
logger.warn("Unknown channel for Audio Source thing: {}", channelUID);
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,7 +172,7 @@ public class AudioSourceHandler extends AbstractOmnilinkHandler {
|
||||
logger.debug("Received null bridge while polling Audio Source statuses!");
|
||||
}
|
||||
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
|
||||
logger.debug("Exception recieved while polling for Audio Source statuses: {}", e.getMessage());
|
||||
logger.debug("Exception received while polling for Audio Source statuses: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,17 +95,15 @@ public class ButtonHandler extends AbstractOmnilinkHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (channelUID.getId()) {
|
||||
case CHANNEL_BUTTON_PRESS:
|
||||
if (command instanceof OnOffType) {
|
||||
sendOmnilinkCommand(CommandMessage.CMD_BUTTON, 0, thingID);
|
||||
updateChannels();
|
||||
} else {
|
||||
logger.debug("Invalid command: {}, must be OnOffType", command);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.warn("Unknown channel for Button thing: {}", channelUID);
|
||||
if (CHANNEL_BUTTON_PRESS.equals(channelUID.getId())) {
|
||||
if (command instanceof OnOffType) {
|
||||
sendOmnilinkCommand(CommandMessage.CMD_BUTTON, 0, thingID);
|
||||
updateChannels();
|
||||
} else {
|
||||
logger.debug("Invalid command: {}, must be OnOffType", command);
|
||||
}
|
||||
} else {
|
||||
logger.warn("Unknown channel for Button thing: {}", channelUID);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,7 @@ public class HumiditySensorHandler extends AbstractOmnilinkStatusHandler<Extende
|
||||
thingID);
|
||||
break;
|
||||
default:
|
||||
logger.warn("Unknown channel for Humdity Sensor thing: {}", channelUID);
|
||||
logger.warn("Unknown channel for Humidity Sensor thing: {}", channelUID);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,17 +88,16 @@ public class LockHandler extends AbstractOmnilinkStatusHandler<ExtendedAccessCon
|
||||
return;
|
||||
}
|
||||
|
||||
switch (channelUID.getId()) {
|
||||
case CHANNEL_LOCK_SWITCH:
|
||||
if (command instanceof OnOffType) {
|
||||
sendOmnilinkCommand(OnOffType.OFF.equals(command) ? CommandMessage.CMD_UNLOCK_DOOR
|
||||
: CommandMessage.CMD_LOCK_DOOR, 0, thingID);
|
||||
} else {
|
||||
logger.debug("Invalid command {}, must be OnOffType", command);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.warn("Unknown channel for Lock thing: {}", channelUID);
|
||||
if (CHANNEL_LOCK_SWITCH.equals(channelUID.getId())) {
|
||||
if (command instanceof OnOffType) {
|
||||
sendOmnilinkCommand(
|
||||
OnOffType.OFF.equals(command) ? CommandMessage.CMD_UNLOCK_DOOR : CommandMessage.CMD_LOCK_DOOR,
|
||||
0, thingID);
|
||||
} else {
|
||||
logger.debug("Invalid command {}, must be OnOffType", command);
|
||||
}
|
||||
} else {
|
||||
logger.warn("Unknown channel for Lock thing: {}", channelUID);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,22 +44,15 @@ public class LuminaAreaHandler extends AbstractAreaHandler {
|
||||
|
||||
@Override
|
||||
protected int getMode(ChannelUID channelUID) {
|
||||
switch (channelUID.getId()) {
|
||||
case CHANNEL_AREA_SECURITY_MODE_HOME:
|
||||
return CommandMessage.CMD_SECURITY_LUMINA_HOME_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_SLEEP:
|
||||
return CommandMessage.CMD_SECURITY_LUMINA_SLEEP_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_AWAY:
|
||||
return CommandMessage.CMD_SECURITY_LUMINA_AWAY_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_VACATION:
|
||||
return CommandMessage.CMD_SECURITY_LUMINA_VACATION_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_PARTY:
|
||||
return CommandMessage.CMD_SECURITY_LUMINA_PARTY_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_SPECIAL:
|
||||
return CommandMessage.CMD_SECURITY_LUMINA_SPECIAL_MODE;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown channel for area thing " + channelUID);
|
||||
}
|
||||
return switch (channelUID.getId()) {
|
||||
case CHANNEL_AREA_SECURITY_MODE_HOME -> CommandMessage.CMD_SECURITY_LUMINA_HOME_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_SLEEP -> CommandMessage.CMD_SECURITY_LUMINA_SLEEP_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_AWAY -> CommandMessage.CMD_SECURITY_LUMINA_AWAY_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_VACATION -> CommandMessage.CMD_SECURITY_LUMINA_VACATION_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_PARTY -> CommandMessage.CMD_SECURITY_LUMINA_PARTY_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_SPECIAL -> CommandMessage.CMD_SECURITY_LUMINA_SPECIAL_MODE;
|
||||
default -> throw new IllegalStateException("Unknown channel for area thing " + channelUID);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -45,24 +45,16 @@ public class OmniAreaHandler extends AbstractAreaHandler {
|
||||
|
||||
@Override
|
||||
protected int getMode(ChannelUID channelUID) {
|
||||
switch (channelUID.getId()) {
|
||||
case CHANNEL_AREA_SECURITY_MODE_DISARM:
|
||||
return CommandMessage.CMD_SECURITY_OMNI_DISARM;
|
||||
case CHANNEL_AREA_SECURITY_MODE_DAY:
|
||||
return CommandMessage.CMD_SECURITY_OMNI_DAY_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_NIGHT:
|
||||
return CommandMessage.CMD_SECURITY_OMNI_NIGHT_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_AWAY:
|
||||
return CommandMessage.CMD_SECURITY_OMNI_AWAY_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_VACATION:
|
||||
return CommandMessage.CMD_SECURITY_OMNI_VACATION_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_DAY_INSTANT:
|
||||
return CommandMessage.CMD_SECURITY_OMNI_DAY_INSTANT_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_NIGHT_DELAYED:
|
||||
return CommandMessage.CMD_SECURITY_OMNI_NIGHT_DELAYED_MODE;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown channel for area thing " + channelUID);
|
||||
}
|
||||
return switch (channelUID.getId()) {
|
||||
case CHANNEL_AREA_SECURITY_MODE_DISARM -> CommandMessage.CMD_SECURITY_OMNI_DISARM;
|
||||
case CHANNEL_AREA_SECURITY_MODE_DAY -> CommandMessage.CMD_SECURITY_OMNI_DAY_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_NIGHT -> CommandMessage.CMD_SECURITY_OMNI_NIGHT_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_AWAY -> CommandMessage.CMD_SECURITY_OMNI_AWAY_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_VACATION -> CommandMessage.CMD_SECURITY_OMNI_VACATION_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_DAY_INSTANT -> CommandMessage.CMD_SECURITY_OMNI_DAY_INSTANT_MODE;
|
||||
case CHANNEL_AREA_SECURITY_MODE_NIGHT_DELAYED -> CommandMessage.CMD_SECURITY_OMNI_NIGHT_DELAYED_MODE;
|
||||
default -> throw new IllegalStateException("Unknown channel for area thing " + channelUID);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -95,7 +95,6 @@ public class OmnilinkBridgeHandler extends BaseBridgeHandler implements Notifica
|
||||
private @Nullable Connection omniConnection = null;
|
||||
private @Nullable ScheduledFuture<?> connectJob;
|
||||
private @Nullable ScheduledFuture<?> eventPollingJob;
|
||||
private final int autoReconnectPeriod = 60;
|
||||
private Optional<AudioPlayer> audioPlayer = Optional.empty();
|
||||
private Optional<SystemType> systemType = Optional.empty();
|
||||
private final Gson gson = new Gson();
|
||||
@ -240,9 +239,9 @@ public class OmnilinkBridgeHandler extends BaseBridgeHandler implements Notifica
|
||||
* HAI only supports one audio player - cycle through features until we find a feature that is an audio
|
||||
* player.
|
||||
*/
|
||||
audioPlayer = Objects.requireNonNull(reqSystemFeatures().getFeatures().stream()
|
||||
.map(featureCode -> AudioPlayer.getAudioPlayerForFeatureCode(featureCode))
|
||||
.filter(Optional::isPresent).findFirst().orElse(Optional.empty()));
|
||||
audioPlayer = Objects.requireNonNull(
|
||||
reqSystemFeatures().getFeatures().stream().map(AudioPlayer::getAudioPlayerForFeatureCode)
|
||||
.filter(Optional::isPresent).findFirst().orElse(Optional.empty()));
|
||||
|
||||
systemType = SystemType.getType(reqSystemInformation().getModel());
|
||||
|
||||
@ -288,80 +287,83 @@ public class OmnilinkBridgeHandler extends BaseBridgeHandler implements Notifica
|
||||
if (objectStatus != null) {
|
||||
Status[] statuses = objectStatus.getStatuses();
|
||||
for (Status status : statuses) {
|
||||
if (status instanceof ExtendedUnitStatus unitStatus) {
|
||||
int unitNumber = unitStatus.getNumber();
|
||||
switch (status) {
|
||||
case ExtendedUnitStatus unitStatus -> {
|
||||
int unitNumber = unitStatus.getNumber();
|
||||
|
||||
logger.debug("Received status update for Unit: {}, status: {}", unitNumber, unitStatus);
|
||||
Optional<Thing> theThing = getUnitThing(unitNumber);
|
||||
theThing.map(Thing::getHandler)
|
||||
.ifPresent(theHandler -> ((UnitHandler) theHandler).handleStatus(unitStatus));
|
||||
} else if (status instanceof ExtendedZoneStatus zoneStatus) {
|
||||
int zoneNumber = zoneStatus.getNumber();
|
||||
|
||||
logger.debug("Received status update for Zone: {}, status: {}", zoneNumber, zoneStatus);
|
||||
Optional<Thing> theThing = getChildThing(THING_TYPE_ZONE, zoneNumber);
|
||||
theThing.map(Thing::getHandler)
|
||||
.ifPresent(theHandler -> ((ZoneHandler) theHandler).handleStatus(zoneStatus));
|
||||
} else if (status instanceof ExtendedAreaStatus areaStatus) {
|
||||
int areaNumber = areaStatus.getNumber();
|
||||
|
||||
logger.debug("Received status update for Area: {}, status: {}", areaNumber, areaStatus);
|
||||
systemType.ifPresent(t -> {
|
||||
Optional<Thing> theThing = Optional.empty();
|
||||
switch (t) {
|
||||
case LUMINA:
|
||||
theThing = getChildThing(THING_TYPE_LUMINA_AREA, areaNumber);
|
||||
break;
|
||||
case OMNI:
|
||||
theThing = getChildThing(THING_TYPE_OMNI_AREA, areaNumber);
|
||||
break;
|
||||
}
|
||||
logger.debug("Received status update for Unit: {}, status: {}", unitNumber, unitStatus);
|
||||
Optional<Thing> theThing = getUnitThing(unitNumber);
|
||||
theThing.map(Thing::getHandler)
|
||||
.ifPresent(theHandler -> ((AbstractAreaHandler) theHandler).handleStatus(areaStatus));
|
||||
});
|
||||
} else if (status instanceof ExtendedAccessControlReaderLockStatus lockStatus) {
|
||||
int lockNumber = lockStatus.getNumber();
|
||||
|
||||
logger.debug("Received status update for Lock: {}, status: {}", lockNumber, lockStatus);
|
||||
Optional<Thing> theThing = getChildThing(THING_TYPE_LOCK, lockNumber);
|
||||
theThing.map(Thing::getHandler)
|
||||
.ifPresent(theHandler -> ((LockHandler) theHandler).handleStatus(lockStatus));
|
||||
} else if (status instanceof ExtendedThermostatStatus thermostatStatus) {
|
||||
int thermostatNumber = thermostatStatus.getNumber();
|
||||
|
||||
logger.debug("Received status update for Thermostat: {}, status: {}", thermostatNumber,
|
||||
thermostatStatus);
|
||||
Optional<Thing> theThing = getChildThing(THING_TYPE_THERMOSTAT, thermostatNumber);
|
||||
theThing.map(Thing::getHandler)
|
||||
.ifPresent(theHandler -> ((ThermostatHandler) theHandler).handleStatus(thermostatStatus));
|
||||
} else if (status instanceof ExtendedAudioZoneStatus audioZoneStatus) {
|
||||
int audioZoneNumber = audioZoneStatus.getNumber();
|
||||
|
||||
logger.debug("Received status update for Audio Zone: {}, status: {}", audioZoneNumber,
|
||||
audioZoneStatus);
|
||||
Optional<Thing> theThing = getChildThing(THING_TYPE_AUDIO_ZONE, audioZoneNumber);
|
||||
theThing.map(Thing::getHandler)
|
||||
.ifPresent(theHandler -> ((AudioZoneHandler) theHandler).handleStatus(audioZoneStatus));
|
||||
} else if (status instanceof ExtendedAuxSensorStatus auxSensorStatus) {
|
||||
int auxSensorNumber = auxSensorStatus.getNumber();
|
||||
|
||||
// Aux Sensors can be either temperature or humidity, need to check both.
|
||||
Optional<Thing> tempThing = getChildThing(THING_TYPE_TEMP_SENSOR, auxSensorNumber);
|
||||
Optional<Thing> humidityThing = getChildThing(THING_TYPE_HUMIDITY_SENSOR, auxSensorNumber);
|
||||
if (tempThing.isPresent()) {
|
||||
logger.debug("Received status update for Temperature Sensor: {}, status: {}", auxSensorNumber,
|
||||
auxSensorStatus);
|
||||
tempThing.map(Thing::getHandler).ifPresent(
|
||||
theHandler -> ((TempSensorHandler) theHandler).handleStatus(auxSensorStatus));
|
||||
.ifPresent(theHandler -> ((UnitHandler) theHandler).handleStatus(unitStatus));
|
||||
}
|
||||
if (humidityThing.isPresent()) {
|
||||
logger.debug("Received status update for Humidity Sensor: {}, status: {}", auxSensorNumber,
|
||||
auxSensorStatus);
|
||||
humidityThing.map(Thing::getHandler).ifPresent(
|
||||
theHandler -> ((HumiditySensorHandler) theHandler).handleStatus(auxSensorStatus));
|
||||
case ExtendedZoneStatus zoneStatus -> {
|
||||
int zoneNumber = zoneStatus.getNumber();
|
||||
|
||||
logger.debug("Received status update for Zone: {}, status: {}", zoneNumber, zoneStatus);
|
||||
Optional<Thing> theThing = getChildThing(THING_TYPE_ZONE, zoneNumber);
|
||||
theThing.map(Thing::getHandler)
|
||||
.ifPresent(theHandler -> ((ZoneHandler) theHandler).handleStatus(zoneStatus));
|
||||
}
|
||||
} else {
|
||||
logger.debug("Received Object Status Notification that was not processed: {}", objectStatus);
|
||||
case ExtendedAreaStatus areaStatus -> {
|
||||
int areaNumber = areaStatus.getNumber();
|
||||
|
||||
logger.debug("Received status update for Area: {}, status: {}", areaNumber, areaStatus);
|
||||
systemType.ifPresent(t -> {
|
||||
Optional<Thing> theThing = switch (t) {
|
||||
case LUMINA -> getChildThing(THING_TYPE_LUMINA_AREA, areaNumber);
|
||||
case OMNI -> getChildThing(THING_TYPE_OMNI_AREA, areaNumber);
|
||||
};
|
||||
theThing.map(Thing::getHandler).ifPresent(
|
||||
theHandler -> ((AbstractAreaHandler) theHandler).handleStatus(areaStatus));
|
||||
});
|
||||
}
|
||||
case ExtendedAccessControlReaderLockStatus lockStatus -> {
|
||||
int lockNumber = lockStatus.getNumber();
|
||||
|
||||
logger.debug("Received status update for Lock: {}, status: {}", lockNumber, lockStatus);
|
||||
Optional<Thing> theThing = getChildThing(THING_TYPE_LOCK, lockNumber);
|
||||
theThing.map(Thing::getHandler)
|
||||
.ifPresent(theHandler -> ((LockHandler) theHandler).handleStatus(lockStatus));
|
||||
}
|
||||
case ExtendedThermostatStatus thermostatStatus -> {
|
||||
int thermostatNumber = thermostatStatus.getNumber();
|
||||
|
||||
logger.debug("Received status update for Thermostat: {}, status: {}", thermostatNumber,
|
||||
thermostatStatus);
|
||||
Optional<Thing> theThing = getChildThing(THING_TYPE_THERMOSTAT, thermostatNumber);
|
||||
theThing.map(Thing::getHandler).ifPresent(
|
||||
theHandler -> ((ThermostatHandler) theHandler).handleStatus(thermostatStatus));
|
||||
}
|
||||
case ExtendedAudioZoneStatus audioZoneStatus -> {
|
||||
int audioZoneNumber = audioZoneStatus.getNumber();
|
||||
|
||||
logger.debug("Received status update for Audio Zone: {}, status: {}", audioZoneNumber,
|
||||
audioZoneStatus);
|
||||
Optional<Thing> theThing = getChildThing(THING_TYPE_AUDIO_ZONE, audioZoneNumber);
|
||||
theThing.map(Thing::getHandler)
|
||||
.ifPresent(theHandler -> ((AudioZoneHandler) theHandler).handleStatus(audioZoneStatus));
|
||||
}
|
||||
case ExtendedAuxSensorStatus auxSensorStatus -> {
|
||||
int auxSensorNumber = auxSensorStatus.getNumber();
|
||||
|
||||
// Aux Sensors can be either temperature or humidity, need to check both.
|
||||
Optional<Thing> tempThing = getChildThing(THING_TYPE_TEMP_SENSOR, auxSensorNumber);
|
||||
Optional<Thing> humidityThing = getChildThing(THING_TYPE_HUMIDITY_SENSOR, auxSensorNumber);
|
||||
if (tempThing.isPresent()) {
|
||||
logger.debug("Received status update for Temperature Sensor: {}, status: {}",
|
||||
auxSensorNumber, auxSensorStatus);
|
||||
tempThing.map(Thing::getHandler).ifPresent(
|
||||
theHandler -> ((TempSensorHandler) theHandler).handleStatus(auxSensorStatus));
|
||||
}
|
||||
if (humidityThing.isPresent()) {
|
||||
logger.debug("Received status update for Humidity Sensor: {}, status: {}", auxSensorNumber,
|
||||
auxSensorStatus);
|
||||
humidityThing.map(Thing::getHandler).ifPresent(
|
||||
theHandler -> ((HumiditySensorHandler) theHandler).handleStatus(auxSensorStatus));
|
||||
}
|
||||
}
|
||||
case null, default ->
|
||||
logger.debug("Received Object Status Notification that was not processed: {}", objectStatus);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -476,12 +478,9 @@ public class OmnilinkBridgeHandler extends BaseBridgeHandler implements Notifica
|
||||
SystemStatus status = getOmniConnection().reqSystemStatus();
|
||||
logger.debug("Received system status: {}", status);
|
||||
// Update controller's reported time
|
||||
String dateString = new StringBuilder().append(2000 + status.getYear()).append("-")
|
||||
.append(String.format("%02d", status.getMonth())).append("-")
|
||||
.append(String.format("%02d", status.getDay())).append("T")
|
||||
.append(String.format("%02d", status.getHour())).append(":")
|
||||
.append(String.format("%02d", status.getMinute())).append(":")
|
||||
.append(String.format("%02d", status.getSecond())).toString();
|
||||
String dateString = (2000 + status.getYear()) + "-" + String.format("%02d", status.getMonth()) + "-"
|
||||
+ String.format("%02d", status.getDay()) + "T" + String.format("%02d", status.getHour()) + ":"
|
||||
+ String.format("%02d", status.getMinute()) + ":" + String.format("%02d", status.getSecond());
|
||||
updateState(CHANNEL_SYSTEM_DATE, new DateTimeType(dateString));
|
||||
}
|
||||
|
||||
@ -578,10 +577,8 @@ public class OmnilinkBridgeHandler extends BaseBridgeHandler implements Notifica
|
||||
SystemInformation systemInformation = reqSystemInformation();
|
||||
Map<String, String> properties = editProperties();
|
||||
properties.put(Thing.PROPERTY_MODEL_ID, Integer.toString(systemInformation.getModel()));
|
||||
properties.put(Thing.PROPERTY_FIRMWARE_VERSION,
|
||||
Integer.toString(systemInformation.getMajor()) + "."
|
||||
+ Integer.toString(systemInformation.getMinor()) + "."
|
||||
+ Integer.toString(systemInformation.getRevision()));
|
||||
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, systemInformation.getMajor() + "."
|
||||
+ systemInformation.getMinor() + "." + systemInformation.getRevision());
|
||||
properties.put(THING_PROPERTIES_PHONE_NUMBER, systemInformation.getPhone());
|
||||
updateProperties(properties);
|
||||
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
|
||||
@ -597,8 +594,7 @@ public class OmnilinkBridgeHandler extends BaseBridgeHandler implements Notifica
|
||||
private void scheduleReconnectJob() {
|
||||
ScheduledFuture<?> currentReconnectJob = connectJob;
|
||||
if (currentReconnectJob == null || currentReconnectJob.isDone()) {
|
||||
connectJob = super.scheduler.scheduleWithFixedDelay(this::makeOmnilinkConnection, 0, autoReconnectPeriod,
|
||||
TimeUnit.SECONDS);
|
||||
connectJob = super.scheduler.scheduleWithFixedDelay(this::makeOmnilinkConnection, 0, 60, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@ -647,13 +643,13 @@ public class OmnilinkBridgeHandler extends BaseBridgeHandler implements Notifica
|
||||
logger.debug("Processing event log message number: {}", logData.getEventNumber());
|
||||
eventLogNumber = logData.getEventNumber();
|
||||
String json = gson.toJson(logData);
|
||||
logger.debug("Receieved event log message: {}", json);
|
||||
logger.debug("Received event log message: {}", json);
|
||||
updateState(CHANNEL_EVENT_LOG, new StringType(json));
|
||||
}
|
||||
} while (message.getMessageType() != Message.MESG_TYPE_END_OF_DATA);
|
||||
|
||||
} catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
|
||||
logger.debug("Exception recieved while polling for event log messages: {}", e.getMessage());
|
||||
logger.debug("Exception received while polling for event log messages: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ public class TempSensorHandler extends AbstractOmnilinkStatusHandler<ExtendedAux
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
|
||||
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
|
||||
Optional<TemperatureFormat> temperatureFormat = Optional.empty();
|
||||
Optional<TemperatureFormat> temperatureFormat;
|
||||
|
||||
if (command instanceof RefreshType) {
|
||||
retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
|
||||
@ -118,7 +118,7 @@ public class TempSensorHandler extends AbstractOmnilinkStatusHandler<ExtendedAux
|
||||
if (bridgeHandler != null) {
|
||||
temperatureFormat = bridgeHandler.getTemperatureFormat();
|
||||
if (temperatureFormat.isEmpty()) {
|
||||
logger.warn("Receieved null temperature format!");
|
||||
logger.warn("Received null temperature format!");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -158,7 +158,7 @@ public class TempSensorHandler extends AbstractOmnilinkStatusHandler<ExtendedAux
|
||||
temperatureFormat.get().omniToFormat(status.getHeatSetpoint()),
|
||||
temperatureFormat.get().getFormatNumber() == 1 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
|
||||
} else {
|
||||
logger.warn("Receieved null temperature format, could not update Temperature Sensor channels!");
|
||||
logger.warn("Received null temperature format, could not update Temperature Sensor channels!");
|
||||
}
|
||||
} else {
|
||||
logger.debug("Received null bridge while updating Temperature Sensor channels!");
|
||||
|
@ -75,7 +75,7 @@ public class ThermostatHandler extends AbstractOmnilinkStatusHandler<ExtendedThe
|
||||
private final int bit;
|
||||
private final int modeValue;
|
||||
|
||||
private ThermostatStatus(int bit, int modeValue) {
|
||||
ThermostatStatus(int bit, int modeValue) {
|
||||
this.bit = bit;
|
||||
this.modeValue = modeValue;
|
||||
}
|
||||
@ -122,7 +122,7 @@ public class ThermostatHandler extends AbstractOmnilinkStatusHandler<ExtendedThe
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
|
||||
final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
|
||||
Optional<TemperatureFormat> temperatureFormat = Optional.empty();
|
||||
Optional<TemperatureFormat> temperatureFormat;
|
||||
|
||||
if (command instanceof RefreshType) {
|
||||
retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
|
||||
@ -137,7 +137,7 @@ public class ThermostatHandler extends AbstractOmnilinkStatusHandler<ExtendedThe
|
||||
if (bridgeHandler != null) {
|
||||
temperatureFormat = bridgeHandler.getTemperatureFormat();
|
||||
if (temperatureFormat.isEmpty()) {
|
||||
logger.warn("Receieved null temperature format!");
|
||||
logger.warn("Received null temperature format!");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -226,7 +226,7 @@ public class ThermostatHandler extends AbstractOmnilinkStatusHandler<ExtendedThe
|
||||
temperatureFormat.get().omniToFormat(status.getHeatSetpoint()),
|
||||
temperatureFormat.get().getFormatNumber() == 1 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
|
||||
} else {
|
||||
logger.warn("Receieved null temperature format, could not update Thermostat channels!");
|
||||
logger.warn("Received null temperature format, could not update Thermostat channels!");
|
||||
}
|
||||
} else {
|
||||
logger.warn("Could not connect to Bridge, failed to get temperature format!");
|
||||
|
@ -160,11 +160,9 @@ public class UnitHandler extends AbstractOmnilinkStatusHandler<ExtendedUnitStatu
|
||||
public void updateChannels(ExtendedUnitStatus status) {
|
||||
logger.debug("updateChannels called for Unit status: {}", status);
|
||||
int unitStatus = status.getStatus();
|
||||
int level = 0;
|
||||
int level = Status.UNIT_OFF;
|
||||
|
||||
if (unitStatus == Status.UNIT_OFF) {
|
||||
level = 0;
|
||||
} else if (unitStatus == Status.UNIT_ON) {
|
||||
if (unitStatus == Status.UNIT_ON) {
|
||||
level = 100;
|
||||
} else if ((unitStatus >= Status.UNIT_SCENE_A) && (unitStatus <= Status.UNIT_SCENE_L)) {
|
||||
level = 100;
|
||||
@ -206,7 +204,7 @@ public class UnitHandler extends AbstractOmnilinkStatusHandler<ExtendedUnitStatu
|
||||
/**
|
||||
* Handle a switch press event by triggering the appropriate channel.
|
||||
*
|
||||
* @param switchPressEvent
|
||||
* @param switchPressEvent A switch press event to handle.
|
||||
*/
|
||||
public void handleSwitchPressEvent(SwitchPressEvent switchPressEvent) {
|
||||
ChannelUID activateChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_SWITCH_PRESS_EVENT);
|
||||
|
@ -81,7 +81,7 @@ public class ZoneHandler extends AbstractOmnilinkStatusHandler<ExtendedZoneStatu
|
||||
final List<AreaProperties> areas = super.getAreaProperties();
|
||||
if (areas != null) {
|
||||
for (AreaProperties areaProperties : areas) {
|
||||
int areaFilter = super.bitFilterForArea(areaProperties);
|
||||
int areaFilter = bitFilterForArea(areaProperties);
|
||||
|
||||
ObjectPropertyRequest<ZoneProperties> objectPropertyRequest = ObjectPropertyRequest
|
||||
.builder(bridgeHandler, ObjectPropertyRequests.ZONE, getThingNumber(), 0).selectNamed()
|
||||
@ -115,16 +115,11 @@ public class ZoneHandler extends AbstractOmnilinkStatusHandler<ExtendedZoneStatu
|
||||
return;
|
||||
}
|
||||
|
||||
switch (channelUID.getId()) {
|
||||
case CHANNEL_ZONE_BYPASS:
|
||||
mode = CommandMessage.CMD_SECURITY_BYPASS_ZONE;
|
||||
break;
|
||||
case CHANNEL_ZONE_RESTORE:
|
||||
mode = CommandMessage.CMD_SECURITY_RESTORE_ZONE;
|
||||
break;
|
||||
default:
|
||||
mode = -1;
|
||||
}
|
||||
mode = switch (channelUID.getId()) {
|
||||
case CHANNEL_ZONE_BYPASS -> CommandMessage.CMD_SECURITY_BYPASS_ZONE;
|
||||
case CHANNEL_ZONE_RESTORE -> CommandMessage.CMD_SECURITY_RESTORE_ZONE;
|
||||
default -> -1;
|
||||
};
|
||||
logger.debug("mode {} on zone {} with code {}", mode, thingID, command.toFullString());
|
||||
char[] code = command.toFullString().toCharArray();
|
||||
if (code.length != 4) {
|
||||
@ -174,7 +169,7 @@ public class ZoneHandler extends AbstractOmnilinkStatusHandler<ExtendedZoneStatu
|
||||
@Override
|
||||
protected void updateChannels(ExtendedZoneStatus zoneStatus) {
|
||||
// 0 Secure. 1 Not ready, 3 Trouble
|
||||
int current = ((zoneStatus.getStatus() >> 0) & 0x03);
|
||||
int current = ((zoneStatus.getStatus()) & 0x03);
|
||||
// 0 Secure, 1 Tripped, 2 Reset, but previously tripped
|
||||
int latched = ((zoneStatus.getStatus() >> 2) & 0x03);
|
||||
// 0 Disarmed, 1 Armed, 2 Bypass user, 3 Bypass system
|
||||
|
@ -49,13 +49,11 @@ public class DimmableUnitHandler extends UnitHandler {
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
|
||||
switch (channelUID.getId()) {
|
||||
case CHANNEL_UNIT_LEVEL:
|
||||
handleUnitLevel(channelUID, command);
|
||||
break;
|
||||
default:
|
||||
logger.debug("Unknown channel for Dimmable Unit thing: {}", channelUID);
|
||||
super.handleCommand(channelUID, command);
|
||||
if (CHANNEL_UNIT_LEVEL.equals(channelUID.getId())) {
|
||||
handleUnitLevel(channelUID, command);
|
||||
} else {
|
||||
logger.debug("Unknown channel for Dimmable Unit thing: {}", channelUID);
|
||||
super.handleCommand(channelUID, command);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,7 @@ public class UpbRoomHandler extends UnitHandler {
|
||||
param2 = ((roomNum * 6) - 3) + cmdValue - 2;
|
||||
break;
|
||||
default:
|
||||
logger.warn("Unexpected UPB Room state: {}", Integer.toString(cmdValue));
|
||||
logger.warn("Unexpected UPB Room state: {}", cmdValue);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -55,18 +55,16 @@ public class UpbUnitHandler extends DimmableUnitHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (channelUID.getId()) {
|
||||
case CHANNEL_UPB_STATUS:
|
||||
if (command instanceof StringType) {
|
||||
sendOmnilinkCommand(CommandMessage.CMD_UNIT_UPB_REQ_STATUS, 0, thingID);
|
||||
updateState(CHANNEL_UPB_STATUS, UnDefType.UNDEF);
|
||||
} else {
|
||||
logger.debug("Invalid command: {}, must be StringType", command);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.debug("Unknown channel for UPB Unit thing: {}", channelUID);
|
||||
super.handleCommand(channelUID, command);
|
||||
if (CHANNEL_UPB_STATUS.equals(channelUID.getId())) {
|
||||
if (command instanceof StringType) {
|
||||
sendOmnilinkCommand(CommandMessage.CMD_UNIT_UPB_REQ_STATUS, 0, thingID);
|
||||
updateState(CHANNEL_UPB_STATUS, UnDefType.UNDEF);
|
||||
} else {
|
||||
logger.debug("Invalid command: {}, must be StringType", command);
|
||||
}
|
||||
} else {
|
||||
logger.debug("Unknown channel for UPB Unit thing: {}", channelUID);
|
||||
super.handleCommand(channelUID, command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user