added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.folding-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-folding" description="Folding Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.folding/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.folding.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link FoldingBinding} class defines common constants, which are
* used across the whole binding.
*
* @author Marius Bjoernstad - Initial contribution
*/
@NonNullByDefault
public class FoldingBindingConstants {
public static final String BINDING_ID = "folding";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_CLIENT = new ThingTypeUID(BINDING_ID, "client");
public static final ThingTypeUID THING_TYPE_SLOT = new ThingTypeUID(BINDING_ID, "slot");
public static final String PARAM_SLOT_ID = "id";
// List of all Channel ids
public static final String CHANNEL_STATUS = "status";
}

View File

@@ -0,0 +1,58 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.folding.internal;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.openhab.binding.folding.internal.handler.FoldingClientHandler;
import org.openhab.binding.folding.internal.handler.SlotHandler;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Component;
/**
* The {@link FoldingHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Marius Bjoernstad - Initial contribution
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.folding")
public class FoldingHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<>(
Arrays.asList(FoldingBindingConstants.THING_TYPE_CLIENT, FoldingBindingConstants.THING_TYPE_SLOT));
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(FoldingBindingConstants.THING_TYPE_CLIENT)) {
return new FoldingClientHandler((Bridge) thing);
} else if (thingTypeUID.equals(FoldingBindingConstants.THING_TYPE_SLOT)) {
return new SlotHandler(thing);
}
return null;
}
}

View File

@@ -0,0 +1,47 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.folding.internal.discovery;
import org.openhab.core.thing.ThingUID;
/**
* Singleton instance to connect the Client handler and the discovery
* service.
*
* @author Marius Bjoernstad - Initial contribution
*/
public class FoldingDiscoveryProxy {
private static FoldingDiscoveryProxy instance;
private FoldingSlotDiscoveryService discoveryService = null;
private FoldingDiscoveryProxy() {
}
public static FoldingDiscoveryProxy getInstance() {
if (instance == null) {
instance = new FoldingDiscoveryProxy();
}
return instance;
}
public void setService(FoldingSlotDiscoveryService service) {
this.discoveryService = service;
}
public void newSlot(ThingUID bridgeUID, String host, String id, String description) {
if (instance != null) {
discoveryService.newSlot(bridgeUID, host, id, description);
}
}
}

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.folding.internal.discovery;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.openhab.binding.folding.internal.FoldingBindingConstants;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Component;
/**
* Discovery service implementation.
*
* The Client handler has to be configured manually, but once connected,
* it will publish discovered slots to this service. This service converts
* the internal representation to discovery results.
*
* @author Marius Bjoernstad - Initial contribution
*/
@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.folding")
public class FoldingSlotDiscoveryService extends AbstractDiscoveryService {
public FoldingSlotDiscoveryService() {
super(Collections.singleton(FoldingBindingConstants.THING_TYPE_SLOT), 10, true);
FoldingDiscoveryProxy.getInstance().setService(this);
}
@Override
protected void startScan() {
}
protected String getLabel(String host, String description) {
if (description == null) {
description = "slot";
}
int endOfLabel = description.indexOf(' ');
if (endOfLabel > 0) {
description = description.substring(0, endOfLabel);
}
endOfLabel = description.indexOf(':');
if (endOfLabel > 0) {
description = description.substring(0, endOfLabel);
}
return description + " @ " + host;
}
public void newSlot(ThingUID bridgeUID, String host, String id, String description) {
if (isBackgroundDiscoveryEnabled() && id != null) {
Map<String, Object> properties = new HashMap<>(1);
properties.put(FoldingBindingConstants.PARAM_SLOT_ID, id);
ThingUID thingUID = new ThingUID(FoldingBindingConstants.THING_TYPE_SLOT, bridgeUID, id);
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withBridge(bridgeUID).withLabel(getLabel(host, description)).build();
thingDiscovered(discoveryResult);
}
}
}

View File

