initial commit for ecowitt binding

This commit is contained in:
Av3m 2022-01-08 21:26:34 +01:00 committed by Thomas Vogl
parent 5ae6572315
commit 13b4080e4a
11 changed files with 571 additions and 148 deletions

View File

@ -7,7 +7,7 @@
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.2.0-SNAPSHOT</version>
<version>3.2.0</version>
</parent>
<artifactId>org.openhab.binding.ecowitt</artifactId>

View File

@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2021 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 interfaces;
import org.eclipse.jdt.annotation.NonNull;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.binding.ThingHandler;
/**
* @author Av3m - Initial contribution
*/
public interface ChannelUpdater {
void updateChannel(Channel ch, String strValue);
void setStateUpdater(StateUpdater stateUpdater);
}

View File

@ -0,0 +1,25 @@
/**
* Copyright (c) 2010-2021 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 interfaces;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.types.State;
/**
* @author Av3m - Initial contribution
*/
public interface StateUpdater {
Channel getChannel(String channelName);
void updateState(ChannelUID uid, State state);
}

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.ecowitt.internal;
package org.openhab.binding.ecowitt.internal.config;
/**
* The {@link ecowittConfiguration} class contains fields mapping thing configuration parameters.
@ -18,11 +18,6 @@ package org.openhab.binding.ecowitt.internal;
* @author Av3m - Initial contribution
*/
public class ecowittConfiguration {
/**
* Sample configuration parameters. Replace with your own.
*/
public String hostname;
public String password;
public int refreshInterval;
public String bindHostname;
public int bindPort;
}

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
@ -21,14 +21,12 @@ import org.openhab.core.thing.ThingTypeUID;
*
* @author Av3m - Initial contribution
*/
@NonNullByDefault
public class ecowittBindingConstants {
private static final String BINDING_ID = "ecowitt";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_SAMPLE = new ThingTypeUID(BINDING_ID, "sample");
// List of all Channel ids
public static final String CHANNEL_1 = "channel1";
public static final ThingTypeUID THING_TYPE_GW1000 = new ThingTypeUID(BINDING_ID, "gw1000");
}

View File