@@ -0,0 +1,245 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.folding.internal.handler;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.openhab.binding.folding.internal.discovery.FoldingDiscoveryProxy;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
/**
* The {@link FoldingClientHandler} connects to a single Folding@home client,
* and controls it. The Client handler can also act as a bridge for the
* {@link SlotHandler}.
*
* @author Marius Bjørnstad - Initial contribution
*/
public class FoldingClientHandler extends BaseBridgeHandler {
private Logger logger = LoggerFactory.getLogger(FoldingClientHandler.class);
private ScheduledFuture<?> refreshJob;
private boolean initializing = true;
private Socket activeSocket;
private BufferedReader socketReader;
private Gson gson;
private volatile int idRefresh = 0;
private Map<String, SlotUpdateListener> slotUpdateListeners = new HashMap<>();
public FoldingClientHandler(Bridge thing) {
super(thing);
gson = new Gson();
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
try {
if (command instanceof RefreshType) {
refresh();
} else if (channelUID.getId().equals("run")) {
if (command == OnOffType.ON) {
sendCommand("unpause");
} else if (command == OnOffType.OFF) {
sendCommand("pause");
}
refresh();
delayedRefresh();
} else if (channelUID.getId().equals("finish")) {
if (command == OnOffType.ON) {
sendCommand("finish");
} else if (command == OnOffType.OFF) {
sendCommand("unpause");
}
refresh();
delayedRefresh();
}
} catch (IOException e) {
logger.debug("Input/output error while handing command", e);
disconnected();
}
}
@Override
public void initialize() {
BigDecimal period = (BigDecimal) getThing().getConfiguration().get("polling");
if (period != null && period.longValue() != 0) {
refreshJob = scheduler.scheduleWithFixedDelay(this::refresh, 5, period.longValue(), TimeUnit.SECONDS);
} else {
refresh();
}
}
@Override
public synchronized void dispose() {
if (refreshJob != null) {
refreshJob.cancel(true);
}
closeSocket();
}
public synchronized void refresh() {
initializing = false;
List<SlotInfo> slotList = null;
try {
Socket s = getSocket();
s.getOutputStream().write(("slot-info\r\n").getBytes());
socketReader.readLine(); // Discard PyON header
JsonReader jr = new JsonReader(socketReader);
jr.setLenient(true);
Type slotListType = new TypeToken<List<SlotInfo>>() {
}.getType();
slotList = gson.fromJson(jr, slotListType);
} catch (IOException e) {
logger.debug("Input/error while refreshing Folding client state", e);
disconnected();
return;
}
boolean running = false, finishing = true;
for (SlotInfo si : slotList) {
finishing &= "FINISHING".equals(si.status);
running |= "FINISHING".equals(si.status) || "RUNNING".equals(si.status);
SlotUpdateListener listener = slotUpdateListeners.get(si.id);
if (listener != null) {
listener.refreshed(si);
} else {
logger.debug("Providing a new discovery result for slot {}", si.id);
String host = (String) getThing().getConfiguration().get("host");
FoldingDiscoveryProxy.getInstance().newSlot(getThing().getUID(), host, si.id, si.description);
}
}
updateState(getThing().getChannel("run").getUID(), running ? OnOffType.ON : OnOffType.OFF);
updateState(getThing().getChannel("finish").getUID(), finishing ? OnOffType.ON : OnOffType.OFF);
}
public void delayedRefresh() {
final int iRefresh = ++idRefresh;
refreshJob = scheduler.schedule(() -> {
if (iRefresh == idRefresh) { // Make a best effort to not run multiple deferred refresh
refresh();
}
}, 5, TimeUnit.SECONDS);
}
void closeSocket() {
if (activeSocket != null && activeSocket.isConnected()) {
try {
socketReader.close();
} catch (IOException e) {
}
}
socketReader = null;
activeSocket = null;
}
private void disconnected() {
closeSocket();
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
}
private synchronized Socket getSocket() throws IOException {
if (activeSocket == null) {
String cfgHost = (String) getThing().getConfiguration().get("host");
BigDecimal cfgPort = (BigDecimal) getThing().getConfiguration().get("port");
String password = (String) getThing().getConfiguration().get("password");
if (cfgHost == null || cfgHost.isEmpty()) {
throw new IOException("Host was not configured");
} else if (cfgPort == null || cfgPort.intValue() == 0) {
throw new IOException("Port was not configured");
}
activeSocket = new Socket();
activeSocket.connect(new InetSocketAddress(cfgHost, cfgPort.intValue()), 2000);
socketReader = new BufferedReader(new InputStreamReader(activeSocket.getInputStream()));
readUntilPrompt(activeSocket); // Discard initial banner message
if (password != null) {
activeSocket.getOutputStream().write(("auth \"" + password + "\"\r\n").getBytes());
if (readUntilPrompt(activeSocket).startsWith("OK")) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Incorrect password");
}
} else {
updateStatus(ThingStatus.ONLINE);
}
}
return activeSocket;
}
private synchronized String readUntilPrompt(Socket s) throws IOException {
boolean havePrompt1 = false;
StringBuilder response = new StringBuilder();
try {
while (true) {
int c = socketReader.read();
if (havePrompt1) {
if (c == ' ') {
return response.toString();
} else {
response.append((char) c);
}
}
response.append((char) c);
havePrompt1 = (c == '>');
}
} catch (IOException e) {
disconnected();
throw e;
}
}
public synchronized void sendCommand(String command) throws IOException {
try {
Socket s = getSocket();
s.getOutputStream().write((command + "\r\n").getBytes());
readUntilPrompt(s);
} catch (IOException e) {
disconnected();
throw e;
}
}
public void registerSlot(String id, SlotUpdateListener slotListener) {
slotUpdateListeners.put(id, slotListener);
if (!initializing) {
delayedRefresh();
}
}
}

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.folding.internal.handler;
import java.io.IOException;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Thing handler representing a Folding slot.
*
* If control of each slot (CPU, GPUs, etc.) is desired, the user can add
* Slot handlers. The Slot handler exposes the status of a slot, and allows
* users to start / stop folding.
*
* @author Marius Bjørnstad - Initial contribution
*/
public class SlotHandler extends BaseThingHandler implements SlotUpdateListener {
private Logger logger = LoggerFactory.getLogger(SlotHandler.class);
public SlotHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
getBridgeHandler().registerSlot(myId(), this);
}
private FoldingClientHandler getBridgeHandler() {
return (FoldingClientHandler) super.getBridge().getHandler();
}
private String myId() {
return (String) getThing().getConfiguration().get("id");
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
try {
if (channelUID.getId().equals("run")) {
if (command == OnOffType.ON) {
getBridgeHandler().sendCommand("unpause " + myId());
} else if (command == OnOffType.OFF) {
getBridgeHandler().sendCommand("pause " + myId());
}
} else if (channelUID.getId().equals("finish")) {
if (command == OnOffType.ON) {
getBridgeHandler().sendCommand("finish " + myId());
} else if (command == OnOffType.OFF) {
getBridgeHandler().sendCommand("unpause " + myId());
}
}
getBridgeHandler().refresh();
getBridgeHandler().delayedRefresh();
} catch (IOException e) {
logger.debug("Input/output error while handing command to Folding slot", e);
}
}
@Override
public void refreshed(SlotInfo si) {
updateStatus(ThingStatus.ONLINE);
updateState(getThing().getChannel("status").getUID(), new StringType(si.status));
boolean finishing = "FINISHING".equals(si.status);
boolean run = finishing || "READY".equals(si.status) || "RUNNING".equals(si.status);
updateState(getThing().getChannel("finish").getUID(), finishing ? OnOffType.ON : OnOffType.OFF);
updateState(getThing().getChannel("run").getUID(), run ? OnOffType.ON : OnOffType.OFF);
updateState(getThing().getChannel("description").getUID(), new StringType(si.description));
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.folding.internal.handler;
import java.util.Map;
/**
* Slot information entity
*
* This class specifies the format of the Json-compatible data received from
* the Folding client process.
*
* @author Marius Bjoernstad - Initial contribution
*/
public class SlotInfo {
public String id, status, description, reason;
public Map<String, String> options;
boolean idle;
}

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.folding.internal.handler;
/**
* Interface for callback from Client handler to Slot handler.
*
* The client performs refreshes regularly, retrieving information about
* all slots. It then calls refreshed on all registered SlotUpdateListeners.
*
* @author Marius Bjørnstad - Initial contribution
*/
public interface SlotUpdateListener {
void refreshed(SlotInfo si);
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="folding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>Folding@home</name>
<description>Control Folding@home clients on computers.</description>
<author>Marius Bjoernstad</author>
</binding:binding>

View File

@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="folding"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<bridge-type id="client">
<label>Client</label>
<description>Folding@home client daemon (represented as bridge).</description>
<channels>
<channel id="run" typeId="run"/>
<channel id="finish" typeId="finish"/>
</channels>
<config-description>
<parameter name="host" type="text">
<label>Host</label>
<description>Hostname or IP address</description>
<default>127.0.0.1</default>
<context>network-address</context>
</parameter>
<parameter name="port" type="integer">
<label>Port</label>
<description></description>
<default>36330</default>
</parameter>
<parameter name="password" type="text">
<label>Password</label>
<description>Authentication password (leave empty for no p/w).</description>
<context>password</context>
</parameter>
<parameter name="polling" type="integer" unit="s">
<label>Polling Interval</label>
<description>Polling interval in seconds (0=polling disabled).</description>
<default>120</default>
</parameter>
</config-description>
</bridge-type>
<channel-type id="run">
<item-type>Switch</item-type>
<label>Run</label>
<description>Slot running.</description>
</channel-type>
<channel-type id="finish">
<item-type>Switch</item-type>
<label>Finish</label>
<description>Finish current work then pause.</description>
</channel-type>
<thing-type id="slot">
<supported-bridge-type-refs>
<bridge-type-ref id="client"/>
</supported-bridge-type-refs>
<label>Slot</label>
<description>Folding client compute slot.</description>
<channels>
<channel id="description" typeId="description"/>
<channel id="run" typeId="run"/>
<channel id="finish" typeId="finish"/>
<channel id="status" typeId="status"/>
</channels>
<config-description>
<parameter name="id" type="text">
<label>Slot ID</label>
<description>Slot ID, "00", "01", etc.</description>
<default>00</default>
</parameter>
</config-description>
</thing-type>
<channel-type id="description">
<item-type>String</item-type>
<label>Description</label>
<description>Description of the Folding@home slot.</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="status">
<item-type>String</item-type>
<label>Status</label>
<description>Current status</description>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>