@ -1,100 +0,0 @@
/**
* Copyright (c) 2010-2022 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.ecowitt.internal;
import static org.openhab.binding.ecowitt.internal.ecowittBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
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.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link ecowittHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Av3m - Initial contribution
*/
@NonNullByDefault
public class ecowittHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(ecowittHandler.class);
private @Nullable ecowittConfiguration config;
public ecowittHandler(Thing thing) {
super(thing);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (CHANNEL_1.equals(channelUID.getId())) {
if (command instanceof RefreshType) {
// TODO: handle data refresh
}
// TODO: handle command
// Note: if communication with thing fails for some reason,
// indicate that by setting the status with detail information:
// updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
// "Could not control device at IP address x.x.x.x");
}
}
@Override
public void initialize() {
config = getConfigAs(ecowittConfiguration.class);
// TODO: Initialize the handler.
// The framework requires you to return from this method quickly. Also, before leaving this method a thing
// status from one of ONLINE, OFFLINE or UNKNOWN must be set. This might already be the real thing status in
// case you can decide it directly.
// In case you can not decide the thing status directly (e.g. for long running connection handshake using WAN
// access or similar) you should set status UNKNOWN here and then decide the real status asynchronously in the
// background.
// set the thing status to UNKNOWN temporarily and let the background task decide for the real status.
// the framework is then able to reuse the resources from the thing handler initialization.
// we set this upfront to reliably check status updates in unit tests.
updateStatus(ThingStatus.UNKNOWN);
// Example for background initialization:
scheduler.execute(() -> {
boolean thingReachable = true; // <background task with long running initialization here>
// when done do:
if (thingReachable) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE);
}
});
// These logging types should be primarily used by bindings
// logger.trace("Example trace message");
// logger.debug("Example debug message");
// logger.warn("Example warn message");
// Note: When initialization can NOT be done set the status with more details for further
// analysis. See also class ThingStatusDetail for all available status details.
// Add a description to give user information to understand why thing does not work as expected. E.g.
// updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
// "Can not access device as username and/or password are invalid");
}
}

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.ecowitt.internal;
package org.openhab.binding.ecowitt.internal.factories;
import static org.openhab.binding.ecowitt.internal.ecowittBindingConstants.*;
@ -18,6 +18,8 @@ import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ecowitt.internal.handlers.EcowittThingHandler;
import org.openhab.binding.ecowitt.internal.servlets.EcowittGW1000Servlet;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
@ -35,7 +37,7 @@ import org.osgi.service.component.annotations.Component;
@Component(configurationPid = "binding.ecowitt", service = ThingHandlerFactory.class)
public class ecowittHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_SAMPLE);
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_GW1000);
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
@ -46,8 +48,8 @@ public class ecowittHandlerFactory extends BaseThingHandlerFactory {
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_SAMPLE.equals(thingTypeUID)) {
return new ecowittHandler(thing);
if (THING_TYPE_GW1000.equals(thingTypeUID)) {
return new EcowittThingHandler(thing, EcowittGW1000Servlet.class);
}
return null;

View File

@ -0,0 +1,137 @@
/**
* Copyright (c) 2010-2021 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.ecowitt.internal.handlers;
import interfaces.StateUpdater;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.openhab.binding.ecowitt.internal.config.ecowittConfiguration;
import org.openhab.binding.ecowitt.internal.servlets.AbstractEcowittServlet;
import org.openhab.core.thing.Channel;
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.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.eclipse.jetty.server.Server;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
/**
* @author Av3m - Initial contribution
*/
public class EcowittThingHandler<T extends AbstractEcowittServlet> extends BaseThingHandler implements StateUpdater {
private final Logger logger = LoggerFactory.getLogger(EcowittThingHandler.class);
private @Nullable ecowittConfiguration config;
private @Nullable Server server;
private Class<T> classServlet;
public EcowittThingHandler(Thing thing, Class<T> classServlet) {
super(thing);
this.classServlet = classServlet;
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
//no command supported - do nothing!
}
@Override
public void initialize() {
config = getConfigAs(ecowittConfiguration.class);
updateStatus(ThingStatus.UNKNOWN);
if (config == null ) {
logger.error("no config object!");
updateStatus(ThingStatus.OFFLINE);
return;
}
// Example for background initialization:
scheduler.execute(() -> {
server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setHost(config.bindHostname);
connector.setPort(config.bindPort);
connector.setReuseAddress(true);
server.addConnector(connector);
ServletHandler servletHandler = new ServletHandler();
server.setHandler(servletHandler);
String thingTypeName = thing.getThingTypeUID().getId();
logger.info("url base: {}", thingTypeName);
ServletHolder holder = servletHandler.addServletWithMapping(
this.classServlet, "/" + thingTypeName);
try {
server.start();
try {
Servlet servlet = holder.getServlet();
if ( servlet != null) {
((AbstractEcowittServlet) servlet).setStateUpdater(this);
} else {
logger.error("could not get servlet");
}
} catch (ServletException e) {
logger.error("could not get servlet");
}
updateStatus(ThingStatus.ONLINE);
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE);
logger.error("{}", e.getMessage());
}
});
}
@Override
public void dispose() {
if ( server != null && !server.isStopped() ) {
try {
server.stop();
} catch (Exception e) {
logger.error("could not stop server");
logger.error("{}", e.getMessage());
}
}
}
@Override
public Channel getChannel(String channelName) {
return thing.getChannel(channelName);
}
@Override
public void updateState(ChannelUID uid, State state) {
super.updateState(uid, state);
}
}

View File

@ -0,0 +1,75 @@
/**
* Copyright (c) 2010-2021 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.ecowitt.internal.servlets;
import interfaces.StateUpdater;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.http.HttpStatus;
import org.openhab.core.thing.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
/**
* @author Av3m - Initial contribution
*/
@NonNullByDefault
public abstract class AbstractEcowittServlet extends HttpServlet {
public AbstractEcowittServlet() {
super();
}
@Nullable
protected StateUpdater stateUpdater = null;
private final Logger logger = LoggerFactory.getLogger(AbstractEcowittServlet.class);
protected void doPost(HttpServletRequest request, HttpServletResponse response)
{
for (Map.Entry<String, String[]> stringEntry : request.getParameterMap().entrySet()) {
if ( stringEntry.getValue().length > 0 && !stringEntry.getValue()[0].equals("")) {
updateValue(stringEntry.getKey(), stringEntry.getValue()[0]);
}
}
response.setStatus(HttpStatus.OK_200);
}
private void updateValue(String field, String value) {
if ( stateUpdater == null ) { return; }
@Nullable Channel ch = stateUpdater.getChannel(field);
if (ch == null ) {
logger.debug("channel for field {} (current value: {}) not implemented", field, value);
return;
}
updateChannel(ch, value);
}
protected abstract void updateChannel(Channel ch, String strValue);
public abstract String getName();
public void setStateUpdater(StateUpdater stateUpdater) {
this.stateUpdater = stateUpdater;
}
}

View File

@ -0,0 +1,154 @@
/**
* Copyright (c) 2010-2021 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.ecowitt.internal.servlets;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.MetricPrefix;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.type.ChannelTypeUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
/**
* @author Av3m - Initial contribution
*/
@NonNullByDefault
public class EcowittGW1000Servlet extends AbstractEcowittServlet {
public EcowittGW1000Servlet() {
super();
}
@Override
public String getName() {
return EcowittGW1000Servlet.class.getName();
}
@Override
protected void updateChannel(Channel ch, String strValue) {
if (stateUpdater == null) {return; }
String id = ch.getUID().getId();
@Nullable ChannelTypeUID channelUID = ch.getChannelTypeUID();
if ( channelUID == null ) {return; }
String type = channelUID.getId();
float value;
switch(type) {
case "humidity":
case "atmospheric-humidity":
case "moisture":
value = Float.parseFloat(strValue);
stateUpdater.updateState(ch.getUID(), new QuantityType<>(value, Units.PERCENT));
break;
case "temperature":
case "outdoor-temperature":
value = Float.parseFloat(strValue);
stateUpdater.updateState(ch.getUID(), new QuantityType<>(value, ImperialUnits.FAHRENHEIT));
break;
case "uv-index":
case "lightning-num":
value = Float.parseFloat(strValue);
stateUpdater.updateState(ch.getUID(), new DecimalType(value));
break;
case "rain-amount":
value = Float.parseFloat(strValue);
stateUpdater.updateState(ch.getUID(), new QuantityType<>(value, ImperialUnits.INCH));
break;
case "rain-rate":
value = Float.parseFloat(strValue);
stateUpdater.updateState(ch.getUID(), new QuantityType<>(value, Units.INCHES_PER_HOUR));
break;
case "wind-speed":
value = Float.parseFloat(strValue);
stateUpdater.updateState(ch.getUID(), new QuantityType<>(value, ImperialUnits.MILES_PER_HOUR));
break;
case "wind-direction":
value = Float.parseFloat(strValue);
stateUpdater.updateState(ch.getUID(), new QuantityType<>(value, Units.DEGREE_ANGLE));
break;
case "barometric-pressure":
value = Float.parseFloat(strValue);
stateUpdater.updateState(ch.getUID(), new QuantityType<>(value, ImperialUnits.INCH_OF_MERCURY));
break;
case "solar-radiation":
value = Float.parseFloat(strValue);
stateUpdater.updateState(ch.getUID(), new QuantityType<>(value, Units.IRRADIANCE));
break;
case "lightning-distance":
value = Float.parseFloat(strValue);
stateUpdater.updateState(ch.getUID(), new QuantityType<>(value, MetricPrefix.KILO(SIUnits.METRE)));
break;
case "lightning-timestamp":
Instant i = Instant.ofEpochSecond(Long.parseLong(strValue));
stateUpdater.updateState(ch.getUID(), new DateTimeType(ZonedDateTime.ofInstant(i, ZoneOffset.UTC)));
break;
case "pm25-particles":
value = Float.parseFloat(strValue);
stateUpdater.updateState(ch.getUID(), new QuantityType<>(value, Units.MICROGRAM_PER_CUBICMETRE));
break;
case "battery-level":
value = Float.parseFloat(strValue);
int batLevel = 0;
switch(id) {
case "pm25batt1":
case "pm25batt2":
case "wh57batt":
batLevel = (int) (100/5 * value);
break;
case "wh40batt":
case "soilbatt1":
case "soilbatt2":
case "soilbatt3":
case "soilbatt4":
float minLevel = 1.1f;
float maxLevel = 1.5f;
if (value >= maxLevel) {batLevel = 100;}
else if (value < minLevel) {batLevel = 0;}
else { batLevel = (int) (100 * (value - minLevel) / (maxLevel - minLevel)); }
break;
case "wh65batt":
case "battout":
case "wh26batt":
case "wh25batt":
if ( value == 0) { batLevel = 100;}
else { batLevel = 0;}
break;
}
stateUpdater.updateState(ch.getUID(), new QuantityType<>(batLevel, Units.PERCENT));
break;
}
}
}

View File

@ -5,42 +5,153 @@
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Sample Thing Type -->
<thing-type id="sample">
<!-- Comment this in for Things which need a Bridge to function.
<supported-bridge-type-refs>
<bridge-type-ref id="ecowittBridge" />
</supported-bridge-type-refs>
-->
<label>ecowitt Binding Thing</label>
<description>Sample thing for ecowitt Binding</description>
<thing-type id="gw1000">
<label>Ecowitt GW1000 Thing</label>
<description>Thing for GW1000 Weather Station</description>
<channels>
<channel id="channel1" typeId="sample-channel"/>
</channels>
<channel id="tempf" typeId="system.outdoor-temperature"><label>outdoor temperature</label><description>outdoor temperature</description></channel>
<channel id="tempinf" typeId="system.outdoor-temperature"><label>indoor temperature</label><description>indoor temperature</description></channel>
<channel id="temp1f" typeId="system.outdoor-temperature"><label>temperature CH1</label><description>temperature CH1</description></channel>
<channel id="temp2f" typeId="system.outdoor-temperature"><label>temperature CH2</label><description>temperature CH2</description></channel>
<channel id="temp3f" typeId="system.outdoor-temperature"><label>temperature CH3</label><description>temperature CH3</description></channel>
<channel id="temp4f" typeId="system.outdoor-temperature"><label>temperature CH4</label><description>temperature CH4</description></channel>
<channel id="temp5f" typeId="system.outdoor-temperature"><label>temperature CH5</label><description>temperature CH5</description></channel>
<channel id="temp6f" typeId="system.outdoor-temperature"><label>temperature CH6</label><description>temperature CH6</description></channel>
<channel id="temp7f" typeId="system.outdoor-temperature"><label>temperature CH7</label><description>temperature CH7</description></channel>
<channel id="temp8f" typeId="system.outdoor-temperature"><label>temperature CH8</label><description>temperature CH8</description></channel>
<channel id="humidity" typeId="system.atmospheric-humidity"><label>outdoor humidity</label><description>outdoor humidity</description></channel>
<channel id="humidityin" typeId="humidity"><label>indoor humidity</label><description>indoor humidity</description></channel>
<channel id="humidity1" typeId="humidity"><label>humidity CH1</label><description>humidity CH1</description></channel>
<channel id="humidity2" typeId="humidity"><label>humidity CH2</label><description>humidity CH2</description></channel>
<channel id="humidity3" typeId="humidity"><label>humidity CH3</label><description>humidity CH3</description></channel>
<channel id="humidity4" typeId="humidity"><label>humidity CH4</label><description>humidity CH4</description></channel>
<channel id="humidity5" typeId="humidity"><label>humidity CH5</label><description>humidity CH5</description></channel>
<channel id="humidity6" typeId="humidity"><label>humidity CH6</label><description>humidity CH6</description></channel>
<channel id="humidity7" typeId="humidity"><label>humidity CH7</label><description>humidity CH7</description></channel>
<channel id="humidity8" typeId="humidity"><label>humidity CH8</label><description>humidity CH8</description></channel>
<channel id="winddir" typeId="system.wind-direction"><label>wind direction</label><description>wind direction</description></channel>
<channel id="windspeedmph" typeId="system.wind-speed"><label>wind speed</label><description>wind speed</description></channel>
<channel id="windgustmph" typeId="system.wind-speed"><label>wind gust</label><description>wind gust</description></channel>
<channel id="maxdailygust" typeId="system.wind-speed"><label>daily maximum value gust</label><description>daily maximum value gust</description></channel>
<channel id="totalrainin" typeId="rain-amount"><label>total amount of rain</label><description>total amount of rain</description></channel>
<channel id="eventrainin" typeId="rain-amount"><label>amount of rain (event-based)</label><description>amount of rain (event-based)</description></channel>
<channel id="yearlyrainin" typeId="rain-amount"><label>yearly amount of rain</label><description>yearly amount of rain</description></channel>
<channel id="monthlyrainin" typeId="rain-amount"><label>monthly amount of rain</label><description>monthly amount of rain</description></channel>
<channel id="weeklyrainin" typeId="rain-amount"><label>weekly amount of rain</label><description>weekly amount of rain</description></channel>
<channel id="dailyrainin" typeId="rain-amount"><label>daily amount of rain</label><description>daily amount of rain</description></channel>
<channel id="hourlyrainin" typeId="rain-amount"><label>hourly amount of rain</label><description>hourly amount of rain</description></channel>
<channel id="rainratein" typeId="rain-rate"><label>rain amount per hour</label><description>rain amount per hour</description></channel>
<channel id="solarradiation" typeId="solar-radiation"><label>solar radiation (intensity)</label><description>solar radiation (intensity)</description></channel>
<channel id="uv" typeId="uv-index"><label>uv index</label><description>uv index</description></channel>
<channel id="baromabsin" typeId="system.barometric-pressure"><label>absolute barometric pressure</label><description>absolute barometric pressure</description></channel>
<channel id="baromrelin" typeId="system.barometric-pressure"><label>relative barometric pressure</label><description>relative barometric pressure</description></channel>
<channel id="soilmoisture1" typeId="moisture"><label>soil moisture CH1</label><description>soil moisture CH1</description></channel>
<channel id="soilmoisture2" typeId="moisture"><label>soil moisture CH2</label><description>soil moisture CH2</description></channel>
<channel id="soilmoisture3" typeId="moisture"><label>soil moisture CH3</label><description>soil moisture CH3</description></channel>
<channel id="soilmoisture4" typeId="moisture"><label>soil moisture CH4</label><description>soil moisture CH4</description></channel>
<channel id="lightning" typeId="lightning-distance"><label>distance of last lightning</label><description>distance of last lightning</description></channel>
<channel id="lightning_time" typeId="lightning-timestamp"><label>timestamp of last lightning</label><description>timestamp of last lightning</description></channel>
<channel id="lightning_num" typeId="lightning-num"><label>number of lightnings per day</label><description>number of lightnings</description></channel>
<channel id="pm25_ch1" typeId="pm25-particles"><label>PM2.5 particle sensor CH1</label><description>PM2.5 particle sensor CH1</description></channel>
<channel id="pm25_avg_24h_ch1" typeId="pm25-particles"><label>daily average PM2.5 particle sensor CH1</label><description>daily average PM2.5 particle sensor CH1</description></channel>
<channel id="pm25_ch2" typeId="pm25-particles"><label>PM2.5 particle sensor CH2</label><description>PM2.5 particle sensor CH2</description></channel>
<channel id="pm25_avg_24h_ch2" typeId="pm25-particles"><label>daily average PM2.5 particle sensor CH2</label><description>daily average PM2.5 particle sensor CH2</description></channel>
<channel id="wh57batt" typeId="system.battery-level"><label>battery level WH57 sensor</label><description>battery level WH57 sensor</description></channel>
<channel id="wh65batt" typeId="system.battery-level"><label>battery level WH65 sensor</label><description>battery level WH65 sensor</description></channel>
<channel id="pm25batt1" typeId="system.battery-level"><label>battery level PM2.5 CH1 sensor</label><description>battery level PM2.5 CH1 sensor</description></channel>
<channel id="pm25batt2" typeId="system.battery-level"><label>battery level PM2.5 CH2 sensor</label><description>battery level PM2.5 CH2 sensor</description></channel>
<channel id="soilbatt1" typeId="system.battery-level"><label>battery level soil moisture sensor CH1</label><description>battery level soil moisture sensor CH1</description></channel>
<channel id="soilbatt2" typeId="system.battery-level"><label>battery level soil moisture sensor CH2</label><description>battery level soil moisture sensor CH2</description></channel>
<channel id="soilbatt3" typeId="system.battery-level"><label>battery level soil moisture sensor CH3</label><description>battery level soil moisture sensor CH3</description></channel>
<channel id="soilbatt4" typeId="system.battery-level"><label>battery level soil moisture sensor CH4</label><description>battery level soil moisture sensor CH4</description></channel>
</channels>
<config-description>
<parameter name="hostname" type="text" required="true">
<parameter name="bindHostname" type="text" required="true">
<context>network-address</context>
<label>Hostname</label>
<description>Hostname or IP address of the device</description>
<label>Hostname for HTTP Server</label>
<default>0.0.0.0</default>
<description/>
</parameter>
<parameter name="password" type="text" required="true">
<context>password</context>
<label>Password</label>
<description>Passwort to access the device</description>
</parameter>
<parameter name="refreshInterval" type="integer" unit="s" min="1">
<label>Refresh Interval</label>
<description>Interval the device is polled in sec.</description>
<parameter name="bindPort" type="integer" min="1000">
<label>Port for HTTP Server</label>
<default>40111</default>
<description/>
</parameter>
</config-description>
</thing-type>
<!-- Sample Channel Type -->
<channel-type id="sample-channel">
<item-type>Number:Temperature</item-type>
<label>Example Temperature</label><!-- Use uppercase words, except prepositions. 2-3 words, max 25 chars -->
<description>Sample channel for ecowitt Binding</description>
<channel-type id="humidity">
<item-type>Number:Dimensionless</item-type>
<label>Humidity</label>
<category>Humidity</category>
<state readOnly="true" pattern="%3f %" />
</channel-type>
<channel-type id="rain-amount">
<item-type>Number:Length</item-type>
<label>Rain Amount</label>
<category>Rain</category>
<state readOnly="true" pattern="%.1f mm" />
</channel-type>
<channel-type id="rain-rate">
<item-type>Number:Speed</item-type>
<label>Rain Amount</label>
<category>Rain</category>
<state readOnly="true" pattern="%.1f mm/h" />
</channel-type>
<channel-type id="solar-radiation">
<item-type>Number:Intensity</item-type>
<label>Solar Radiation</label>
<category>Sun</category>
<state readOnly="true" pattern="%.1f W/m²" />
</channel-type>
<channel-type id="uv-index">
<item-type>Number</item-type>
<label>UV Index</label>
<category>Sun</category>
</channel-type>
<channel-type id="moisture">
<item-type>Number:Dimensionless</item-type>
<label>Soil Moisture</label>
<category>Moisture</category>
<state readOnly="true" pattern="%.1f %" />
</channel-type>
<channel-type id="lightning-distance">
<item-type>Number:Length</item-type>
<label>Distance of last lightning</label>
<category>Energy</category>
</channel-type>
<channel-type id="lightning-num">
<item-type>Number</item-type>
<label>Number of Lightnings this day</label>
<category>Energy</category>
</channel-type>
<channel-type id="lightning-timestamp">
<item-type>DateTime</item-type>
<label>Timestamp of last lightning</label>
<category>Energy</category>
</channel-type>
<channel-type id="pm25-particles">
<item-type>Number</item-type>
<label>PM2.5 Particle Density</label>
<state readOnly="true" pattern="%.1f µg/m³" />
</channel-type>
</thing:thing-descriptions>