# Gardena Binding
-This is the binding for [Gardena smart system](https://www.gardena.com/de/rasenpflege/smartsystem/).
+This is the binding for [Gardena smart system](https://www.gardena.com/smart).
This binding allows you to integrate, view and control Gardena smart system devices in the openHAB environment.
## Supported Things
Devices connected to Gardena smart system, currently:
-| Thing type | Name |
-|--------------------------|--------------------------|
-| bridge | smart Garden Gateway |
-| mower | smart Sileno(+) Mower |
-| watering_computer | smart Water Control |
-| sensor | smart Sensor |
-| electronic_pressure_pump | smart Pressure Pump |
-| power | smart Power |
-| ic24 | smart Irrigation Control |
-
-The schedules are not yet integrated!
+| Thing type | Name |
+|--------------------------|----------------------------------------------------|
+| bridge | smart Gateway |
+| mower | smart SILENO(+), SILENO city, SILENO life Mower |
+| sensor | smart Sensor |
+| pump | smart Pressure Pump |
+| power | smart Power Adapter |
+| water_control | smart Water Control |
+| irrigation_control | smart Irrigation Control |
## Discovery
There are several settings for an account:
-| Name | Required | Description |
-|-----------------------|----------|------------------------------------------------------------------------------------------|
-| **email** | yes | The email address for logging into the Gardena smart system |
-| **password** | yes | The password for logging into the Gardena smart system |
-| **sessionTimeout** | no | The timeout in minutes for a session to Gardena smart system (default = 30) |
-| **connectionTimeout** | no | The timeout in seconds for connections to Gardena smart system (default = 10) |
-| **refresh** | no | The interval in seconds for refreshing the data from Gardena smart system (default = 60) |
+| Name | Required | Description |
+|-----------------------|----------|-----------------------------------------------------------------------------------------------------|
+| **email** | yes | The email address for logging into the Gardena smart system |
+| **password** | yes | The password for logging into the Gardena smart system |
+| **apiKey** | yes | The Gardena smart system integration API key |
+| **connectionTimeout** | no | The timeout in seconds for connections to Gardena smart system integration API (default = 10) |
-**Example**
+### Obtaining your API Key
-### Things
+1. Goto https://developer.husqvarnagroup.cloud/, sign in using your GARDENA smart system account and accept the terms of use
+2. Create and save a new application via the 'Create application' button
+3. Connect both _Authentication API_ and _GARDENA smart system API_ to your application via the 'Connect new API' button
+4. Copy the application key to use with this binding as _apiKey_
-Minimal Thing configuration:
+## Examples
-```java
-Bridge gardena:account:home [ email="...", password="..." ]
-```
+### Things
-Configuration with refresh:
+Minimal Thing configuration:
```java
-Bridge gardena:account:home [ email="...", password="...", refresh=30 ]
+Bridge gardena:account:home [ email="...", password="...", apiKey="..." ]
```
Configuration of multiple bridges:
```java
-Bridge gardena:account:home1 [ email="...", password="..." ]
-Bridge gardena:account:home2 [ email="...", password="..." ]
+Bridge gardena:account:home1 [ email="...", password="...", apiKey="..." ]
+Bridge gardena:account:home2 [ email="...", password="...", apiKey="..." ]
```
Once a connection to an account is established, connected Things are discovered automatically.
-Alternatively, you can manually configure a Thing:
+Alternatively, you can manually configure Things:
-```perl
-Bridge gardena:account:home [ email="...", password="..." ]
+```java
+Bridge gardena:account:home [ email="...", password="...", apiKey="..." ]
{
Thing mower myMower [ deviceId="c81ad682-6e45-42ce-bed1-6b4eff5620c8" ]
+ Thing water_control myWaterControl [ deviceId="c81ad682-6e45-42ce-bed1-6b4eff5620c8" ]
+ Thing sensor mySensor [ deviceId="c81ad682-6e45-42ce-bed1-6b4eff5620c8" ]
+ Thing pump myEPP [ deviceId="c81ad682-6e45-42ce-bed1-6b4eff5620c8" ]
+ Thing power myPowerSocket [ deviceId="c81ad682-6e45-42ce-bed1-6b4eff5620c8" ]
+ Thing irrigation_control myIrrigationControl [ deviceId="c81ad682-6e45-42ce-bed1-6b4eff5620c8" ]
}
```
-## Items
+### Items
In the items file, you can link items to channels of your Things:
```java
-Number Battery_Level "Battery [%d %%]" {channel="gardena:mower:home:myMower:battery#level"}
+Number Mower_Battery_Level "Battery [%d %%]" {channel="gardena:mower:home:myMower:common#batteryLevel"}
```
-## Sensor refresh
+### Sensor refresh
-You can send a REFRESH command to items linked to these Sensor channels:
+Sensor refresh commands are not yet supported by the Gardena smart system integration API.
-- ambient_temperature#temperature
-- soil_temperature#temperature
-- humidity#humidity
-- light#light
+### Example configuration
-In the console:
-
-```shell
-smarthome:send ITEM_NAME REFRESH
```
+// smart Water Control
+String WC_Valve_Activity "Valve Activity" { channel="gardena:water_control:home:myWateringComputer:valve#activity" }
+Number WC_Valve_Duration "Last Watering Duration [%d min]" { channel="gardena:water_control:home:myWateringComputer:valve#duration" }
-In scripts:
+Number WC_Valve_cmd_Duration "Command Duration [%d min]" { channel="gardena:water_control:home:myWateringComputer:valve_commands#commandDuration" }
+Switch WC_Valve_cmd_OpenWithDuration "Watering Timer [%d min]" { channel="gardena:water_control:home:myWateringComputer:valve_commands#start_seconds_to_override" }
+Switch WC_Valve_cmd_CloseValve "Stop Switch" { channel="gardena:water_control:home:myWateringComputer:valve_commands#stop_until_next_task" }
-```
-import org.openhab.core.types.RefreshType
-...
-sendCommand(ITEM_NAME, RefreshType.REFRESH)
+smarthome:status WC_Valve_Duration // returns the duration of the last watering request if still active, or 0
+smarthome:status WC_Valve_Activity // returns the current valve activity (CLOSED|MANUAL_WATERING|SCHEDULED_WATERING)
```
-## Examples
+All channels are read-only, except the command group and the lastUpdate timestamp
```
-// smart Water Control
-Switch Watering_Valve "Valve" { channel="gardena:watering_computer:home:myValve:outlet#valve_open" }
-Number Watering_Duration "Duration [%d min]" { channel="gardena:watering_computer:home:myValve:outlet#button_manual_override_time" }
-
-// smart Power Plug
-String Power_Timer "Power Timer [%s]" { channel="gardena:power:home:myPowerplug:power#power_timer" }
-
-// smart Irrigation Control
-Number Watering_Timer_1 "Watering Timer 1 [%d min] { channel="gardena:ic24:home:myIrrigationController:watering#watering_timer_1" }
-
-// smart Pressure Pump
-Number Pump_Timer "Pump Timer [%d min] { channel="gardena:electronic_pressure_pump:home:myPressurePump:manual_watering#manual_watering_timer" }
+smarthome:send WC_Valve_cmd_Duration.sendCommand(10) // set the duration for the command to 10min
+smarthome:send WC_Valve_cmd_OpenWithDuration.sendCommand(ON) // start watering
+smarthome:send WC_Valve_cmd_CloseValve.sendCommand(ON) // stop any active watering
```
+If you send a REFRESH command to the last update timestamp (no matter which thing), **ALL** items from **ALL** things are updated
```
-Watering_Duration.sendCommand(30) // 30 minutes
-Watering_Valve.sendCommand(ON)
-
-Power_Timer.sendCommand("on")
-Power_Timer.sendCommand("off")
-Power_Timer.sendCommand("180") // on for 180 seconds
+DateTime LastUpdate "LastUpdate [%1$td.%1$tm.%1$tY %1$tH:%1$tM]" { channel="gardena:water_control:home:myWateringComputer:common#lastUpdate_timestamp" }
-Watering_Timer_1.sendCommand(0) // turn off watering
-Watering_Timer_1.sendCommand(30) // turn on for 30 minutes
-
-Pump_Timer.sendCommand(0) // turn the pump off
-Pump_Timer.sendCommand(30) // turn the pump on for 30 minutes
+// refresh ALL items
+smarthome:send LastUpdate REFRESH
```
### Debugging and Tracing
log:set INFO org.openhab.binding.gardena
```
-**Note:** The Online/Offline status is not always valid. I'm using the ```connection_status``` property Gardena sends for each device, but it seems not to be very reliable.
-My watering control for example shows offline, but it is still working.
-I have to press the button on the device, then the status changed to online.
-My mower always shows online, regardless of whether it is switched on or off.
-This is not a binding issue, it must be fixed by Gardena.
-
-When the binding sends a command to a device, it communicates only with the Gardena online service.
-It has not control over, whether the command is sent from the online service via your gateway to the device.
+**Notes and known limitations:**
+When the binding sends a command to a device, it communicates only with the Gardena smart system integration API.
+It has no control over whether the command is sent from the online service via your gateway to the device.
It is the same as if you send the command in the Gardena App.
+
+Schedules, sensor-refresh commands, irrigation control master valve configuration etc. are not supported.
+This binding relies on the GARDENA smart system integration API.
+Further API documentation: https://developer.1689.cloud/apis/GARDENA+smart+system+API
+
+###Troubleshooting
+Occasionally it can happen that the API key is no longer valid.
+
+```
+HTTP protocol violation: Authentication challenge without WWW-Authenticate header
+```
+
+If this error message appears in the log file, simply renew or delete/create a new API key as described in 'Obtaining your API Key' and restart openHAB.
+
import org.openhab.core.thing.ThingTypeUID;
/**
- * The {@link GardenaBinding} class defines common constants, which are used across the whole binding.
+ * The {@link GardenaBindingConstants} class defines common constants, which are used across the whole binding.
*
* @author Gerhard Riegler - Initial contribution
*/
@NonNullByDefault
public class GardenaBindingConstants {
-
public static final String BINDING_ID = "gardena";
public static final ThingTypeUID THING_TYPE_ACCOUNT = new ThingTypeUID(BINDING_ID, "account");
- public static final String PROPERTY_MANUFACTURER = "manufacturer";
- public static final String PROPERTY_PRODUCT = "product";
- public static final String PROPERTY_SERIALNUMBER = "serial_number";
- public static final String PROPERTY_SGTIN = "sgtin";
- public static final String PROPERTY_VERSION = "version";
- public static final String PROPERTY_CATEGORY = "category";
-
- public static final String PROPERTY_CONNECTION_STATUS = "connection_status";
- public static final String PROPERTY_CONNECTION_STATUS_UNREACH_VALUE = "status_device_unreachable";
- public static final String PROPERTY_STATE = "state";
+ public static final String PROPERTY_SERIALNUMBER = "serial";
+ public static final String PROPERTY_MODELTYPE = "modelType";
- public static final String ABILITY_DEVICE_INFO = "device_info";
- public static final String ABILITY_RADIO = "radio";
+ public static final String CONNECTION_STATUS_ONLINE = "ONLINE";
- public static final String SETTING_LEAKAGE_DETECTION = "leakage_detection";
- public static final String SETTING_OPERATION_MODE = "operating_mode";
- public static final String SETTING_TURN_ON_PRESSURE = "turn_on_pressure";
+ public static final String DEVICE_TYPE_MOWER = "mower";
+ public static final String DEVICE_TYPE_PUMP = "pump";
+ public static final String DEVICE_TYPE_IRRIGATION_CONTROL = "irrigation_control";
+ public static final String DEVICE_TYPE_WATER_CONTROL = "water_control";
+ public static final String DEVICE_TYPE_SENSOR = "sensor";
+ public static final String DEVICE_TYPE_POWER = "power";
}
*/
package org.openhab.binding.gardena.internal;
-import java.util.Set;
-import java.util.concurrent.ScheduledExecutorService;
+import java.util.Collection;
-import org.openhab.binding.gardena.internal.config.GardenaConfig;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.gardena.internal.exception.GardenaDeviceNotFoundException;
import org.openhab.binding.gardena.internal.exception.GardenaException;
-import org.openhab.binding.gardena.internal.model.Device;
-import org.openhab.binding.gardena.internal.model.Location;
-import org.openhab.binding.gardena.internal.model.Setting;
+import org.openhab.binding.gardena.internal.model.dto.Device;
+import org.openhab.binding.gardena.internal.model.dto.api.DataItem;
+import org.openhab.binding.gardena.internal.model.dto.command.GardenaCommand;
/**
- * Describes the methods required for the communication with Gardens Smart Home.
+ * Describes the methods required for the communication with Gardena smart system.
*
* @author Gerhard Riegler - Initial contribution
*/
+@NonNullByDefault
public interface GardenaSmart {
/**
- * Initializes Gardena Smart Home and loads all devices from all locations.
- */
- public void init(String id, GardenaConfig config, GardenaSmartEventListener eventListener,
- ScheduledExecutorService scheduler) throws GardenaException;
-
- /**
- * Disposes Gardena Smart Home.
+ * Disposes Gardena smart system.
*/
public void dispose();
/**
- * Loads all devices from all locations.
- */
- public void loadAllDevices() throws GardenaException;
-
- /**
- * Returns all locations.
+ * Returns all devices from all locations.
*/
- public Set<Location> getLocations();
+ public Collection<Device> getAllDevices();
/**
* Returns a device with the given id.
*/
- public Device getDevice(String deviceId) throws GardenaException;
+ public Device getDevice(String deviceId) throws GardenaDeviceNotFoundException;
/**
- * Sends a command to Gardena Smart Home.
+ * Sends a command to Gardena smart system.
*/
- public void sendCommand(Device device, GardenaSmartCommandName commandName, Object value) throws GardenaException;
+ public void sendCommand(DataItem<?> dataItem, GardenaCommand gardenaCommand) throws GardenaException;
/**
- * Sends a setting to Gardena Smart Home.
+ * Returns the id.
*/
- public void sendSetting(Setting setting, Object value) throws GardenaException;
+ public String getId();
/**
- * Returns the id.
+ * Restarts all WebSocket.
*/
- public String getId();
+ public void restartWebsockets();
}
+++ /dev/null
-/**
- * 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.gardena.internal;
-
-/**
- * A names of all GardenaSmart commands.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public enum GardenaSmartCommandName {
- // mower
- PARK_UNTIL_FURTHER_NOTICE,
- PARK_UNTIL_NEXT_TIMER,
- START_OVERRIDE_TIMER,
- START_RESUME_SCHEDULE,
- DURATION_PROPERTY,
-
- // sensor
- MEASURE_AMBIENT_TEMPERATURE,
- MEASURE_LIGHT,
- MEASURE_SOIL_HUMIDITY,
- MEASURE_SOIL_TEMPERATURE,
-
- // outlet
- OUTLET_MANUAL_OVERRIDE_TIME,
- OUTLET_VALVE,
-
- // power
- POWER_TIMER,
-
- // irrigation control
- WATERING_TIMER_VALVE_1,
- WATERING_TIMER_VALVE_2,
- WATERING_TIMER_VALVE_3,
- WATERING_TIMER_VALVE_4,
- WATERING_TIMER_VALVE_5,
- WATERING_TIMER_VALVE_6,
-
- // pump
- PUMP_MANUAL_WATERING_TIMER
-}
*/
package org.openhab.binding.gardena.internal;
-import org.openhab.binding.gardena.internal.model.Device;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.gardena.internal.model.dto.Device;
/**
* Listener with methods called from events within the {@link GardenaSmart} class.
*
* @author Gerhard Riegler - Initial contribution
*/
+@NonNullByDefault
public interface GardenaSmartEventListener {
/**
public void onNewDevice(Device device);
/**
- * Called when a device has been deleted.
+ * Called when an unrecoverable error occurs.
*/
- public void onDeviceDeleted(Device device);
-
- /**
- * Called when the connection is lost to Gardena Smart Home.
- */
- public void onConnectionLost();
-
- /**
- * Called when the connection is resumed to Gardena Smart Home.
- */
- public void onConnectionResumed();
+ public void onError();
}
*/
package org.openhab.binding.gardena.internal;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-import org.apache.commons.lang.ObjectUtils;
-import org.apache.commons.lang.StringUtils;
+import java.util.*;
+import java.util.concurrent.*;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.HttpResponseException;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.util.AbstractTypedContentProvider;
+import org.eclipse.jetty.client.util.FormContentProvider;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.Fields;
import org.openhab.binding.gardena.internal.config.GardenaConfig;
-import org.openhab.binding.gardena.internal.config.GardenaConfigWrapper;
import org.openhab.binding.gardena.internal.exception.GardenaDeviceNotFoundException;
import org.openhab.binding.gardena.internal.exception.GardenaException;
-import org.openhab.binding.gardena.internal.exception.GardenaUnauthorizedException;
-import org.openhab.binding.gardena.internal.model.Ability;
-import org.openhab.binding.gardena.internal.model.Device;
-import org.openhab.binding.gardena.internal.model.Devices;
-import org.openhab.binding.gardena.internal.model.Errors;
-import org.openhab.binding.gardena.internal.model.Location;
-import org.openhab.binding.gardena.internal.model.Locations;
-import org.openhab.binding.gardena.internal.model.NoResult;
-import org.openhab.binding.gardena.internal.model.Property;
-import org.openhab.binding.gardena.internal.model.PropertyValue;
-import org.openhab.binding.gardena.internal.model.Session;
-import org.openhab.binding.gardena.internal.model.SessionWrapper;
-import org.openhab.binding.gardena.internal.model.Setting;
-import org.openhab.binding.gardena.internal.model.command.Command;
-import org.openhab.binding.gardena.internal.model.command.MowerParkUntilFurtherNoticeCommand;
-import org.openhab.binding.gardena.internal.model.command.MowerParkUntilNextTimerCommand;
-import org.openhab.binding.gardena.internal.model.command.MowerStartOverrideTimerCommand;
-import org.openhab.binding.gardena.internal.model.command.MowerStartResumeScheduleCommand;
-import org.openhab.binding.gardena.internal.model.command.SensorMeasureAmbientTemperatureCommand;
-import org.openhab.binding.gardena.internal.model.command.SensorMeasureLightCommand;
-import org.openhab.binding.gardena.internal.model.command.SensorMeasureSoilHumidityCommand;
-import org.openhab.binding.gardena.internal.model.command.SensorMeasureSoilTemperatureCommand;
-import org.openhab.binding.gardena.internal.model.command.SettingCommand;
-import org.openhab.binding.gardena.internal.model.command.SettingCommandWrapper;
-import org.openhab.binding.gardena.internal.model.command.WateringCancelOverrideCommand;
-import org.openhab.binding.gardena.internal.model.command.WateringManualOverrideCommand;
-import org.openhab.binding.gardena.internal.model.deser.DateDeserializer;
-import org.openhab.binding.gardena.internal.model.deser.PropertyValueDeserializer;
-import org.openhab.binding.gardena.internal.model.property.BaseProperty;
-import org.openhab.binding.gardena.internal.model.property.IrrigationControlWateringProperty;
-import org.openhab.binding.gardena.internal.model.property.PropertyWrapper;
-import org.openhab.binding.gardena.internal.model.property.StringProperty;
+import org.openhab.binding.gardena.internal.model.DataItemDeserializer;
+import org.openhab.binding.gardena.internal.model.dto.Device;
+import org.openhab.binding.gardena.internal.model.dto.api.*;
+import org.openhab.binding.gardena.internal.model.dto.command.GardenaCommand;
+import org.openhab.binding.gardena.internal.model.dto.command.GardenaCommandRequest;
+import org.openhab.core.io.net.http.HttpClientFactory;
+import org.openhab.core.io.net.http.WebSocketFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
+import com.google.gson.JsonSyntaxException;
/**
- * {@link GardenaSmart} implementation to access Gardena Smart Home.
+ * {@link GardenaSmart} implementation to access Gardena smart system.
*
* @author Gerhard Riegler - Initial contribution
*/
-public class GardenaSmartImpl implements GardenaSmart {
+@NonNullByDefault
+public class GardenaSmartImpl implements GardenaSmart, GardenaSmartWebSocketListener {
private final Logger logger = LoggerFactory.getLogger(GardenaSmartImpl.class);
- public static final String DEVICE_CATEGORY_PUMP = "electronic_pressure_pump";
- private static final String ABILITY_MOWER = "mower";
- private static final String ABILITY_OUTLET = "outlet";
- private static final String ABILITY_HUMIDITY = "humidity";
- private static final String ABILITY_LIGHT = "light";
- private static final String ABILITY_AMBIENT_TEMPERATURE = "ambient_temperature";
- private static final String ABILITY_SOIL_TEMPERATURE = "soil_temperature";
- private static final String ABILITY_POWER = "power";
- private static final String ABILITY_WATERING = "watering";
- private static final String ABILITY_MANUAL_WATERING = "manual_watering";
-
- private static final String PROPERTY_BUTTON_MANUAL_OVERRIDE_TIME = "button_manual_override_time";
- private static final String PROPERTY_POWER_TIMER = "power_timer";
- private static final String PROPERTY_WATERING_TIMER = "watering_timer_";
- private static final String PROPERTY_MANUAL_WATERING_TIMER = "manual_watering_timer";
-
- private static final String DEVICE_CATEGORY_MOWER = "mower";
- private static final String DEVICE_CATEGORY_GATEWAY = "gateway";
-
- private static final String DEFAULT_MOWER_DURATION = "180";
-
- private static final String URL = "https://smart.gardena.com";
- private static final String URL_LOGIN = URL + "/v1/auth/token";
- private static final String URL_LOCATIONS = URL + "/v1/locations/?user_id=";
- private static final String URL_DEVICES = URL + "/v1/devices/?locationId=";
- private static final String URL_COMMAND = URL + "/v1/devices/%s/abilities/%s/command?locationId=%s";
- private static final String URL_PROPERTY = URL + "/v1/devices/%s/abilities/%s/properties/%s?locationId=%s";
- private static final String URL_SETTING = URL + "/v1/devices/%s/settings/%s?locationId=%s";
-
- private Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new DateDeserializer())
- .registerTypeAdapter(PropertyValue.class, new PropertyValueDeserializer()).create();
- private HttpClient httpClient;
+ private Gson gson = new GsonBuilder().registerTypeAdapter(DataItem.class, new DataItemDeserializer()).create();
- private String mowerDuration = DEFAULT_MOWER_DURATION;
- private Session session;
- private GardenaConfig config;
- private String id;
+ private static final String URL_API_HUSQUARNA = "https://api.authentication.husqvarnagroup.dev/v1";
+ private static final String URL_API_GARDENA = "https://api.smart.gardena.dev/v1";
+ private static final String URL_API_TOKEN = URL_API_HUSQUARNA + "/oauth2/token";
+ private static final String URL_API_WEBSOCKET = URL_API_GARDENA + "/websocket";
+ private static final String URL_API_LOCATIONS = URL_API_GARDENA + "/locations";
+ private static final String URL_API_COMMAND = URL_API_GARDENA + "/command";
+ private String id;
+ private GardenaConfig config;
private ScheduledExecutorService scheduler;
- private ScheduledFuture<?> refreshThreadFuture;
- private RefreshDevicesThread refreshDevicesThread = new RefreshDevicesThread();
+ private Map<String, Device> allDevicesById = new HashMap<>();
+ private LocationsResponse locationsResponse;
private GardenaSmartEventListener eventListener;
- private Map<String, Device> allDevicesById = new HashMap<>();
- private Set<Location> allLocations = new HashSet<>();
+ private HttpClient httpClient;
+ private List<GardenaSmartWebSocket> webSockets = new ArrayList<>();
+ private @Nullable PostOAuth2Response token;
+ private boolean initialized = false;
+ private WebSocketFactory webSocketFactory;
- @Override
- public void init(String id, GardenaConfig config, GardenaSmartEventListener eventListener,
- ScheduledExecutorService scheduler) throws GardenaException {
+ private Set<Device> devicesToNotify = ConcurrentHashMap.newKeySet();
+ private @Nullable ScheduledFuture<?> deviceToNotifyFuture;
+ private @Nullable ScheduledFuture<?> newDeviceFuture;
+
+ public GardenaSmartImpl(String id, GardenaConfig config, GardenaSmartEventListener eventListener,
+ ScheduledExecutorService scheduler, HttpClientFactory httpClientFactory, WebSocketFactory webSocketFactory)
+ throws GardenaException {
this.id = id;
this.config = config;
this.eventListener = eventListener;
this.scheduler = scheduler;
+ this.webSocketFactory = webSocketFactory;
- if (!config.isValid()) {
- throw new GardenaException("Invalid config, no email or password specified");
- }
-
- httpClient = new HttpClient(new SslContextFactory(true));
- httpClient.setConnectTimeout(config.getConnectionTimeout() * 1000L);
-
+ logger.debug("Starting GardenaSmart");
try {
+ httpClient = httpClientFactory.createHttpClient(id);
+ httpClient.setConnectTimeout(config.getConnectionTimeout() * 1000L);
+ httpClient.setIdleTimeout(httpClient.getConnectTimeout());
httpClient.start();
- } catch (Exception ex) {
- throw new GardenaException(ex.getMessage(), ex);
- }
- loadAllDevices();
- }
+ // initially load access token
+ verifyToken();
+ locationsResponse = loadLocations();
- @Override
- public void dispose() {
- stopRefreshThread(true);
- if (httpClient != null) {
- try {
- httpClient.stop();
- } catch (Exception e) {
- // ignore
+ // assemble devices
+ for (LocationDataItem location : locationsResponse.data) {
+ LocationResponse locationResponse = loadLocation(location.id);
+ if (locationResponse.included != null) {
+ for (DataItem<?> dataItem : locationResponse.included) {
+ handleDataItem(dataItem);
+ }
+ }
+ }
+
+ for (Device device : allDevicesById.values()) {
+ device.evaluateDeviceType();
}
- httpClient.destroy();
+
+ startWebsockets();
+ initialized = true;
+ } catch (Exception ex) {
+ throw new GardenaException(ex.getMessage(), ex);
}
- allLocations.clear();
- allDevicesById.clear();
}
/**
- * Schedules the device refresh thread.
+ * Starts the websockets for each location.
*/
- private void startRefreshThread() {
- refreshThreadFuture = scheduler.scheduleWithFixedDelay(refreshDevicesThread, 6, config.getRefresh(),
- TimeUnit.SECONDS);
+ private void startWebsockets() throws Exception {
+ for (LocationDataItem location : locationsResponse.data) {
+ WebSocketCreatedResponse webSocketCreatedResponse = getWebsocketInfo(location.id);
+ String socketId = id + "-" + location.attributes.name;
+ webSockets.add(new GardenaSmartWebSocket(this, webSocketCreatedResponse, config, scheduler,
+ webSocketFactory, token, socketId));
+ }
}
/**
- * Stops the device refresh thread.
+ * Stops all websockets.
*/
- private void stopRefreshThread(boolean force) {
- if (refreshThreadFuture != null) {
- refreshThreadFuture.cancel(force);
- }
- }
-
- @Override
- public String getId() {
- return id;
- }
-
- @Override
- public Set<Location> getLocations() {
- return allLocations;
- }
-
- @Override
- public Device getDevice(String deviceId) throws GardenaException {
- Device device = allDevicesById.get(deviceId);
- if (device == null) {
- throw new GardenaDeviceNotFoundException(
- String.format("Device with id '%s' not found on gateway '%s'", deviceId, id));
- }
- return device;
- }
-
- @Override
- public void loadAllDevices() throws GardenaException {
- stopRefreshThread(false);
- try {
- allLocations.clear();
- allDevicesById.clear();
-
- verifySession();
- Locations locations = executeRequest(HttpMethod.GET,
- URL_LOCATIONS + session.getSessionAttributes().getUserId(), null, Locations.class);
-
- for (Location location : locations.getLocations()) {
- allLocations.add(location);
- Devices devices = loadDevices(location);
- for (Device device : devices.getDevices()) {
- if (DEVICE_CATEGORY_GATEWAY.equals(device.getCategory())) {
- location.getDeviceIds().remove(device.getId());
- } else {
- allDevicesById.put(device.getId(), device);
- }
- }
- }
- } finally {
- startRefreshThread();
+ private void stopWebsockets() {
+ for (GardenaSmartWebSocket webSocket : webSockets) {
+ webSocket.stop();
}
+ webSockets.clear();
}
/**
- * Loads all devices for the location, adds virtual properties for commands.
+ * Communicates with Gardena smart home system and parses the result.
*/
- private Devices loadDevices(Location location) throws GardenaException {
- Devices devices = executeRequest(HttpMethod.GET, URL_DEVICES + location.getId(), null, Devices.class);
- for (Device device : devices.getDevices()) {
- device.setLocation(location);
- for (Ability ability : device.getAbilities()) {
- ability.setDevice(device);
- for (Property property : ability.getProperties()) {
- property.setAbility(ability);
-
- if (device.getCategory().equals(DEVICE_CATEGORY_PUMP)) {
- if (property.getName().equals(PROPERTY_MANUAL_WATERING_TIMER)) {
- Integer duration = getIntegerValue(property.getValueAsString());
- if (duration == null) {
- duration = 0;
- }
- property.setValue(new PropertyValue(String.valueOf(duration / 60)));
- }
- }
+ private <T> T executeRequest(HttpMethod method, String url, @Nullable Object content, @Nullable Class<T> result)
+ throws GardenaException {
+ try {
+ AbstractTypedContentProvider contentProvider = null;
+ String contentType = "application/vnd.api+json";
+ if (content != null) {
+ if (content instanceof Fields) {
+ contentProvider = new FormContentProvider((Fields) content);
+ contentType = "application/x-www-form-urlencoded";
+ } else {
+ contentProvider = new StringContentProvider(gson.toJson(content));
}
}
- for (Setting setting : device.getSettings()) {
- setting.setDevice(device);
+
+ if (logger.isTraceEnabled()) {
+ logger.trace(">>> {} {}, data: {}", method, url, content == null ? null : gson.toJson(content));
}
- if (DEVICE_CATEGORY_MOWER.equals(device.getCategory())) {
- Ability mower = device.getAbility(ABILITY_MOWER);
- mower.addProperty(new Property(GardenaSmartCommandName.PARK_UNTIL_NEXT_TIMER, "false"));
- mower.addProperty(new Property(GardenaSmartCommandName.PARK_UNTIL_FURTHER_NOTICE, "false"));
- mower.addProperty(new Property(GardenaSmartCommandName.START_RESUME_SCHEDULE, "false"));
- mower.addProperty(new Property(GardenaSmartCommandName.START_OVERRIDE_TIMER, "false"));
+ Request request = httpClient.newRequest(url).method(method).header(HttpHeader.CONTENT_TYPE, contentType)
+ .header(HttpHeader.ACCEPT, "application/vnd.api+json").header(HttpHeader.ACCEPT_ENCODING, "gzip");
- mower.addProperty(new Property(GardenaSmartCommandName.DURATION_PROPERTY, mowerDuration));
+ if (!URL_API_TOKEN.equals(url)) {
+ verifyToken();
+ final PostOAuth2Response token = this.token;
+ if (token != null) {
+ request.header("Authorization", token.tokenType + " " + token.accessToken);
+ request.header("Authorization-provider", token.provider);
+ }
+ request.header("X-Api-Key", config.getApiKey());
}
- }
- return devices;
- }
- @Override
- public void sendCommand(Device device, GardenaSmartCommandName commandName, Object value) throws GardenaException {
- Ability ability = null;
- Command command = null;
-
- switch (commandName) {
- case PARK_UNTIL_NEXT_TIMER:
- ability = device.getAbility(ABILITY_MOWER);
- command = new MowerParkUntilNextTimerCommand();
- break;
- case PARK_UNTIL_FURTHER_NOTICE:
- ability = device.getAbility(ABILITY_MOWER);
- command = new MowerParkUntilFurtherNoticeCommand();
- break;
- case START_RESUME_SCHEDULE:
- ability = device.getAbility(ABILITY_MOWER);
- command = new MowerStartResumeScheduleCommand();
- break;
- case START_OVERRIDE_TIMER:
- ability = device.getAbility(ABILITY_MOWER);
- command = new MowerStartOverrideTimerCommand(mowerDuration);
- break;
- case DURATION_PROPERTY:
- if (value == null) {
- throw new GardenaException("Command '" + commandName + "' requires a value");
- }
- mowerDuration = ObjectUtils.toString(value);
- return;
- case MEASURE_AMBIENT_TEMPERATURE:
- ability = device.getAbility(ABILITY_AMBIENT_TEMPERATURE);
- command = new SensorMeasureAmbientTemperatureCommand();
- break;
- case MEASURE_LIGHT:
- ability = device.getAbility(ABILITY_LIGHT);
- command = new SensorMeasureLightCommand();
- break;
- case MEASURE_SOIL_HUMIDITY:
- ability = device.getAbility(ABILITY_HUMIDITY);
- command = new SensorMeasureSoilHumidityCommand();
- break;
- case MEASURE_SOIL_TEMPERATURE:
- ability = device.getAbility(ABILITY_SOIL_TEMPERATURE);
- command = new SensorMeasureSoilTemperatureCommand();
- break;
- case OUTLET_MANUAL_OVERRIDE_TIME:
- if (value == null) {
- throw new GardenaException("Command '" + commandName + "' requires a value");
- }
- StringProperty prop = new StringProperty(PROPERTY_BUTTON_MANUAL_OVERRIDE_TIME,
- ObjectUtils.toString(value));
-
- executeSetProperty(device, ABILITY_OUTLET, PROPERTY_BUTTON_MANUAL_OVERRIDE_TIME, prop);
- break;
- case OUTLET_VALVE:
- ability = device.getAbility(ABILITY_OUTLET);
- if (value != null && value == Boolean.TRUE) {
- String wateringDuration = device.getAbility(ABILITY_OUTLET)
- .getProperty(PROPERTY_BUTTON_MANUAL_OVERRIDE_TIME).getValueAsString();
- command = new WateringManualOverrideCommand(wateringDuration);
- } else {
- command = new WateringCancelOverrideCommand();
- }
- break;
- case POWER_TIMER:
- if (value == null) {
- throw new GardenaException("Command '" + commandName + "' requires a value");
- }
- prop = new StringProperty(PROPERTY_POWER_TIMER, ObjectUtils.toString(value));
- executeSetProperty(device, ABILITY_POWER, PROPERTY_POWER_TIMER, prop);
- break;
- case WATERING_TIMER_VALVE_1:
- case WATERING_TIMER_VALVE_2:
- case WATERING_TIMER_VALVE_3:
- case WATERING_TIMER_VALVE_4:
- case WATERING_TIMER_VALVE_5:
- case WATERING_TIMER_VALVE_6:
- if (value == null) {
- throw new GardenaException("Command '" + commandName + "' requires a value");
- } else if (!(value instanceof Integer)) {
- throw new GardenaException("Watering duration value '" + value + "' not a number");
- }
- int valveId = Integer.parseInt(StringUtils.right(commandName.toString(), 1));
- String wateringTimerProperty = PROPERTY_WATERING_TIMER + valveId;
- IrrigationControlWateringProperty irrigationProp = new IrrigationControlWateringProperty(
- wateringTimerProperty, (Integer) value, valveId);
- executeSetProperty(device, ABILITY_WATERING, wateringTimerProperty, irrigationProp);
- break;
- case PUMP_MANUAL_WATERING_TIMER:
- Integer duration = getIntegerValue(value);
- if (duration == null) {
- throw new GardenaException("Command '" + commandName + "' requires a number value");
- }
- prop = new StringProperty(PROPERTY_MANUAL_WATERING_TIMER, String.valueOf(duration * 60));
+ request.content(contentProvider);
+ ContentResponse contentResponse = request.send();
+ int status = contentResponse.getStatus();
+ if (logger.isTraceEnabled()) {
+ logger.trace("<<< status:{}, {}", status, contentResponse.getContentAsString());
+ }
- executeSetProperty(device, ABILITY_MANUAL_WATERING, PROPERTY_MANUAL_WATERING_TIMER, prop);
- break;
- default:
- throw new GardenaException("Unknown command " + commandName);
- }
+ if (status != 200 && status != 204 && status != 201 && status != 202) {
+ throw new GardenaException(String.format("Error %s %s, %s", status, contentResponse.getReason(),
+ contentResponse.getContentAsString()));
+ }
- if (command != null) {
- stopRefreshThread(false);
- executeRequest(HttpMethod.POST, getCommandUrl(device, ability), command, NoResult.class);
- startRefreshThread();
+ if (result == null) {
+ return (T) null;
+ }
+ return (T) gson.fromJson(contentResponse.getContentAsString(), result);
+ } catch (InterruptedException | TimeoutException | ExecutionException ex) {
+ throw new GardenaException(ex.getMessage(), ex);
}
}
- private Integer getIntegerValue(Object value) {
- try {
- return Integer.valueOf(ObjectUtils.toString(value));
- } catch (NumberFormatException ex) {
- return null;
+ /**
+ * Creates or refreshes the access token for the Gardena smart system.
+ */
+ private synchronized void verifyToken() throws GardenaException {
+ Fields fields = new Fields();
+ fields.add("client_id", config.getApiKey());
+
+ PostOAuth2Response token = this.token;
+ if (token == null || token.isRefreshTokenExpired()) {
+ // new token
+ logger.debug("Gardena API login using password, reason: {}",
+ token == null ? "no token available" : "refresh token expired");
+ fields.add("grant_type", "password");
+ fields.add("username", config.getEmail());
+ fields.add("password", config.getPassword());
+ token = executeRequest(HttpMethod.POST, URL_API_TOKEN, fields, PostOAuth2Response.class);
+ token.postProcess();
+ this.token = token;
+ } else if (token.isAccessTokenExpired()) {
+ // refresh token
+ logger.debug("Gardena API login using refreshToken, reason: access token expired");
+ fields.add("grant_type", "refresh_token");
+ fields.add("refresh_token", token.refreshToken);
+ try {
+ PostOAuth2Response tempToken = executeRequest(HttpMethod.POST, URL_API_TOKEN, fields,
+ PostOAuth2Response.class);
+ token.accessToken = tempToken.accessToken;
+ token.expiresIn = tempToken.expiresIn;
+ token.postProcess();
+ this.token = token;
+ } catch (GardenaException ex) {
+ // refresh token issue
+ this.token = null;
+ verifyToken();
+ }
+ } else {
+ logger.debug("Gardena API token valid");
}
+ logger.debug("{}", token.toString());
}
/**
- * Sends the new property value for the ability.
+ * Loads all locations.
*/
- private void executeSetProperty(Device device, String ability, String property, BaseProperty value)
- throws GardenaException {
- String propertyUrl = String.format(URL_PROPERTY, device.getId(), ability, property,
- device.getLocation().getId());
- stopRefreshThread(false);
- executeRequest(HttpMethod.PUT, propertyUrl, new PropertyWrapper(value), NoResult.class);
- device.getAbility(ability).getProperty(property).setValue(new PropertyValue(value.getValue()));
- startRefreshThread();
- }
-
- @Override
- public void sendSetting(Setting setting, Object value) throws GardenaException {
- SettingCommand settingCommand = new SettingCommand(setting.getName());
- settingCommand.setDeviceId(setting.getDevice().getId());
- settingCommand.setValue(value);
-
- stopRefreshThread(false);
- executeRequest(HttpMethod.PUT, getSettingUrl(setting), new SettingCommandWrapper(settingCommand),
- NoResult.class);
- startRefreshThread();
+ private LocationsResponse loadLocations() throws GardenaException {
+ return executeRequest(HttpMethod.GET, URL_API_LOCATIONS, null, LocationsResponse.class);
}
/**
- * Returns the command url.
+ * Loads all devices for a given location.
*/
- private String getCommandUrl(Device device, Ability ability) throws GardenaException {
- return String.format(URL_COMMAND, device.getId(), ability.getName(), device.getLocation().getId());
+ private LocationResponse loadLocation(String locationId) throws GardenaException {
+ return executeRequest(HttpMethod.GET, URL_API_LOCATIONS + "/" + locationId, null, LocationResponse.class);
}
/**
- * Returns the settings url.
+ * Returns the websocket url for a given location.
*/
- private String getSettingUrl(Setting setting) {
- Device device = setting.getDevice();
- return String.format(URL_SETTING, device.getId(), setting.getId(), device.getLocation().getId());
+ private WebSocketCreatedResponse getWebsocketInfo(String locationId) throws GardenaException {
+ return executeRequest(HttpMethod.POST, URL_API_WEBSOCKET, new CreateWebSocketRequest(locationId),
+ WebSocketCreatedResponse.class);
}
/**
- * Communicates with Gardena Smart Home and parses the result.
+ * Stops the client.
*/
- private synchronized <T> T executeRequest(HttpMethod method, String url, Object contentObject, Class<T> result)
- throws GardenaException {
- try {
- if (logger.isTraceEnabled()) {
- logger.trace("{} request: {}", method, url);
- if (contentObject != null) {
- logger.trace("{} data : {}", method, gson.toJson(contentObject));
- }
- }
-
- Request request = httpClient.newRequest(url).method(method)
- .timeout(config.getConnectionTimeout(), TimeUnit.SECONDS)
- .idleTimeout(config.getConnectionTimeout(), TimeUnit.SECONDS)
- .header(HttpHeader.CONTENT_TYPE, "application/json").header(HttpHeader.ACCEPT, "application/json")
- .header(HttpHeader.ACCEPT_ENCODING, "gzip");
-
- if (contentObject != null) {
- StringContentProvider content = new StringContentProvider(gson.toJson(contentObject));
- request.content(content);
- }
-
- if (!result.equals(SessionWrapper.class)) {
- verifySession();
- request.header("authorization", "Bearer " + session.getToken());
- request.header("authorization-provider", session.getSessionAttributes().getProvider());
- }
-
- ContentResponse contentResponse = request.send();
- int status = contentResponse.getStatus();
- if (logger.isTraceEnabled()) {
- logger.trace("Status : {}", status);
- logger.trace("Response: {}", contentResponse.getContentAsString());
- }
-
- if (status == 500) {
- throw new GardenaException(
- gson.fromJson(contentResponse.getContentAsString(), Errors.class).toString());
- } else if (status != 200 && status != 204 && status != 201) {
- throw new GardenaException(String.format("Error %s %s", status, contentResponse.getReason()));
- }
+ public void dispose() {
+ logger.debug("Disposing GardenaSmart");
- if (result == NoResult.class) {
- return null;
- }
+ final ScheduledFuture<?> newDeviceFuture = this.newDeviceFuture;
+ if (newDeviceFuture != null) {
+ newDeviceFuture.cancel(true);
+ }
- return gson.fromJson(contentResponse.getContentAsString(), result);
- } catch (ExecutionException ex) {
- Throwable cause = ex.getCause();
- if (cause instanceof HttpResponseException) {
- HttpResponseException responseException = (HttpResponseException) ex.getCause();
- int status = responseException.getResponse().getStatus();
- if (status == 401) {
- throw new GardenaUnauthorizedException(ex.getCause());
- }
- }
- throw new GardenaException(ex.getMessage(), ex);
- } catch (Exception ex) {
- throw new GardenaException(ex.getMessage(), ex);
+ final ScheduledFuture<?> deviceToNotifyFuture = this.deviceToNotifyFuture;
+ if (deviceToNotifyFuture != null) {
+ deviceToNotifyFuture.cancel(true);
+ }
+ stopWebsockets();
+ try {
+ httpClient.stop();
+ } catch (Exception e) {
+ // ignore
}
+ httpClient.destroy();
+ locationsResponse = new LocationsResponse();
+ allDevicesById.clear();
+ initialized = false;
}
/**
- * Verifies the Gardena Smart Home session and reconnects if necessary.
+ * Restarts all websockets.
*/
- private void verifySession() throws GardenaException {
- if (session == null
- || session.getCreated() + (config.getSessionTimeout() * 60000) <= System.currentTimeMillis()) {
- logger.trace("(Re)logging in to Gardena Smart Home");
- session = executeRequest(HttpMethod.POST, URL_LOGIN, new GardenaConfigWrapper(config), SessionWrapper.class)
- .getSession();
+ @Override
+ public synchronized void restartWebsockets() {
+ logger.debug("Restarting GardenaSmart Webservice");
+ stopWebsockets();
+ try {
+ startWebsockets();
+ } catch (Exception ex) {
+ logger.warn("Restarting GardenaSmart Webservice failed: {}, restarting binding", ex.getMessage());
+ eventListener.onError();
}
}
/**
- * Thread which refreshes the data from Gardena Smart Home.
+ * Sets the dataItem from the websocket event into the correct device.
*/
- private class RefreshDevicesThread implements Runnable {
- private boolean connectionLost = false;
-
- @Override
- public void run() {
- try {
- logger.debug("Refreshing gardena device data");
- final Map<String, Device> newDevicesById = new HashMap<>();
-
- for (Location location : allLocations) {
- Devices devices = loadDevices(location);
- for (Device device : devices.getDevices()) {
- if (DEVICE_CATEGORY_GATEWAY.equals(device.getCategory())) {
- location.getDeviceIds().remove(device.getId());
- } else {
- newDevicesById.put(device.getId(), device);
+ private void handleDataItem(final DataItem<?> dataItem) throws GardenaException {
+ final String deviceId = dataItem.getDeviceId();
+ Device device = allDevicesById.get(deviceId);
+ if (device == null && !(dataItem instanceof LocationDataItem)) {
+ device = new Device(deviceId);
+ allDevicesById.put(device.id, device);
+
+ if (initialized) {
+ newDeviceFuture = scheduler.schedule(() -> {
+ Device newDevice = allDevicesById.get(deviceId);
+ if (newDevice != null) {
+ newDevice.evaluateDeviceType();
+ if (newDevice.deviceType != null) {
+ eventListener.onNewDevice(newDevice);
}
}
- }
-
- if (connectionLost) {
- connectionLost = false;
- logger.info("Connection resumed to Gardena Smart Home with id '{}'", id);
- eventListener.onConnectionResumed();
- }
-
- // determine deleted devices
- Collection<Device> deletedDevices = allDevicesById.values().stream()
- .filter(d -> !newDevicesById.values().contains(d)).collect(Collectors.toSet());
+ }, 3, TimeUnit.SECONDS);
+ }
+ }
- // determine new devices
- Collection<Device> newDevices = newDevicesById.values().stream()
- .filter(d -> !allDevicesById.values().contains(d)).collect(Collectors.toSet());
+ if (device != null) {
+ device.setDataItem(dataItem);
+ }
+ }
- // determine updated devices
- Collection<Device> updatedDevices = allDevicesById.values().stream().distinct()
- .filter(newDevicesById.values()::contains).collect(Collectors.toSet());
+ @Override
+ public void onWebSocketClose() {
+ restartWebsockets();
+ }
- allDevicesById = newDevicesById;
+ @Override
+ public void onWebSocketError() {
+ eventListener.onError();
+ }
- for (Device deletedDevice : deletedDevices) {
- eventListener.onDeviceDeleted(deletedDevice);
+ @Override
+ public void onWebSocketMessage(String msg) {
+ try {
+ DataItem<?> dataItem = gson.fromJson(msg, DataItem.class);
+ if (dataItem != null) {
+ handleDataItem(dataItem);
+ Device device = allDevicesById.get(dataItem.getDeviceId());
+ if (device != null && device.active) {
+ devicesToNotify.add(device);
+
+ // delay the deviceUpdated event to filter multiple events for the same device dataItem property
+ if (deviceToNotifyFuture == null) {
+ deviceToNotifyFuture = scheduler.schedule(() -> {
+ deviceToNotifyFuture = null;
+ Iterator<Device> notifyIterator = devicesToNotify.iterator();
+ while (notifyIterator.hasNext()) {
+ eventListener.onDeviceUpdated(notifyIterator.next());
+ notifyIterator.remove();
+ }
+ }, 1, TimeUnit.SECONDS);
+ }
}
+ }
+ } catch (GardenaException | JsonSyntaxException ex) {
+ logger.warn("Ignoring message: {}", ex.getMessage());
+ }
+ }
- for (Device newDevice : newDevices) {
- eventListener.onNewDevice(newDevice);
- }
+ @Override
+ public Device getDevice(String deviceId) throws GardenaDeviceNotFoundException {
+ Device device = allDevicesById.get(deviceId);
+ if (device == null) {
+ throw new GardenaDeviceNotFoundException("Device with id " + deviceId + " not found");
+ }
+ return device;
+ }
- for (Device updatedDevice : updatedDevices) {
- eventListener.onDeviceUpdated(updatedDevice);
- }
+ @Override
+ public void sendCommand(DataItem<?> dataItem, GardenaCommand gardenaCommand) throws GardenaException {
+ executeRequest(HttpMethod.PUT, URL_API_COMMAND + "/" + dataItem.id, new GardenaCommandRequest(gardenaCommand),
+ null);
+ }
- } catch (GardenaException ex) {
- if (!connectionLost) {
- connectionLost = true;
- logger.warn("Connection lost to Gardena Smart Home with id '{}'", id);
- logger.trace("{}", ex.getMessage(), ex);
- eventListener.onConnectionLost();
- }
- }
- }
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public Collection<Device> getAllDevices() {
+ return allDevicesById.values();
}
}
--- /dev/null
+/**
+ * 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.gardena.internal;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.time.Instant;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.annotations.*;
+import org.eclipse.jetty.websocket.api.extensions.Frame;
+import org.eclipse.jetty.websocket.client.WebSocketClient;
+import org.eclipse.jetty.websocket.common.WebSocketSession;
+import org.eclipse.jetty.websocket.common.frames.PongFrame;
+import org.openhab.binding.gardena.internal.config.GardenaConfig;
+import org.openhab.binding.gardena.internal.model.dto.api.PostOAuth2Response;
+import org.openhab.binding.gardena.internal.model.dto.api.WebSocketCreatedResponse;
+import org.openhab.core.io.net.http.WebSocketFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link GardenaSmartWebSocket} implements the websocket for receiving constant updates from the Gardena smart
+ * system.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+@NonNullByDefault
+@WebSocket
+public class GardenaSmartWebSocket {
+ private final Logger logger = LoggerFactory.getLogger(GardenaSmartWebSocket.class);
+ private final GardenaSmartWebSocketListener socketEventListener;
+ private final long WEBSOCKET_IDLE_TIMEOUT = 300;
+
+ private WebSocketSession session;
+ private WebSocketClient webSocketClient;
+ private boolean closing;
+ private Instant lastPong = Instant.now();
+ private ScheduledExecutorService scheduler;
+ private @Nullable ScheduledFuture<?> connectionTracker;
+ private ByteBuffer pingPayload = ByteBuffer.wrap("ping".getBytes(StandardCharsets.UTF_8));
+ private @Nullable PostOAuth2Response token;
+ private String socketId;
+
+ /**
+ * Starts the websocket session.
+ */
+ public GardenaSmartWebSocket(GardenaSmartWebSocketListener socketEventListener,
+ WebSocketCreatedResponse webSocketCreatedResponse, GardenaConfig config, ScheduledExecutorService scheduler,
+ WebSocketFactory webSocketFactory, @Nullable PostOAuth2Response token, String socketId) throws Exception {
+ this.socketEventListener = socketEventListener;
+ this.scheduler = scheduler;
+ this.token = token;
+ this.socketId = socketId;
+
+ webSocketClient = webSocketFactory.createWebSocketClient(socketId);
+ webSocketClient.setConnectTimeout(config.getConnectionTimeout() * 1000L);
+ webSocketClient.setStopTimeout(3000);
+ webSocketClient.setMaxIdleTimeout(150000);
+ webSocketClient.start();
+
+ logger.debug("Connecting to Gardena Webservice ({})", socketId);
+ session = (WebSocketSession) webSocketClient
+ .connect(this, new URI(webSocketCreatedResponse.data.attributes.url)).get();
+ session.setStopTimeout(3000);
+ }
+
+ /**
+ * Stops the websocket session.
+ */
+ public synchronized void stop() {
+ closing = true;
+ final ScheduledFuture<?> connectionTracker = this.connectionTracker;
+ if (connectionTracker != null) {
+ connectionTracker.cancel(true);
+ }
+ if (isRunning()) {
+ logger.debug("Closing Gardena Webservice client ({})", socketId);
+ try {
+ session.close();
+ } catch (Exception ex) {
+ // ignore
+ } finally {
+ try {
+ webSocketClient.stop();
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns true, if the websocket is running.
+ */
+ public synchronized boolean isRunning() {
+ return session.isOpen();
+ }
+
+ @OnWebSocketConnect
+ public void onConnect(Session session) {
+ closing = false;
+ logger.debug("Connected to Gardena Webservice ({})", socketId);
+
+ connectionTracker = scheduler.scheduleWithFixedDelay(this::sendKeepAlivePing, 2, 2, TimeUnit.MINUTES);
+ }
+
+ @OnWebSocketFrame
+ public void onFrame(Frame pong) {
+ if (pong instanceof PongFrame) {
+ lastPong = Instant.now();
+ logger.trace("Pong received ({})", socketId);
+ }
+ }
+
+ @OnWebSocketClose
+ public void onClose(int statusCode, String reason) {
+ if (!closing) {
+ logger.debug("Connection to Gardena Webservice was closed ({}): code: {}, reason: {}", socketId, statusCode,
+ reason);
+ socketEventListener.onWebSocketClose();
+ }
+ }
+
+ @OnWebSocketError
+ public void onError(Throwable cause) {
+ if (!closing) {
+ logger.warn("Gardena Webservice error ({}): {}, restarting", socketId, cause.getMessage());
+ logger.debug("{}", cause.getMessage(), cause);
+ socketEventListener.onWebSocketError();
+ }
+ }
+
+ @OnWebSocketMessage
+ public void onMessage(String msg) {
+ if (!closing) {
+ logger.trace("<<< event ({}): {}", socketId, msg);
+ socketEventListener.onWebSocketMessage(msg);
+ }
+ }
+
+ /**
+ * Sends a ping to tell the Gardena smart system that the client is alive.
+ */
+ private void sendKeepAlivePing() {
+ try {
+ logger.trace("Sending ping ({})", socketId);
+ session.getRemote().sendPing(pingPayload);
+ final PostOAuth2Response accessToken = token;
+ if ((Instant.now().getEpochSecond() - lastPong.getEpochSecond() > WEBSOCKET_IDLE_TIMEOUT)
+ || accessToken == null || accessToken.isAccessTokenExpired()) {
+ session.close(1000, "Timeout manually closing dead connection (" + socketId + ")");
+ }
+ } catch (IOException ex) {
+ logger.debug("{}", ex.getMessage());
+ }
+ }
+}
--- /dev/null
+/**
+ * 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.gardena.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link GardenaSmartWebSocketListener} is called by the {@link GardenaSmartWebSocket} on new Events and if the
+ * {@link GardenaSmartWebSocket}
+ * closed the connection.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+@NonNullByDefault
+public interface GardenaSmartWebSocketListener {
+ /**
+ * This method is called, when the evenRunner stops abnormally (statuscode <> 1000).
+ */
+ void onWebSocketClose();
+
+ /**
+ * This method is called when the Gardena websocket services throws an onError.
+ */
+ void onWebSocketError();
+
+ /**
+ * This method is called, whenever a new event comes from the Gardena service.
+ *
+ * @param msg
+ */
+ void onWebSocketMessage(String msg);
+}
*/
package org.openhab.binding.gardena.internal.config;
-import org.apache.commons.lang.StringUtils;
-import org.apache.commons.lang.builder.ToStringBuilder;
-import org.apache.commons.lang.builder.ToStringStyle;
-
-import com.google.gson.annotations.SerializedName;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
/**
* The main Gardena config class.
*
* @author Gerhard Riegler - Initial contribution
*/
+@NonNullByDefault
public class GardenaConfig {
-
- private static final Integer DEFAULT_SESSION_TIMEOUT = 30;
private static final Integer DEFAULT_CONNECTION_TIMEOUT = 10;
- private static final Integer DEFAULT_REFRESH = 60;
- @SerializedName("username")
- private String email;
- private String password;
+ private @Nullable String email;
+ private @Nullable String password;
+ private @Nullable String apiKey;
- private transient Integer sessionTimeout = DEFAULT_SESSION_TIMEOUT;
private transient Integer connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
- private transient Integer refresh = DEFAULT_REFRESH;
public GardenaConfig() {
}
}
/**
- * Returns the email to connect to Gardena Smart Home.
+ * Returns the email to connect to Gardena smart system.
*/
- public String getEmail() {
+ public @Nullable String getEmail() {
return email;
}
/**
- * Sets the email to connect to Gardena Smart Home.
+ * Sets the email to connect to Gardena smart system.
*/
public void setEmail(String email) {
this.email = email;
}
/**
- * Returns the password to connect to Gardena Smart Home.
+ * Returns the password to connect to Gardena smart system.
*/
- public String getPassword() {
+ public @Nullable String getPassword() {
return password;
}
/**
- * Sets the password to connect to Gardena Smart Home.
+ * Sets the password to connect to Gardena smart system.
*/
public void setPassword(String password) {
this.password = password;
}
/**
- * Returns the session timeout to Gardena Smart Home.
- */
- public int getSessionTimeout() {
- return sessionTimeout;
- }
-
- /**
- * Sets the session timeout to Gardena Smart Home.
- */
- public void setSessionTimeout(int sessionTimeout) {
- this.sessionTimeout = sessionTimeout;
- }
-
- /**
- * Returns the connection timeout to Gardena Smart Home.
+ * Returns the connection timeout to Gardena smart system.
*/
public Integer getConnectionTimeout() {
return connectionTimeout;
}
/**
- * Sets the connection timeout to Gardena Smart Home.
+ * Sets the connection timeout to Gardena smart system.
*/
public void setConnectionTimeout(Integer connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
/**
- * Returns the refresh interval to fetch new data from Gardena Smart Home.
+ * Returns the api key.
*/
- public Integer getRefresh() {
- return refresh;
+ public @Nullable String getApiKey() {
+ return apiKey;
}
/**
- * Returns the refresh interval to fetch new data from Gardena Smart Home.
+ * Sets the api key.
*/
- public void setRefresh(Integer refresh) {
- this.refresh = refresh;
+ public void setApiKey(String apiKey) {
+ this.apiKey = apiKey;
}
/**
- * Validate the config, if at least email and password is specified.
+ * Validate the config if email, password and apiKey is specified.
*/
public boolean isValid() {
- return StringUtils.isNotBlank(email) && StringUtils.isNotBlank(password);
+ final String email = this.email;
+ final String password = this.password;
+ final String apiKey = this.apiKey;
+ return email != null && !email.isBlank() && password != null && !password.isBlank() && apiKey != null
+ && !apiKey.isBlank();
}
@Override
public String toString() {
- return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("email", email)
- .append("password", StringUtils.isBlank(password) ? "" : StringUtils.repeat("*", password.length()))
- .append("sessionTimeout", sessionTimeout).append("connectionTimeout", connectionTimeout)
- .append("refresh", refresh).toString();
+ StringBuilder sb = new StringBuilder(GardenaConfig.class.getSimpleName()).append("[");
+ sb.append("email: ").append(email).append(", ");
+ sb.append("connectionTimeout: ").append(connectionTimeout).append(", ");
+ sb.append("apiKey: ").append(apiKey);
+ return sb.append("]").toString();
}
}
+++ /dev/null
-/**
- * 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.gardena.internal.config;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * GardenaConfgData wrapper for valid Gardena JSON serialization.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-
-public class GardenaConfigDataWrapper {
- @SerializedName("attributes")
- private GardenaConfig config;
-
- @SerializedName("type")
- private String typeToken = "token";
-
- public GardenaConfigDataWrapper() {
- }
-
- public GardenaConfigDataWrapper(GardenaConfig config) {
- this.config = config;
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.config;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * GardenaConfg wrapper for valid Gardena JSON serialization.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class GardenaConfigWrapper {
- @SerializedName("data")
- private GardenaConfigDataWrapper dataWrapper;
-
- public GardenaConfigWrapper() {
- }
-
- public GardenaConfigWrapper(GardenaConfig config) {
- this.dataWrapper = new GardenaConfigDataWrapper(config);
- }
-}
import org.openhab.binding.gardena.internal.GardenaSmart;
import org.openhab.binding.gardena.internal.exception.GardenaException;
import org.openhab.binding.gardena.internal.handler.GardenaAccountHandler;
-import org.openhab.binding.gardena.internal.model.Device;
-import org.openhab.binding.gardena.internal.model.Location;
+import org.openhab.binding.gardena.internal.model.dto.Device;
+import org.openhab.binding.gardena.internal.util.PropertyUtils;
import org.openhab.binding.gardena.internal.util.UidUtils;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.slf4j.LoggerFactory;
/**
- * The {@link GardenaDeviceDiscoveryService} is used to discover devices that are connected to Gardena Smart Home.
+ * The {@link GardenaDeviceDiscoveryService} is used to discover devices that are connected to Gardena smart system.
*
* @author Gerhard Riegler - Initial contribution
*/
+@NonNullByDefault
public class GardenaDeviceDiscoveryService extends AbstractDiscoveryService
implements DiscoveryService, ThingHandlerService {
private final Logger logger = LoggerFactory.getLogger(GardenaDeviceDiscoveryService.class);
- private static final int DISCOVER_TIMEOUT_SECONDS = 30;
+ private static final int DISCOVER_TIMEOUT_SECONDS = 5;
private @NonNullByDefault({}) GardenaAccountHandler accountHandler;
- private Future<?> scanFuture;
+ private @Nullable Future<?> scanFuture;
public GardenaDeviceDiscoveryService() {
super(Collections.unmodifiableSet(Stream.of(new ThingTypeUID(BINDING_ID, "-")).collect(Collectors.toSet())),
@Override
public void stopScan() {
logger.debug("Stopping Gardena discovery scan");
+ final Future<?> scanFuture = this.scanFuture;
if (scanFuture != null) {
scanFuture.cancel(true);
}
}
/**
- * Starts a thread which loads all Gardena devices registered in the account
+ * Starts a thread which loads all Gardena devices registered in the account.
*/
- public void loadDevices() {
+ private void loadDevices() {
if (scanFuture == null) {
scanFuture = scheduler.submit(() -> {
- try {
- GardenaSmart gardena = accountHandler.getGardenaSmart();
- gardena.loadAllDevices();
- for (Location location : gardena.getLocations()) {
- for (String deviceId : location.getDeviceIds()) {
- deviceDiscovered(gardena.getDevice(deviceId));
- }
+ GardenaSmart gardena = accountHandler.getGardenaSmart();
+ if (gardena != null) {
+ for (Device device : gardena.getAllDevices()) {
+ deviceDiscovered(device);
}
for (Thing thing : accountHandler.getThing().getThings()) {
}
}
- logger.debug("Finished Gardena device discovery scan on gateway '{}'",
- accountHandler.getGardenaSmart().getId());
- } catch (GardenaException ex) {
- logger.error("{}", ex.getMessage(), ex);
- } finally {
+ logger.debug("Finished Gardena device discovery scan on gateway '{}'", gardena.getId());
scanFuture = null;
removeOlderResults(getTimestampOfLastScan());
}
* Waits for the discovery scan to finish and then returns.
*/
public void waitForScanFinishing() {
+ final Future<?> scanFuture = this.scanFuture;
if (scanFuture != null) {
logger.debug("Waiting for finishing Gardena device discovery scan");
try {
* Generates the DiscoveryResult from a Gardena device.
*/
public void deviceDiscovered(Device device) {
- ThingUID accountUID = accountHandler.getThing().getUID();
- ThingUID thingUID = UidUtils.generateThingUID(device, accountHandler.getThing());
-
- DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(accountUID)
- .withLabel(device.getName()).build();
- thingDiscovered(discoveryResult);
- }
+ if (device.active) {
+ ThingUID accountUID = accountHandler.getThing().getUID();
+ ThingUID thingUID = UidUtils.generateThingUID(device, accountHandler.getThing());
- /**
- * Removes the Gardena device.
- */
- public void deviceRemoved(Device device) {
- ThingUID thingUID = UidUtils.generateThingUID(device, accountHandler.getThing());
- thingRemoved(thingUID);
+ try {
+ DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(accountUID)
+ .withLabel(PropertyUtils.getPropertyValue(device, "common.attributes.name.value", String.class))
+ .withProperty("id", device.id).withProperty("type", device.deviceType)
+ .withRepresentationProperty("id").build();
+ thingDiscovered(discoveryResult);
+ } catch (GardenaException ex) {
+ logger.warn("{}", ex.getMessage());
+ }
+ }
}
}
*/
package org.openhab.binding.gardena.internal.exception;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
/**
- * Exception if a device is not found, this happens if a device is requested and the data from Gardena Smart Home has
+ * Exception if a device is not found, this happens if a device is requested and the data from Gardena smart system has
* not been loaded.
*
* @author Gerhard Riegler - Initial contribution
*/
+@NonNullByDefault
public class GardenaDeviceNotFoundException extends GardenaException {
private static final long serialVersionUID = 2704767320916725490L;
import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
/**
- * Exception if something happens in the communication to Gardena Smart Home.
+ * Exception if something happens in the communication to Gardena smart system.
*
* @author Gerhard Riegler - Initial contribution
*/
+@NonNullByDefault
public class GardenaException extends IOException {
private static final long serialVersionUID = 8568935118878542270L;
super(ex);
}
- public GardenaException(String message, Throwable cause) {
+ public GardenaException(@Nullable String message, Throwable cause) {
super(message, cause);
}
}
+++ /dev/null
-/**
- * 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.gardena.internal.exception;
-
-/**
- * Exception for invalid user and password.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class GardenaUnauthorizedException extends GardenaException {
-
- private static final long serialVersionUID = 4343137351443555679L;
-
- public GardenaUnauthorizedException(Throwable ex) {
- super(ex);
- }
-}
*/
package org.openhab.binding.gardena.internal.handler;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
/**
* Exception if the AccountHandler is not available.
*
* @author Gerhard Riegler - Initial contribution
*/
+@NonNullByDefault
public class AccountHandlerNotAvailableException extends Exception {
private static final long serialVersionUID = -1895774551653276530L;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.gardena.internal.GardenaSmart;
import org.openhab.binding.gardena.internal.GardenaSmartEventListener;
import org.openhab.binding.gardena.internal.GardenaSmartImpl;
import org.openhab.binding.gardena.internal.config.GardenaConfig;
import org.openhab.binding.gardena.internal.discovery.GardenaDeviceDiscoveryService;
import org.openhab.binding.gardena.internal.exception.GardenaException;
-import org.openhab.binding.gardena.internal.model.Device;
+import org.openhab.binding.gardena.internal.model.dto.Device;
import org.openhab.binding.gardena.internal.util.UidUtils;
-import org.openhab.core.thing.Bridge;
-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.ThingStatusDetail;
-import org.openhab.core.thing.ThingUID;
+import org.openhab.core.io.net.http.HttpClientFactory;
+import org.openhab.core.io.net.http.WebSocketFactory;
+import org.openhab.core.thing.*;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command;
import org.slf4j.LoggerFactory;
/**
- * The {@link GardenaAccountHandler} is the handler for a Gardena Smart Home access and connects it to the framework.
+ * The {@link GardenaAccountHandler} is the handler for a Gardena smart system access and connects it to the framework.
*
* @author Gerhard Riegler - Initial contribution
*/
+@NonNullByDefault
public class GardenaAccountHandler extends BaseBridgeHandler implements GardenaSmartEventListener {
-
private final Logger logger = LoggerFactory.getLogger(GardenaAccountHandler.class);
- private static final long REINITIALIZE_DELAY_SECONDS = 10;
+ private final long REINITIALIZE_DELAY_SECONDS = 10;
- private GardenaDeviceDiscoveryService discoveryService;
+ private @Nullable GardenaDeviceDiscoveryService discoveryService;
- private GardenaSmart gardenaSmart = new GardenaSmartImpl();
- private GardenaConfig gardenaConfig;
+ private @Nullable GardenaSmart gardenaSmart;
+ private HttpClientFactory httpClientFactory;
+ private WebSocketFactory webSocketFactory;
- public GardenaAccountHandler(Bridge bridge) {
+ public GardenaAccountHandler(Bridge bridge, HttpClientFactory httpClientFactory,
+ WebSocketFactory webSocketFactory) {
super(bridge);
+ this.httpClientFactory = httpClientFactory;
+ this.webSocketFactory = webSocketFactory;
}
@Override
public void initialize() {
logger.debug("Initializing Gardena account '{}'", getThing().getUID().getId());
-
- gardenaConfig = getThing().getConfiguration().as(GardenaConfig.class);
- logger.debug("{}", gardenaConfig);
-
initializeGardena();
}
final GardenaAccountHandler instance = this;
scheduler.execute(() -> {
try {
+ GardenaConfig gardenaConfig = getThing().getConfiguration().as(GardenaConfig.class);
+ logger.debug("{}", gardenaConfig);
+
String id = getThing().getUID().getId();
- gardenaSmart.init(id, gardenaConfig, instance, scheduler);
- discoveryService.startScan(null);
- discoveryService.waitForScanFinishing();
+ gardenaSmart = new GardenaSmartImpl(id, gardenaConfig, instance, scheduler, httpClientFactory,
+ webSocketFactory);
+ final GardenaDeviceDiscoveryService discoveryService = this.discoveryService;
+ if (discoveryService != null) {
+ discoveryService.startScan(null);
+ discoveryService.waitForScanFinishing();
+ }
updateStatus(ThingStatus.ONLINE);
} catch (GardenaException ex) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, ex.getMessage());
disposeGardena();
scheduleReinitialize();
- logger.debug("{}", ex.getMessage(), ex);
+ logger.warn("{}", ex.getMessage());
}
});
}
/**
- * Schedules a reinitialization, if Gardea Smart Home account is not reachable at startup.
+ * Schedules a reinitialization, if Gardena smart system account is not reachable.
*/
private void scheduleReinitialize() {
scheduler.schedule(() -> {
- initializeGardena();
+ if (getThing().getStatus() != ThingStatus.UNINITIALIZED) {
+ initializeGardena();
+ }
}, REINITIALIZE_DELAY_SECONDS, TimeUnit.SECONDS);
}
*/
private void disposeGardena() {
logger.debug("Disposing Gardena account '{}'", getThing().getUID().getId());
-
- discoveryService.stopScan();
-
- gardenaSmart.dispose();
+ final GardenaDeviceDiscoveryService discoveryService = this.discoveryService;
+ if (discoveryService != null) {
+ discoveryService.stopScan();
+ }
+ final GardenaSmart gardenaSmart = this.gardenaSmart;
+ if (gardenaSmart != null) {
+ gardenaSmart.dispose();
+ }
}
/**
- * Returns the Gardena Smart Home implementation.
+ * Returns the Gardena smart system implementation.
*/
- public GardenaSmart getGardenaSmart() {
+ public @Nullable GardenaSmart getGardenaSmart() {
return gardenaSmart;
}
@Override
public void onDeviceUpdated(Device device) {
for (ThingUID thingUID : UidUtils.getThingUIDs(device, getThing())) {
- Thing gardenaThing = getThing().getThing(thingUID);
- try {
- GardenaThingHandler gardenaThingHandler = (GardenaThingHandler) gardenaThing.getHandler();
- gardenaThingHandler.updateProperties(device);
- for (Channel channel : gardenaThing.getChannels()) {
- gardenaThingHandler.updateChannel(channel.getUID());
+ final Thing gardenaThing;
+ final GardenaThingHandler gardenaThingHandler;
+ if ((gardenaThing = getThing().getThing(thingUID)) != null
+ && (gardenaThingHandler = (GardenaThingHandler) gardenaThing.getHandler()) != null) {
+ try {
+ gardenaThingHandler.updateProperties(device);
+ for (Channel channel : gardenaThing.getChannels()) {
+ gardenaThingHandler.updateChannel(channel.getUID());
+ }
+ gardenaThingHandler.updateStatus(device);
+ } catch (GardenaException ex) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, ex.getMessage());
+ } catch (AccountHandlerNotAvailableException ignore) {
}
- gardenaThingHandler.updateSettings(device);
- gardenaThingHandler.updateStatus(device);
- } catch (GardenaException ex) {
- logger.error("There is something wrong with your thing '{}', please check or recreate it: {}",
- gardenaThing.getUID(), ex.getMessage());
- logger.debug("Gardena exception caught on device update.", ex);
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, ex.getMessage());
- } catch (AccountHandlerNotAvailableException ignore) {
}
}
}
@Override
public void onNewDevice(Device device) {
+ final GardenaDeviceDiscoveryService discoveryService = this.discoveryService;
if (discoveryService != null) {
discoveryService.deviceDiscovered(device);
}
}
@Override
- public void onDeviceDeleted(Device device) {
- if (discoveryService != null) {
- discoveryService.deviceRemoved(device);
- }
- }
-
- @Override
- public void onConnectionLost() {
+ public void onError() {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Connection lost");
- }
-
- @Override
- public void onConnectionResumed() {
- updateStatus(ThingStatus.ONLINE);
+ disposeGardena();
+ scheduleReinitialize();
}
}
*/
package org.openhab.binding.gardena.internal.handler;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
/**
* The {@link GardenaDeviceConfig} class represents the configuration for a device connected to an Gardena account.
*
* @author Gerhard Riegler - Initial contribution
*/
-
+@NonNullByDefault
public class GardenaDeviceConfig {
- public String deviceId;
+ public @Nullable String deviceId;
}
*/
package org.openhab.binding.gardena.internal.handler;
-import static org.openhab.binding.gardena.internal.GardenaBindingConstants.*;
+import static org.openhab.binding.gardena.internal.GardenaBindingConstants.BINDING_ID;
+import static org.openhab.binding.gardena.internal.GardenaBindingConstants.THING_TYPE_ACCOUNT;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.io.net.http.HttpClientFactory;
+import org.openhab.core.io.net.http.WebSocketFactory;
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.Activate;
import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
/**
* The {@link GardenaHandlerFactory} is responsible for creating Gardena things and thing handlers.
*
* @author Gerhard Riegler - Initial contribution
*/
+@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.gardena")
public class GardenaHandlerFactory extends BaseThingHandlerFactory {
+ private HttpClientFactory httpClientFactory;
+ private WebSocketFactory webSocketFactory;
+ private TimeZoneProvider timeZoneProvider;
+
+ @Activate
+ public GardenaHandlerFactory(final @Reference HttpClientFactory httpClientFactory,
+ final @Reference WebSocketFactory webSocketFactory, final @Reference TimeZoneProvider timeZoneProvider) {
+ this.httpClientFactory = httpClientFactory;
+ this.webSocketFactory = webSocketFactory;
+ this.timeZoneProvider = timeZoneProvider;
+ }
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
}
@Override
- protected ThingHandler createHandler(Thing thing) {
+ protected @Nullable ThingHandler createHandler(Thing thing) {
if (THING_TYPE_ACCOUNT.equals(thing.getThingTypeUID())) {
- return new GardenaAccountHandler((Bridge) thing);
+ return new GardenaAccountHandler((Bridge) thing, httpClientFactory, webSocketFactory);
} else {
- return new GardenaThingHandler(thing);
+ return new GardenaThingHandler(thing, timeZoneProvider);
}
}
}
package org.openhab.binding.gardena.internal.handler;
import static org.openhab.binding.gardena.internal.GardenaBindingConstants.*;
-import static org.openhab.binding.gardena.internal.GardenaSmartCommandName.*;
+import static org.openhab.binding.gardena.internal.model.dto.command.MowerCommand.MowerControl;
+import static org.openhab.binding.gardena.internal.model.dto.command.PowerSocketCommand.PowerSocketControl;
+import static org.openhab.binding.gardena.internal.model.dto.command.ValveCommand.ValveControl;
+import static org.openhab.binding.gardena.internal.model.dto.command.ValveSetCommand.ValveSetControl;
-import java.time.ZoneId;
import java.time.ZonedDateTime;
-import java.util.Calendar;
+import java.util.Date;
import java.util.Map;
-import java.util.Map.Entry;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
-import org.apache.commons.lang.ObjectUtils;
-import org.apache.commons.lang.StringUtils;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.gardena.internal.GardenaSmart;
-import org.openhab.binding.gardena.internal.GardenaSmartCommandName;
-import org.openhab.binding.gardena.internal.GardenaSmartImpl;
+import org.openhab.binding.gardena.internal.GardenaSmartEventListener;
import org.openhab.binding.gardena.internal.exception.GardenaDeviceNotFoundException;
import org.openhab.binding.gardena.internal.exception.GardenaException;
-import org.openhab.binding.gardena.internal.model.Ability;
-import org.openhab.binding.gardena.internal.model.Device;
-import org.openhab.binding.gardena.internal.model.Setting;
-import org.openhab.binding.gardena.internal.util.DateUtils;
+import org.openhab.binding.gardena.internal.model.dto.Device;
+import org.openhab.binding.gardena.internal.model.dto.api.DataItem;
+import org.openhab.binding.gardena.internal.model.dto.command.*;
+import org.openhab.binding.gardena.internal.util.PropertyUtils;
+import org.openhab.binding.gardena.internal.util.StringUtils;
import org.openhab.binding.gardena.internal.util.UidUtils;
-import org.openhab.core.config.core.Configuration;
-import org.openhab.core.config.core.validation.ConfigValidationException;
-import org.openhab.core.library.types.DateTimeType;
-import org.openhab.core.library.types.DecimalType;
-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.ThingStatusDetail;
+import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.library.types.*;
+import org.openhab.core.thing.*;
import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
-import org.openhab.core.types.Type;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
*
* @author Gerhard Riegler - Initial contribution
*/
+@NonNullByDefault
public class GardenaThingHandler extends BaseThingHandler {
-
private final Logger logger = LoggerFactory.getLogger(GardenaThingHandler.class);
- private final Calendar VALID_DATE_START = DateUtils.parseToCalendar("1970-01-02T00:00Z");
+ private TimeZoneProvider timeZoneProvider;
+ private @Nullable ScheduledFuture<?> commandResetFuture;
- public GardenaThingHandler(Thing thing) {
+ public GardenaThingHandler(Thing thing, TimeZoneProvider timeZoneProvider) {
super(thing);
+ this.timeZoneProvider = timeZoneProvider;
}
@Override
try {
Device device = getDevice();
updateProperties(device);
- updateSettings(device);
updateStatus(device);
} catch (GardenaException ex) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, ex.getMessage());
}
}
- /**
- * Updates the thing configuration from the Gardena device.
- */
- protected void updateSettings(Device device) throws GardenaException {
- if (GardenaSmartImpl.DEVICE_CATEGORY_PUMP.equals(device.getCategory())) {
- Configuration config = editConfiguration();
-
- if (!equalsSetting(config, device, SETTING_LEAKAGE_DETECTION)
- || !equalsSetting(config, device, SETTING_OPERATION_MODE)
- || !equalsSetting(config, device, SETTING_TURN_ON_PRESSURE)) {
- config.put(SETTING_LEAKAGE_DETECTION, device.getSetting(SETTING_LEAKAGE_DETECTION).getValue());
- config.put(SETTING_OPERATION_MODE, device.getSetting(SETTING_OPERATION_MODE).getValue());
- config.put(SETTING_TURN_ON_PRESSURE,
- ObjectUtils.toString(device.getSetting(SETTING_TURN_ON_PRESSURE).getValue()));
- updateConfiguration(config);
- }
+ @Override
+ public void dispose() {
+ final ScheduledFuture<?> commandResetFuture = this.commandResetFuture;
+ if (commandResetFuture != null) {
+ commandResetFuture.cancel(true);
}
- }
-
- private boolean equalsSetting(Configuration config, Device device, String key) throws GardenaException {
- return config.get(key) != null
- && config.get(key).equals(ObjectUtils.toString(device.getSetting(key).getValue()));
+ super.dispose();
}
/**
*/
protected void updateProperties(Device device) throws GardenaException {
Map<String, String> properties = editProperties();
- Ability deviceInfo = device.getAbility(ABILITY_DEVICE_INFO);
- setProperty(properties, deviceInfo, PROPERTY_MANUFACTURER);
- setProperty(properties, deviceInfo, PROPERTY_PRODUCT);
- setProperty(properties, deviceInfo, PROPERTY_SERIALNUMBER);
- setProperty(properties, deviceInfo, PROPERTY_SGTIN);
- setProperty(properties, deviceInfo, PROPERTY_VERSION);
- setProperty(properties, deviceInfo, PROPERTY_CATEGORY);
- updateProperties(properties);
- }
-
- private void setProperty(Map<String, String> properties, Ability deviceInfo, String propertyName) {
- try {
- properties.put(propertyName, deviceInfo.getProperty(propertyName).getValueAsString());
- } catch (GardenaException ex) {
- logger.debug("Ignoring missing device property {}", propertyName);
+ String serial = PropertyUtils.getPropertyValue(device, "common.attributes.serial.value", String.class);
+ if (serial != null) {
+ properties.put(PROPERTY_SERIALNUMBER, serial);
+ }
+ String modelType = PropertyUtils.getPropertyValue(device, "common.attributes.modelType.value", String.class);
+ if (modelType != null) {
+ properties.put(PROPERTY_MODELTYPE, modelType);
}
+ updateProperties(properties);
}
@Override
* Updates the channel from the Gardena device.
*/
protected void updateChannel(ChannelUID channelUID) throws GardenaException, AccountHandlerNotAvailableException {
- Device device = getDevice();
- State state = convertToState(device, channelUID);
- if (state != null) {
- updateState(channelUID, state);
+ String groupId = channelUID.getGroupId();
+ if (groupId != null) {
+ boolean isCommand = groupId.endsWith("_commands");
+ if (!isCommand || (isCommand && isLocalDurationCommand(channelUID))) {
+ Device device = getDevice();
+ State state = convertToState(device, channelUID);
+ if (state != null) {
+ updateState(channelUID, state);
+ }
+ }
}
}
/**
* Converts a Gardena property value to a openHAB state.
*/
- private State convertToState(Device device, ChannelUID channelUID) throws GardenaException {
- String abilityName = channelUID.getGroupId();
+ private @Nullable State convertToState(Device device, ChannelUID channelUID) throws GardenaException {
+ if (isLocalDurationCommand(channelUID)) {
+ String dataItemProperty = getDeviceDataItemProperty(channelUID);
+ return new DecimalType(Math.round(device.getLocalService(dataItemProperty).commandDuration / 60.0));
+ }
+
+ String propertyPath = channelUID.getGroupId() + ".attributes.";
String propertyName = channelUID.getIdWithoutGroup();
+ if (propertyName.endsWith("_timestamp")) {
+ propertyPath += propertyName.replace("_", ".");
+ } else {
+ propertyPath += propertyName + ".value";
+ }
+
+ String acceptedItemType = null;
try {
- String value = device.getAbility(abilityName).getProperty(propertyName).getValueAsString();
+ Channel channel = getThing().getChannel(channelUID.getId());
+ if (channel != null) {
+ acceptedItemType = StringUtils.substringBefore(channel.getAcceptedItemType(), ":");
- if (StringUtils.trimToNull(value) == null || StringUtils.equals(value, "N/A")) {
- return UnDefType.NULL;
- }
+ if (acceptedItemType != null) {
+ boolean isNullPropertyValue = PropertyUtils.isNull(device, propertyPath);
+ boolean isDurationProperty = "duration".equals(propertyName);
- switch (getThing().getChannel(channelUID.getId()).getAcceptedItemType()) {
- case "String":
- return new StringType(value);
- case "Number":
- if (ABILITY_RADIO.equals(abilityName) && PROPERTY_STATE.equals(propertyName)) {
- switch (value) {
- case "poor":
- return new DecimalType(1);
- case "good":
- return new DecimalType(2);
- case "excellent":
- return new DecimalType(4);
- default:
- return UnDefType.NULL;
- }
- }
- return new DecimalType(value);
- case "Switch":
- return Boolean.TRUE.toString().equalsIgnoreCase(value) || "on".equalsIgnoreCase(value)
- ? OnOffType.ON
- : OnOffType.OFF;
- case "DateTime":
- Calendar cal = DateUtils.parseToCalendar(value);
- if (cal != null && !cal.before(VALID_DATE_START)) {
- return new DateTimeType(ZonedDateTime.ofInstant(cal.toInstant(), ZoneId.systemDefault()));
- } else {
+ if (isNullPropertyValue && !isDurationProperty) {
return UnDefType.NULL;
}
+ switch (acceptedItemType) {
+ case "String":
+ return new StringType(PropertyUtils.getPropertyValue(device, propertyPath, String.class));
+ case "Number":
+ if (isNullPropertyValue) {
+ return new DecimalType(0);
+ } else {
+ Number value = PropertyUtils.getPropertyValue(device, propertyPath, Number.class);
+ // convert duration from seconds to minutes
+ if (value != null) {
+ if (isDurationProperty) {
+ value = Math.round(value.longValue() / 60.0);
+ }
+ return new DecimalType(value.longValue());
+ }
+ return UnDefType.NULL;
+ }
+ case "DateTime":
+ Date date = PropertyUtils.getPropertyValue(device, propertyPath, Date.class);
+ if (date != null) {
+ ZonedDateTime zdt = ZonedDateTime.ofInstant(date.toInstant(),
+ timeZoneProvider.getTimeZone());
+ return new DateTimeType(zdt);
+ }
+ return UnDefType.NULL;
+ }
+ }
}
} catch (GardenaException e) {
- logger.warn("Channel '{}' cannot be updated as device does not contain property '{}:{}'", channelUID,
- abilityName, propertyName);
- }
- return null;
- }
-
- /**
- * Converts an openHAB type to a Gardena command property.
- */
- private Object convertFromType(Type type) {
- if (type instanceof OnOffType) {
- return type == OnOffType.ON ? Boolean.TRUE : Boolean.FALSE;
- } else if (type instanceof DecimalType) {
- return ((DecimalType) type).intValue();
- } else if (type instanceof StringType) {
- return ((StringType) type).toFullString();
+ logger.warn("Channel '{}' cannot be updated as device does not contain propertyPath '{}'", channelUID,
+ propertyPath);
+ } catch (ClassCastException ex) {
+ logger.warn("Value of propertyPath '{}' can not be casted to {}: {}", propertyPath, acceptedItemType,
+ ex.getMessage());
}
return null;
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
+ logger.debug("Command received: {}", command);
try {
- GardenaSmartCommandName commandName = getCommandName(channelUID);
- logger.debug("Received Gardena command: {}", commandName);
-
+ boolean isOnCommand = command instanceof OnOffType && ((OnOffType) command) == OnOffType.ON;
+ String dataItemProperty = getDeviceDataItemProperty(channelUID);
if (RefreshType.REFRESH == command) {
- logger.debug("Refreshing channel '{}'", channelUID);
- if (commandName != null && commandName.toString().startsWith("MEASURE_")) {
- getGardenaSmart().sendCommand(getDevice(), commandName, null);
+ logger.debug("Refreshing Gardena connection");
+ getGardenaSmart().restartWebsockets();
+ } else if (isLocalDurationCommand(channelUID)) {
+ QuantityType<?> quantityType = (QuantityType<?>) command;
+ getDevice().getLocalService(dataItemProperty).commandDuration = quantityType.intValue() * 60;
+ } else if (isOnCommand) {
+ GardenaCommand gardenaCommand = getGardenaCommand(dataItemProperty, channelUID);
+ logger.debug("Received Gardena command: {}, {}", gardenaCommand.getClass().getSimpleName(),
+ gardenaCommand.attributes.command);
+
+ DataItem<?> dataItem = PropertyUtils.getPropertyValue(getDevice(), dataItemProperty, DataItem.class);
+ if (dataItem == null) {
+ logger.warn("DataItem {} is empty, ignoring command.", dataItemProperty);
} else {
- updateChannel(channelUID);
+ getGardenaSmart().sendCommand(dataItem, gardenaCommand);
+
+ commandResetFuture = scheduler.schedule(() -> {
+ updateState(channelUID, OnOffType.OFF);
+ }, 3, TimeUnit.SECONDS);
}
- } else if (commandName != null) {
- getGardenaSmart().sendCommand(getDevice(), commandName, convertFromType(command));
}
} catch (AccountHandlerNotAvailableException | GardenaDeviceNotFoundException ex) {
// ignore
} catch (Exception ex) {
- logger.warn("{}", ex.getMessage(), ex);
+ logger.warn("{}", ex.getMessage());
+ final Bridge bridge;
+ final ThingHandler handler;
+ if ((bridge = getBridge()) != null && (handler = bridge.getHandler()) != null) {
+ ((GardenaSmartEventListener) handler).onError();
+ }
}
}
/**
* Returns the Gardena command from the channel.
*/
- private GardenaSmartCommandName getCommandName(ChannelUID channelUID) {
- switch (channelUID.getId()) {
- case "mower#park_until_further_notice":
- return PARK_UNTIL_FURTHER_NOTICE;
- case "mower#park_until_next_timer":
- return PARK_UNTIL_NEXT_TIMER;
- case "mower#start_override_timer":
- return START_OVERRIDE_TIMER;
- case "mower#start_resume_schedule":
- return START_RESUME_SCHEDULE;
- case "mower#duration_property":
- return DURATION_PROPERTY;
-
- case "ambient_temperature#temperature":
- return MEASURE_AMBIENT_TEMPERATURE;
- case "soil_temperature#temperature":
- return MEASURE_SOIL_TEMPERATURE;
- case "humidity#humidity":
- return MEASURE_SOIL_HUMIDITY;
- case "light#light":
- return MEASURE_LIGHT;
-
- case "outlet#button_manual_override_time":
- return OUTLET_MANUAL_OVERRIDE_TIME;
- case "outlet#valve_open":
- return OUTLET_VALVE;
-
- case "power#power_timer":
- return POWER_TIMER;
- case "watering#watering_timer_1":
- return WATERING_TIMER_VALVE_1;
- case "watering#watering_timer_2":
- return WATERING_TIMER_VALVE_2;
- case "watering#watering_timer_3":
- return WATERING_TIMER_VALVE_3;
- case "watering#watering_timer_4":
- return WATERING_TIMER_VALVE_4;
- case "watering#watering_timer_5":
- return WATERING_TIMER_VALVE_5;
- case "watering#watering_timer_6":
- return WATERING_TIMER_VALVE_6;
-
- case "manual_watering#manual_watering_timer":
- return PUMP_MANUAL_WATERING_TIMER;
-
- default:
- return null;
+ private GardenaCommand getGardenaCommand(String dataItemProperty, ChannelUID channelUID)
+ throws GardenaException, AccountHandlerNotAvailableException {
+ String commandName = channelUID.getIdWithoutGroup().toUpperCase();
+ String groupId = channelUID.getGroupId();
+ if (groupId != null) {
+ if (groupId.startsWith("valve") && groupId.endsWith("_commands")) {
+ return new ValveCommand(ValveControl.valueOf(commandName),
+ getDevice().getLocalService(dataItemProperty).commandDuration);
+ } else if ("mower_commands".equals(groupId)) {
+ return new MowerCommand(MowerControl.valueOf(commandName),
+ getDevice().getLocalService(dataItemProperty).commandDuration);
+ } else if ("valveSet_commands".equals(groupId)) {
+ return new ValveSetCommand(ValveSetControl.valueOf(commandName));
+ } else if ("powerSocket_commands".equals(groupId)) {
+ return new PowerSocketCommand(PowerSocketControl.valueOf(commandName),
+ getDevice().getLocalService(dataItemProperty).commandDuration);
+ }
}
+ throw new GardenaException("Command " + channelUID.getId() + " not found or groupId null");
}
/**
* Updates the thing status based on the Gardena device status.
*/
protected void updateStatus(Device device) {
- String connectionStatus = "";
- try {
- connectionStatus = device.getAbility(ABILITY_RADIO).getProperty(PROPERTY_CONNECTION_STATUS)
- .getValueAsString();
- } catch (GardenaException ex) {
- // ignore, device has no connection status property
- }
-
- boolean isUnreach = PROPERTY_CONNECTION_STATUS_UNREACH_VALUE.equals(connectionStatus);
-
ThingStatus oldStatus = thing.getStatus();
ThingStatus newStatus = ThingStatus.ONLINE;
ThingStatusDetail newDetail = ThingStatusDetail.NONE;
- if (isUnreach) {
+ if (!CONNECTION_STATUS_ONLINE.equals(device.common.attributes.rfLinkState.value)) {
newStatus = ThingStatus.OFFLINE;
newDetail = ThingStatusDetail.COMMUNICATION_ERROR;
- } else if (!device.isConfigurationSynchronized()) {
- newStatus = thing.getStatus();
- newDetail = ThingStatusDetail.CONFIGURATION_PENDING;
}
if (oldStatus != newStatus || thing.getStatusInfo().getStatusDetail() != newDetail) {
}
}
- @Override
- public void handleConfigurationUpdate(Map<String, Object> configurationParameters)
- throws ConfigValidationException {
- validateConfigurationParameters(configurationParameters);
-
- try {
- GardenaSmart gardena = getGardenaSmart();
- Device device = gardena.getDevice(UidUtils.getGardenaDeviceId(getThing()));
-
- for (Entry<String, Object> configurationParmeter : configurationParameters.entrySet()) {
- String key = configurationParmeter.getKey();
- Object newValue = configurationParmeter.getValue();
- if (newValue != null && SETTING_TURN_ON_PRESSURE.equals(key)) {
- newValue = new Double((String) newValue);
- }
-
- Setting setting = device.getSetting(key);
- if (ObjectUtils.notEqual(setting.getValue(), newValue)) {
- gardena.sendSetting(setting, newValue);
- setting.setValue(newValue);
- }
- }
- updateSettings(device);
- } catch (GardenaException | AccountHandlerNotAvailableException ex) {
- logger.warn("Error setting thing properties: {}", ex.getMessage(), ex);
+ /**
+ * Returns the device property for the dataItem from the channel.
+ */
+ private String getDeviceDataItemProperty(ChannelUID channelUID) throws GardenaException {
+ String dataItemProperty = StringUtils.substringBeforeLast(channelUID.getGroupId(), "_");
+ if (dataItemProperty != null) {
+ return dataItemProperty;
}
+ throw new GardenaException("Can't extract dataItemProperty from channel group " + channelUID.getGroupId());
+ }
+
+ /**
+ * Returns true, if the channel is the duration command.
+ */
+ private boolean isLocalDurationCommand(ChannelUID channelUID) {
+ return "commandDuration".equals(channelUID.getIdWithoutGroup());
}
/**
}
/**
- * Returns the Gardena Smart Home implementation if the bridge is available.
+ * Returns the Gardena smart system implementation if the bridge is available.
*/
private GardenaSmart getGardenaSmart() throws AccountHandlerNotAvailableException {
- if (getBridge() == null || getBridge().getHandler() == null
- || ((GardenaAccountHandler) getBridge().getHandler()).getGardenaSmart() == null) {
- if (thing.getStatus() != ThingStatus.INITIALIZING) {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_MISSING_ERROR);
+ final Bridge bridge;
+ final ThingHandler handler;
+ if ((bridge = getBridge()) != null && (handler = bridge.getHandler()) != null) {
+ final GardenaSmart gardenaSmart = ((GardenaAccountHandler) handler).getGardenaSmart();
+ if (gardenaSmart != null) {
+ return gardenaSmart;
}
- throw new AccountHandlerNotAvailableException("Gardena AccountHandler not yet available!");
}
-
- return ((GardenaAccountHandler) getBridge().getHandler()).getGardenaSmart();
+ if (thing.getStatus() != ThingStatus.INITIALIZING) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_MISSING_ERROR);
+ }
+ throw new AccountHandlerNotAvailableException("Gardena AccountHandler not yet available!");
}
}
+++ /dev/null
-/**
- * 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.gardena.internal.model;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.openhab.binding.gardena.internal.exception.GardenaException;
-
-/**
- * Represents a Gardena ability.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class Ability {
-
- private String name;
- private String type;
- private transient Device device;
-
- private List<Property> properties = new ArrayList<>();
-
- /**
- * Returns the name of the ability.
- */
- public String getName() {
- return name;
- }
-
- /**
- * Returns the type of the ability.
- */
- public String getType() {
- return type;
- }
-
- /**
- * Returns a list of properties of the ability.
- */
- public List<Property> getProperties() {
- return properties;
- }
-
- /**
- * Adds a property to this ability.
- */
- public void addProperty(Property property) {
- property.setAbility(this);
- properties.add(property);
- }
-
- /**
- * Returns the property with the specified name.
- */
- public Property getProperty(String name) throws GardenaException {
- for (Property property : properties) {
- if (property.getName().equals(name)) {
- return property;
- }
- }
- throw new GardenaException("Property '" + name + "' not found in ability '" + this.name + "'");
- }
-
- /**
- * Returns the device of the ability.
- */
- public Device getDevice() {
- return device;
- }
-
- /**
- * Sets the name of the ability.
- */
- public void setDevice(Device device) {
- this.device = device;
- }
-}
--- /dev/null
+/**
+ * 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.gardena.internal.model;
+
+import java.lang.reflect.Type;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.gardena.internal.exception.GardenaException;
+import org.openhab.binding.gardena.internal.model.dto.api.DataItem;
+
+import com.google.gson.*;
+
+/**
+ * Custom deserializer for Gardena DataItems.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+@NonNullByDefault
+public class DataItemDeserializer implements JsonDeserializer<DataItem<?>> {
+ private static Gson gson = new GsonBuilder().create();
+
+ @Override
+ public @Nullable DataItem<?> deserialize(JsonElement element, Type type, JsonDeserializationContext ctx)
+ throws JsonParseException {
+ try {
+ JsonObject jsonObj = element.getAsJsonObject();
+ return gson.fromJson(element, DataItemFactory.create(jsonObj.get("type").getAsString()));
+ } catch (GardenaException ex) {
+ throw new JsonParseException(ex.getMessage(), ex);
+ }
+ }
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.gardena.internal.exception.GardenaException;
+import org.openhab.binding.gardena.internal.model.dto.api.*;
+
+/**
+ * Creates the dataItem object based on the device type.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+@NonNullByDefault
+public class DataItemFactory {
+ public static Class<? extends DataItem<?>> create(String type) throws GardenaException {
+ switch (type) {
+ case "LOCATION":
+ return LocationDataItem.class;
+ case "DEVICE":
+ return DeviceDataItem.class;
+ case "COMMON":
+ return CommonServiceDataItem.class;
+ case "MOWER":
+ return MowerServiceDataItem.class;
+ case "POWER_SOCKET":
+ return PowerSocketServiceDataItem.class;
+ case "VALVE":
+ return ValveServiceDataItem.class;
+ case "VALVE_SET":
+ return ValveSetServiceDataItem.class;
+ case "SENSOR":
+ return SensorServiceDataItem.class;
+ default:
+ throw new GardenaException("Unknown DataItem type: " + type);
+ }
+ }
+}
+++ /dev/null
-/**
- * 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.gardena.internal.model;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.commons.lang.builder.EqualsBuilder;
-import org.apache.commons.lang.builder.HashCodeBuilder;
-import org.openhab.binding.gardena.internal.exception.GardenaException;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Represents a Gardena device.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class Device {
-
- private String id;
- private String name;
- private String description;
- private String category;
- @SerializedName("configuration_synchronized")
- private boolean configurationSynchronized;
- private List<Ability> abilities = new ArrayList<>();
- @SerializedName("scheduled_events")
- private List<ScheduledEvent> scheduledEvents = new ArrayList<>();
- private transient Location location;
- private List<Setting> settings = new ArrayList<>();
-
- /**
- * Returns the id of the device.
- */
- public String getId() {
- return id;
- }
-
- /**
- * Returns the name of the device.
- */
- public String getName() {
- return name;
- }
-
- /**
- * Returns the description of the device.
- */
- public String getDescription() {
- return description;
- }
-
- /**
- * Returns the category of the device.
- */
- public String getCategory() {
- return category;
- }
-
- /**
- * Returns true, if all configurations are synchronized.
- */
- public boolean isConfigurationSynchronized() {
- return configurationSynchronized;
- }
-
- /**
- * Returns a list of abilities of the device.
- */
- public List<Ability> getAbilities() {
- return abilities;
- }
-
- /**
- * Returns a list of scheduled events of the device.
- */
- public List<ScheduledEvent> getScheduledEvents() {
- return scheduledEvents;
- }
-
- /**
- * Returns the location of the device.
- */
- public Location getLocation() {
- return location;
- }
-
- /**
- * Sets the location of the device.
- */
- public void setLocation(Location location) {
- this.location = location;
- }
-
- /**
- * Returns the ability with the specified name.
- */
- public Ability getAbility(String name) throws GardenaException {
- for (Ability ability : abilities) {
- if (ability.getName().equals(name)) {
- return ability;
- }
- }
- throw new GardenaException("Ability '" + name + "' not found in device '" + this.name + "'");
- }
-
- public List<Setting> getSettings() {
- return settings;
- }
-
- /**
- * Returns the setting with the specified name.
- */
- public Setting getSetting(String name) throws GardenaException {
- for (Setting setting : settings) {
- if (setting.getName().equals(name)) {
- return setting;
- }
- }
- throw new GardenaException("Setting '" + name + "' not found in device '" + this.name + "'");
- }
-
- @Override
- public int hashCode() {
- return new HashCodeBuilder().append(id).toHashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null || !(obj instanceof Device)) {
- return false;
- }
- Device comp = (Device) obj;
- return new EqualsBuilder().append(comp.getId(), id).isEquals();
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Represents a List of Gardena devices.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class Devices {
-
- private List<Device> devices = new ArrayList<>();
-
- /**
- * Returns a list of Gardena devices.
- */
- public List<Device> getDevices() {
- return devices;
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model;
-
-import org.apache.commons.lang.builder.ToStringBuilder;
-import org.apache.commons.lang.builder.ToStringStyle;
-
-/**
- * Represents a Gardena error.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class Error {
-
- private String id;
- private String status;
- private String title;
- private String detail;
-
- /**
- * Returns the id of the error.
- */
- public String getId() {
- return id;
- }
-
- /**
- * Returns the status of the error.
- */
- public String getStatus() {
- return status;
- }
-
- /**
- * Returns the title of the error.
- */
- public String getTitle() {
- return title;
- }
-
- /**
- * Returns the detail of the error.
- */
- public String getDetail() {
- return detail;
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("id", id).append("status", status)
- .append("title", title).append("detail", detail).toString();
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.commons.lang.builder.ToStringBuilder;
-import org.apache.commons.lang.builder.ToStringStyle;
-
-/**
- * Represents a List of Gardena errors.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class Errors {
-
- private List<Error> errors = new ArrayList<>();
-
- /**
- * Returns a list of Gardena errors.
- */
- public List<Error> getErrors() {
- return errors;
- }
-
- @Override
- public String toString() {
- ToStringBuilder tsb = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
- for (Error error : errors) {
- tsb.append(error);
- }
- return tsb.toString();
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.commons.lang.builder.EqualsBuilder;
-import org.apache.commons.lang.builder.HashCodeBuilder;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Represents a Gardena location.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class Location {
-
- private String id;
- private String name;
- @SerializedName("devices")
- public List<String> deviceIds = new ArrayList<>();
-
- /**
- * Returns the id of the location.
- */
- public String getId() {
- return id;
- }
-
- /**
- * Returns the name of the location.
- */
- public String getName() {
- return name;
- }
-
- /**
- * Returns the device ids of the location.
- */
- public List<String> getDeviceIds() {
- return deviceIds;
- }
-
- @Override
- public int hashCode() {
- return new HashCodeBuilder().append(id).toHashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null || !(obj instanceof Location)) {
- return false;
- }
- Location comp = (Location) obj;
- return new EqualsBuilder().append(comp.getId(), id).isEquals();
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Represents a List of Gardena locations.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class Locations {
-
- private List<Location> locations = new ArrayList<>();
-
- /**
- * Returns a list of Gardena locations.
- */
- public List<Location> getLocations() {
- return locations;
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model;
-
-/**
- * Empty class that represents a emtpy result from Gardena Smart Home.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class NoResult {
-
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model;
-
-import java.util.Date;
-import java.util.List;
-
-import org.openhab.binding.gardena.internal.GardenaSmartCommandName;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Represents a Gardena property.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class Property {
-
- private String name;
- private PropertyValue value;
- private Date timestamp;
- private String unit;
- private boolean writeable;
-
- @SerializedName("supported_values")
- private List<String> supportedValues;
- private transient Ability ability;
-
- public Property() {
- }
-
- public Property(GardenaSmartCommandName commandName, String value) {
- this.name = commandName.toString().toLowerCase();
- this.value = new PropertyValue(value);
- }
-
- /**
- * Returns the name of the property.
- */
- public String getName() {
- return name;
- }
-
- /**
- * Returns the value of the property.
- */
- public String getValueAsString() {
- return value != null ? value.getValue() : null;
- }
-
- /**
- * Returns the value of the property.
- */
- public PropertyValue getValue() {
- return value;
- }
-
- /**
- * Sets the value of the property.
- */
- public void setValue(PropertyValue value) {
- this.value = value;
- }
-
- /**
- * Returns the timestamp of the property.
- */
- public Date getTimestamp() {
- return timestamp;
- }
-
- /**
- * Returns the unit of the property.
- */
- public String getUnit() {
- return unit;
- }
-
- /**
- * Returns true, if the property is writeable.
- */
- public boolean isWriteable() {
- return writeable;
- }
-
- /**
- * Returns a list of supported values.
- */
- public List<String> getSupportedValues() {
- return supportedValues;
- }
-
- /**
- * Returns the ability of the property.
- */
-
- public Ability getAbility() {
- return ability;
- }
-
- /**
- * Sets the ability of the property.
- */
- public void setAbility(Ability ability) {
- this.ability = ability;
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model;
-
-/**
- * Represents a Gardena property value.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class PropertyValue {
- private String value;
-
- public PropertyValue() {
- }
-
- public PropertyValue(String value) {
- this.value = value;
- }
-
- /**
- * Returns the value of the property.
- */
- public String getValue() {
- return value;
- }
-
- /**
- * Sets the value of the property.
- */
- public void setValue(String value) {
- this.value = value;
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model;
-
-import java.util.List;
-
-/**
- * Represents a Gardena recurrence.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class Recurrence {
-
- private String type;
- private List<String> weekdays;
-
- /**
- * Returns the type of the recurrence.
- */
- public String getType() {
- return type;
- }
-
- /**
- * Returns a list of weekdays.
- */
- public List<String> getWeekdays() {
- return weekdays;
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Represents a Gardena scheduled event.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class ScheduledEvent {
-
- private String id;
- private String type;
- @SerializedName("start_at")
- private String start;
- @SerializedName("end_at")
- private String end;
- private String weekday;
-
- private Recurrence recurrence = new Recurrence();
-
- /**
- * Returns the id of the scheduled event.
- */
- public String getId() {
- return id;
- }
-
- /**
- * Returns the type of the scheduled event.
- */
- public String getType() {
- return type;
- }
-
- /**
- * Returns the start of the scheduled event.
- */
- public String getStart() {
- return start;
- }
-
- /**
- * Returns the end of the scheduled event.
- */
- public String getEnd() {
- return end;
- }
-
- /**
- * Returns the weekday of the scheduled event.
- */
- public String getWeekday() {
- return weekday;
- }
-
- /**
- * Returns the recurrence of the scheduled event.
- */
- public Recurrence getRecurrence() {
- return recurrence;
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Represents a Gardena session.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class Session {
- @SerializedName("id")
- private String token;
- private long created;
-
- @SerializedName("attributes")
- private SessionAttributes sessionAttributes = new SessionAttributes();
-
- public Session() {
- this.created = System.currentTimeMillis();
- }
-
- /**
- * Returns the token of the session.
- */
- public String getToken() {
- return token;
- }
-
- /**
- * Returns the creation timestamp of the session.
- */
- public long getCreated() {
- return created;
- }
-
- /**
- * Returns the session attributes.
- */
- public SessionAttributes getSessionAttributes() {
- return sessionAttributes;
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Session attributes for valid Gardena JSON serialization.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class SessionAttributes {
- @SerializedName("user_id")
- private String userId;
-
- @SerializedName("provider")
- private String provider;
-
- /**
- * Returns the user id.
- */
- public String getUserId() {
- return userId;
- }
-
- /**
- * Sets the user id.
- */
- public void setUserId(String userId) {
- this.userId = userId;
- }
-
- /**
- * Returns the provider.
- */
- public String getProvider() {
- return provider;
- }
-
- /**
- * Sets the provider.
- */
- public void setProvider(String provider) {
- this.provider = provider;
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Session wrapper for valid Gardena JSON serialization.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class SessionWrapper {
- @SerializedName("data")
- private Session session;
-
- public SessionWrapper() {
- }
-
- public SessionWrapper(Session session) {
- this.session = session;
- }
-
- /**
- * Returns the session.
- */
- public Session getSession() {
- return session;
- }
-
- /**
- * Sets the session.
- */
- public void setSession(Session session) {
- this.session = session;
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model;
-
-/**
- * Represents a Gardena setting.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-
-public class Setting {
- private String name;
- private String id;
- private Object value;
- private transient Device device;
-
- /**
- * Returns the name of the setting.
- */
- public String getName() {
- return name;
- }
-
- /**
- * Returns the id of the setting.
- */
- public String getId() {
- return id;
- }
-
- /**
- * Returns the value of the setting.
- */
- public Object getValue() {
- return value;
- }
-
- /**
- * Sets the name of the setting.
- */
- public void setValue(Object value) {
- this.value = value;
- }
-
- /**
- * Returns the device of the setting.
- */
- public Device getDevice() {
- return device;
- }
-
- /**
- * Sets the name of the setting.
- */
- public void setDevice(Device device) {
- this.device = device;
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.command;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Base class for a Gardena command with parameters.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public abstract class Command {
-
- @SerializedName(value = "name")
- protected String command;
- protected CommandParameters parameters;
-
- /**
- * Creates a command with the given name.
- */
- public Command(String command) {
- this.command = command;
- }
-
- /**
- * Returns the command name.
- */
- public String getCommand() {
- return command;
- }
-
- /**
- * Returns the parameters of the command.
- */
- public CommandParameters getParameters() {
- return parameters;
- }
-
- /**
- * Sets the parameters of the command.
- */
- public void setParameters(CommandParameters parameters) {
- this.parameters = parameters;
- }
-
- /**
- * Class to hold the command parameters.
- *
- * @author Gerhard Riegler - Initial contribution
- */
- public class CommandParameters {
- private String duration;
- @SerializedName("manual_override")
- private String manualOverride;
-
- /**
- * Returns the duration parameter.
- */
- public String getDuration() {
- return duration;
- }
-
- /**
- * Sets the duration parameter.
- */
- public void setDuration(String duration) {
- this.duration = duration;
- }
-
- /**
- * Returns the manual override parameter.
- */
- public String getManualOverride() {
- return manualOverride;
- }
-
- /**
- * Sets the manual override parameter.
- */
- public void setManualOverride(String manualOverride) {
- this.manualOverride = manualOverride;
- }
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.command;
-
-/**
- * Command to park a mower until further notice.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class MowerParkUntilFurtherNoticeCommand extends Command {
- private static final String COMMAND = "park_until_further_notice";
-
- public MowerParkUntilFurtherNoticeCommand() {
- super(COMMAND);
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.command;
-
-/**
- * Command to park a mower until next timer.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-
-public class MowerParkUntilNextTimerCommand extends Command {
- private static final String COMMAND = "park_until_next_timer";
-
- public MowerParkUntilNextTimerCommand() {
- super(COMMAND);
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.command;
-
-/**
- * Command to manually override the mower timer.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-
-public class MowerStartOverrideTimerCommand extends Command {
- private static final String COMMAND = "start_override_timer";
-
- public MowerStartOverrideTimerCommand(String durationInMinutes) {
- super(COMMAND);
- parameters = new CommandParameters();
- parameters.setDuration(durationInMinutes);
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.command;
-
-/**
- * Command to resume the mower scheduler.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-
-public class MowerStartResumeScheduleCommand extends Command {
- private static final String COMMAND = "start_resume_schedule";
-
- public MowerStartResumeScheduleCommand() {
- super(COMMAND);
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.command;
-
-/**
- * Sensor command to measure the ambient temperatur.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-
-public class SensorMeasureAmbientTemperatureCommand extends Command {
- private static final String COMMAND = "measure_ambient_temperature";
-
- public SensorMeasureAmbientTemperatureCommand() {
- super(COMMAND);
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.command;
-
-/**
- * Sensor command to measure the light.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-
-public class SensorMeasureLightCommand extends Command {
- private static final String COMMAND = "measure_light";
-
- public SensorMeasureLightCommand() {
- super(COMMAND);
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.command;
-
-/**
- * Sensor command to measure the soil humidity.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-
-public class SensorMeasureSoilHumidityCommand extends Command {
- private static final String COMMAND = "measure_soil_humidity";
-
- public SensorMeasureSoilHumidityCommand() {
- super(COMMAND);
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.command;
-
-/**
- * Sensor command to measure the soil temperature.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-
-public class SensorMeasureSoilTemperatureCommand extends Command {
- private static final String COMMAND = "measure_soil_temperature";
-
- public SensorMeasureSoilTemperatureCommand() {
- super(COMMAND);
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.command;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Common command to set a device setting.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class SettingCommand extends Command {
- private Object value;
- @SerializedName("device")
- private String deviceId;
-
- public SettingCommand(String name) {
- super(name);
- }
-
- /**
- * Returns the value of the setting command.
- */
- public Object getValue() {
- return value;
- }
-
- /**
- * Sets the value of the setting command.
- */
- public void setValue(Object value) {
- this.value = value;
- }
-
- /**
- * Returns the device id of the setting command.
- */
- public String getDeviceId() {
- return deviceId;
- }
-
- /**
- * Sets the device id of the setting command.
- */
- public void setDeviceId(String deviceId) {
- this.deviceId = deviceId;
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.command;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * SettingComand wrapper for valid Gardena JSON serialization.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class SettingCommandWrapper {
- @SerializedName("settings")
- private SettingCommand command;
-
- public SettingCommandWrapper(SettingCommand command) {
- this.command = command;
- }
-
- /**
- * Returns the setting command.
- */
- public SettingCommand getCommand() {
- return command;
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.command;
-
-/**
- * Command to cancel the manual watering.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-
-public class WateringCancelOverrideCommand extends Command {
- private static final String COMMAND = "cancel_override";
-
- public WateringCancelOverrideCommand() {
- super(COMMAND);
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.command;
-
-/**
- * Command to start the manual watering.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-
-public class WateringManualOverrideCommand extends Command {
- private static final String COMMAND = "manual_override";
-
- public WateringManualOverrideCommand(String durationInMinutes) {
- super(COMMAND);
- parameters = new CommandParameters();
- parameters.setDuration(durationInMinutes);
- parameters.setManualOverride("open");
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.deser;
-
-import java.lang.reflect.Type;
-import java.util.Date;
-
-import org.openhab.binding.gardena.internal.util.DateUtils;
-
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParseException;
-
-/**
- * Custom deserializer for date types.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class DateDeserializer implements JsonDeserializer<Date> {
-
- @Override
- public Date deserialize(JsonElement element, Type type, JsonDeserializationContext ctx) throws JsonParseException {
- return DateUtils.parseToDate(element.getAsString());
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.deser;
-
-import java.lang.reflect.Type;
-
-import org.apache.commons.lang.StringUtils;
-import org.openhab.binding.gardena.internal.model.PropertyValue;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.JsonArray;
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParseException;
-
-/**
- * Custom deserializer for Gardena complex property value type.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class PropertyValueDeserializer implements JsonDeserializer<PropertyValue> {
- private final Logger logger = LoggerFactory.getLogger(PropertyValueDeserializer.class);
-
- private static final String PROPERTY_DURATION = "duration";
- private static final String PROPERTY_TYPE = "type";
- private static final String PROPERTY_MAC = "mac";
- private static final String PROPERTY_ISCONNECTED = "isconnected";
-
- @Override
- public PropertyValue deserialize(JsonElement element, Type type, JsonDeserializationContext ctx)
- throws JsonParseException {
- if (element.isJsonObject()) {
- JsonObject jsonObj = element.getAsJsonObject();
- if (jsonObj.has(PROPERTY_DURATION)) {
- long duration = jsonObj.get(PROPERTY_DURATION).getAsLong();
- if (duration != 0) {
- duration = Math.round(duration / 60.0);
- }
- return new PropertyValue(String.valueOf(duration));
- } else if (jsonObj.has(PROPERTY_TYPE)) {
- return new PropertyValue(jsonObj.get(PROPERTY_TYPE).getAsString());
- } else if (jsonObj.has(PROPERTY_MAC) && jsonObj.has(PROPERTY_ISCONNECTED)) {
- // ignore known gateway properties
- return new PropertyValue();
- } else {
- logger.warn("Unsupported json value object, returning empty value");
- return new PropertyValue();
- }
-
- } else if (element.isJsonArray()) {
- JsonArray jsonArray = element.getAsJsonArray();
- return new PropertyValue(StringUtils.join(jsonArray.iterator(), ","));
- } else {
- return new PropertyValue(element.getAsString());
- }
- }
-}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto;
+
+import static org.openhab.binding.gardena.internal.GardenaBindingConstants.*;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openhab.binding.gardena.internal.exception.GardenaException;
+import org.openhab.binding.gardena.internal.model.dto.api.*;
+import org.openhab.binding.gardena.internal.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Represents a Gardena device.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class Device {
+ private final Logger logger = LoggerFactory.getLogger(Device.class);
+
+ private transient static final String DEVICE_TYPE_PREFIX = "gardena smart";
+ public boolean active = true;
+ public String id;
+ public String deviceType;
+ public String location;
+ public CommonServiceDataItem common;
+ public MowerServiceDataItem mower;
+ public PowerSocketServiceDataItem powerSocket;
+ public SensorServiceDataItem sensor;
+ public ValveServiceDataItem valve;
+ public ValveServiceDataItem valveOne;
+ public ValveServiceDataItem valveTwo;
+ public ValveServiceDataItem valveThree;
+ public ValveServiceDataItem valveFour;
+ public ValveServiceDataItem valveFive;
+ public ValveServiceDataItem valveSix;
+ public ValveSetServiceDataItem valveSet;
+
+ private Map<String, LocalService> localServices = new HashMap<>();
+
+ public Device(String id) {
+ this.id = id;
+ }
+
+ /**
+ * Returns the local service or creates one if it does not exist.
+ */
+ public LocalService getLocalService(String key) {
+ LocalService localService = localServices.get(key);
+ if (localService == null) {
+ localService = new LocalService();
+ localServices.put(key, localService);
+ localService.commandDuration = 3600;
+ }
+ return localService;
+ }
+
+ /**
+ * Evaluates the device type.
+ */
+ public void evaluateDeviceType() {
+ if (deviceType == null) {
+ if (common.attributes.modelType.value.toLowerCase().startsWith(DEVICE_TYPE_PREFIX)) {
+ String modelType = common.attributes.modelType.value.toLowerCase();
+ modelType = modelType.substring(14);
+ deviceType = modelType.replace(" ", "_");
+ } else {
+ // workaround: we have to guess the device type, valves cannot be identified if modeType is wrong
+ if (mower != null) {
+ deviceType = DEVICE_TYPE_MOWER;
+ } else if (powerSocket != null) {
+ deviceType = DEVICE_TYPE_POWER;
+ } else if (sensor != null) {
+ deviceType = DEVICE_TYPE_SENSOR;
+ }
+ }
+ if (deviceType == null) {
+ logger.warn("Can't identify device with id {}, wrong modelType sent from the Gardena API", id);
+ active = false;
+ }
+ }
+ }
+
+ /**
+ * Assigns the dataItem to the corresponding property.
+ */
+ public void setDataItem(DataItem<?> dataItem) throws GardenaException {
+ if (dataItem instanceof DeviceDataItem) {
+ // ignore
+ } else if (dataItem instanceof LocationDataItem) {
+ LocationDataItem locationDataItem = (LocationDataItem) dataItem;
+ if (locationDataItem.attributes != null) {
+ location = locationDataItem.attributes.name;
+ }
+ } else if (dataItem instanceof CommonServiceDataItem) {
+ common = (CommonServiceDataItem) dataItem;
+ } else if (dataItem instanceof MowerServiceDataItem) {
+ mower = (MowerServiceDataItem) dataItem;
+ } else if (dataItem instanceof PowerSocketServiceDataItem) {
+ powerSocket = (PowerSocketServiceDataItem) dataItem;
+ } else if (dataItem instanceof SensorServiceDataItem) {
+ sensor = (SensorServiceDataItem) dataItem;
+ } else if (dataItem instanceof ValveSetServiceDataItem) {
+ valveSet = (ValveSetServiceDataItem) dataItem;
+ } else if (dataItem instanceof ValveServiceDataItem) {
+ String valveNumber = StringUtils.substringAfterLast(dataItem.id, ":");
+ if (valveNumber != null
+ && (valveNumber.equals("") || valveNumber.equals("wc") || valveNumber.equals("0"))) {
+ valve = (ValveServiceDataItem) dataItem;
+ } else if ("1".equals(valveNumber)) {
+ valveOne = (ValveServiceDataItem) dataItem;
+ } else if ("2".equals(valveNumber)) {
+ valveTwo = (ValveServiceDataItem) dataItem;
+ } else if ("3".equals(valveNumber)) {
+ valveThree = (ValveServiceDataItem) dataItem;
+ } else if ("4".equals(valveNumber)) {
+ valveFour = (ValveServiceDataItem) dataItem;
+ } else if ("5".equals(valveNumber)) {
+ valveFive = (ValveServiceDataItem) dataItem;
+ } else if ("6".equals(valveNumber)) {
+ valveSix = (ValveServiceDataItem) dataItem;
+ } else {
+ throw new GardenaException("Unknown valveNumber in dataItem with id: " + dataItem.id);
+ }
+ } else {
+ throw new GardenaException("Unknown dataItem with id: " + dataItem.id);
+ }
+
+ if (common != null && common.attributes != null) {
+ common.attributes.lastUpdate.timestamp = new Date();
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof Device)) {
+ return false;
+ }
+ Device comp = (Device) obj;
+ return comp.id.equals(id);
+ }
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto;
+
+/**
+ * A local service exists only in openHAB and the state is not saved on restarts.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+
+public class LocalService {
+ public Integer commandDuration;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class CommonService {
+ public UserDefinedNameWrapper name;
+ public TimestampedIntegerValue batteryLevel;
+ public TimestampedStringValue batteryState;
+ public TimestampedIntegerValue rfLinkLevel;
+ public StringValue serial;
+ public StringValue modelType;
+ public TimestampedStringValue rfLinkState;
+ public TimestampValue lastUpdate = new TimestampValue();
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+
+public class CommonServiceDataItem extends DataItem<CommonService> {
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+
+public class CreateWebSocket {
+ public String locationId;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+
+public class CreateWebSocketDataItem extends DataItem<CreateWebSocket> {
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+
+public class CreateWebSocketRequest {
+ public CreateWebSocketDataItem data;
+
+ public CreateWebSocketRequest(String locationId) {
+ data = new CreateWebSocketDataItem();
+ data.id = "wsreq-" + locationId;
+ data.type = "WEBSOCKET";
+ data.attributes = new CreateWebSocket();
+ data.attributes.locationId = locationId;
+ }
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+import org.openhab.binding.gardena.internal.util.StringUtils;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+
+public class DataItem<T> {
+ public String id;
+ public String type;
+
+ public String getDeviceId() {
+ return StringUtils.substringBeforeLast(id, ":");
+ }
+
+ public T attributes;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+
+public class DeviceDataItem extends DataItem<Void> {
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+
+public class IntegerValue {
+ public Integer value;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+
+public class Location {
+ public String name;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+
+public class LocationDataItem extends DataItem<Location> {
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+import java.util.List;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+
+public class LocationResponse {
+ public LocationDataItem data;
+ public List<DataItem<?>> included;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+import java.util.List;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+
+public class LocationsResponse {
+ public List<LocationDataItem> data;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+
+public class MowerService {
+ public TimestampedStringValue state;
+ public TimestampedStringValue activity;
+ public TimestampedStringValue lastErrorCode;
+ public IntegerValue operatingHours;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+
+public class MowerServiceDataItem extends DataItem<MowerService> {
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class PostOAuth2Response {
+ // refresh token is valid 10 days
+ private transient Instant refreshTokenValidity = Instant.now().plus(10, ChronoUnit.DAYS).minus(1,
+ ChronoUnit.MINUTES);
+ private transient Instant accessTokenValidity;
+
+ @SerializedName("access_token")
+ public String accessToken;
+
+ // The scope of the token (what you are allowed to do)
+ public String scope;
+
+ // The expire time in seconds for the access token
+ @SerializedName("expires_in")
+ public Integer expiresIn;
+
+ @SerializedName("refresh_token")
+ public String refreshToken;
+
+ public String provider;
+
+ @SerializedName("user_id")
+ public String userId;
+
+ @SerializedName("token_type")
+ public String tokenType;
+
+ public void postProcess() {
+ accessTokenValidity = Instant.now().plus(expiresIn - 10, ChronoUnit.SECONDS);
+ }
+
+ public boolean isAccessTokenExpired() {
+ return Instant.now().isAfter(accessTokenValidity);
+ }
+
+ public boolean isRefreshTokenExpired() {
+ return Instant.now().isAfter(refreshTokenValidity);
+ }
+
+ @Override
+ public String toString() {
+ return "Token expiration: accessToken: " + ZonedDateTime.ofInstant(accessTokenValidity, ZoneId.systemDefault())
+ + ", refreshToken: " + ZonedDateTime.ofInstant(refreshTokenValidity, ZoneId.systemDefault());
+ }
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+
+public class PowerSocketService {
+ public TimestampedStringValue activity;
+ public TimestampedStringValue state;
+ public TimestampedStringValue lastErrorCode;
+ public TimestampedIntegerValue duration;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class PowerSocketServiceDataItem extends DataItem<PowerSocketService> {
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class SensorService {
+ public TimestampedIntegerValue soilHumidity;
+ public TimestampedIntegerValue soilTemperature;
+ public TimestampedIntegerValue ambientTemperature;
+ public TimestampedIntegerValue lightIntensity;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class SensorServiceDataItem extends DataItem<SensorService> {
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class StringValue {
+ public String value;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class TimestampValue extends TimestampedAttribute {
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+import java.util.Date;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public abstract class TimestampedAttribute {
+ public Date timestamp;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class TimestampedIntegerValue extends TimestampedAttribute {
+ public Integer value;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class TimestampedStringValue extends TimestampedAttribute {
+ public String value;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class UserDefinedNameWrapper {
+ public String value;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class ValveService {
+ public UserDefinedNameWrapper name;
+ public TimestampedStringValue activity;
+ public TimestampedStringValue state;
+ public TimestampedStringValue lastErrorCode;
+ public TimestampedIntegerValue duration;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class ValveServiceDataItem extends DataItem<ValveService> {
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class ValveSetService {
+ public TimestampedStringValue state;
+ public TimestampedStringValue lastErrorCode;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class ValveSetServiceDataItem extends DataItem<ValveSetService> {
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class WebSocket {
+ public Integer validity;
+ public String url;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class WebSocketCreatedResponse {
+ public WebSocketDataItem data;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.api;
+
+/**
+ * Represents a Gardena object that is sent via the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class WebSocketDataItem extends DataItem<WebSocket> {
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.command;
+
+/**
+ * Represents a Gardena command object to send to the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public abstract class GardenaCommand {
+ public String id;
+ public String type;
+ public GardenaCommandAttributes attributes;
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.command;
+
+/**
+ * Represents a Gardena command object that is sent to the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class GardenaCommandAttributes {
+ public String command;
+ public Integer seconds;
+
+ public GardenaCommandAttributes(String command, Integer seconds) {
+ this.command = command;
+ this.seconds = seconds;
+ }
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.command;
+
+/**
+ * Represents a Gardena command object that is sent to the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class GardenaCommandRequest {
+ public GardenaCommand data;
+
+ public GardenaCommandRequest(GardenaCommand gardenaCommand) {
+ this.data = gardenaCommand;
+ }
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.command;
+
+/**
+ * Represents a Gardena command object that is sent to the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class MowerCommand extends GardenaCommand {
+ private static final String COMMAND_TYPE = "MOWER_CONTROL";
+
+ public enum MowerControl {
+ START_SECONDS_TO_OVERRIDE,
+ START_DONT_OVERRIDE,
+ PARK_UNTIL_NEXT_TASK,
+ PARK_UNTIL_FURTHER_NOTICE
+ }
+
+ public MowerCommand(MowerControl mowerControl, Integer seconds) {
+ this.id = "mcid";
+ this.type = COMMAND_TYPE;
+ this.attributes = new GardenaCommandAttributes(mowerControl.name(), seconds);
+ }
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.command;
+
+/**
+ * Represents a Gardena command object that is sent to the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class PowerSocketCommand extends GardenaCommand {
+ private static final String COMMAND_TYPE = "POWER_SOCKET_CONTROL";
+
+ public enum PowerSocketControl {
+ START_SECONDS_TO_OVERRIDE,
+ START_OVERRIDE,
+ STOP_UNTIL_NEXT_TASK,
+ PAUSE,
+ UNPAUSE
+ }
+
+ public PowerSocketCommand(PowerSocketControl powerSocketControl, Integer seconds) {
+ this.id = "pscid";
+ this.type = COMMAND_TYPE;
+ this.attributes = new GardenaCommandAttributes(powerSocketControl.name(), seconds);
+ }
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.command;
+
+/**
+ * Represents a Gardena command object that is sent to the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class ValveCommand extends GardenaCommand {
+ private static final String COMMAND_TYPE = "VALVE_CONTROL";
+
+ public static enum ValveControl {
+ START_SECONDS_TO_OVERRIDE,
+ STOP_UNTIL_NEXT_TASK,
+ PAUSE,
+ UNPAUSE
+ }
+
+ public ValveCommand(ValveControl valveControl, Integer seconds) {
+ this.id = "vcid";
+ this.type = COMMAND_TYPE;
+ this.attributes = new GardenaCommandAttributes(valveControl.name(), seconds);
+ }
+}
--- /dev/null
+/**
+ * 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.gardena.internal.model.dto.command;
+
+/**
+ * Represents a Gardena command object that is sent to the Gardena API.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+public class ValveSetCommand extends GardenaCommand {
+ private static final String COMMAND_TYPE = "VALVE_SET_CONTROL";
+
+ public enum ValveSetControl {
+ STOP_UNTIL_NEXT_TASK
+ }
+
+ public ValveSetCommand(ValveSetControl valveSetControl) {
+ this.id = "vscid";
+ this.type = COMMAND_TYPE;
+ this.attributes = new GardenaCommandAttributes(valveSetControl.name(), null);
+ }
+}
+++ /dev/null
-/**
- * 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.gardena.internal.model.property;
-
-/**
- * Base class to send properties to Gardena.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public abstract class BaseProperty {
- private String name;
-
- public BaseProperty(String name) {
- this.name = name;
- }
-
- /**
- * Returns the property name.
- */
- public String getName() {
- return name;
- }
-
- /**
- * Returns the value of the property.
- */
- public abstract String getValue();
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.property;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Represents a Gardena complex property value for the irrigation control.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-
-public class IrrigationControlWateringProperty extends BaseProperty {
- private IrrigationControlWateringValue value = new IrrigationControlWateringValue();
-
- public IrrigationControlWateringProperty(String name, int duration, int valveId) {
- super(name);
-
- value.state = duration == 0 ? "idle" : "manual";
- value.duration = duration;
- value.valveId = valveId;
- }
-
- @Override
- public String getValue() {
- return String.valueOf(value.duration);
- }
-
- @SuppressWarnings("unused")
- private class IrrigationControlWateringValue {
- public String state;
- public int duration;
- @SerializedName("valve_id")
- public int valveId;
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.property;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Property wrapper for valid Gardena JSON serialization.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class PropertyWrapper {
- @SerializedName("properties")
- private BaseProperty property;
-
- public PropertyWrapper() {
- }
-
- public PropertyWrapper(BaseProperty property) {
- this.property = property;
- }
-
- /**
- * Returns the property.
- */
- public BaseProperty getProperty() {
- return property;
- }
-
- /**
- * Sets the property.
- */
- public void setProperties(BaseProperty property) {
- this.property = property;
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.model.property;
-
-/**
- * Represents a String Gardena property.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class StringProperty extends BaseProperty {
- private String value;
-
- public StringProperty(String name, String value) {
- super(name);
- this.value = value;
- }
-
- /**
- * Returns the property value.
- */
- @Override
- public String getValue() {
- return value;
- }
-}
+++ /dev/null
-/**
- * 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.gardena.internal.util;
-
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
-import java.util.Calendar;
-import java.util.Date;
-
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Utility class to convert a String to a date or calendar.
- *
- * @author Gerhard Riegler - Initial contribution
- */
-public class DateUtils {
- private static final Logger LOGGER = LoggerFactory.getLogger(DateUtils.class);
- private static final String[] DATE_FORMATS = new String[] { "yyyy-MM-dd'T'HH:mm:ss'Z'",
- "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "yyyy-MM-dd'T'HH:mm'Z'" };
-
- /**
- * Converts a string to a Date, trying different date formats used by Gardena.
- */
- public static Date parseToDate(String text) {
- if (StringUtils.isNotBlank(text)) {
- Date parsedDate = null;
- for (String dateFormat : DATE_FORMATS) {
- try {
- parsedDate = new SimpleDateFormat(dateFormat).parse(text);
- ZonedDateTime gmt = ZonedDateTime.ofInstant(parsedDate.toInstant(), ZoneOffset.UTC);
- LocalDateTime here = gmt.withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime();
- parsedDate = Date.from(here.toInstant(ZoneOffset.UTC));
- break;
- } catch (ParseException ex) {
- }
- }
- if (parsedDate == null) {
- LOGGER.error("Can't parse date {}", text);
- }
- return parsedDate;
- } else {
- return null;
- }
- }
-
- /**
- * Converts a string to a Calendar, trying different date formats used by Gardena.
- */
- public static Calendar parseToCalendar(String text) {
- Date parsedDate = parseToDate(text);
- if (parsedDate != null) {
- Calendar cal = Calendar.getInstance();
- cal.setTime(parsedDate);
- return cal;
- }
- return null;
- }
-}
--- /dev/null
+/**
+ * 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.gardena.internal.util;
+
+import java.lang.reflect.Field;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.gardena.internal.exception.GardenaException;
+
+/**
+ * Utility class to read nested properties.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+@NonNullByDefault
+public class PropertyUtils {
+
+ /**
+ * Returns true if the property is null.
+ */
+ public static boolean isNull(@Nullable Object instance, String propertyPath) throws GardenaException {
+ return getPropertyValue(instance, propertyPath, Object.class) == null;
+ }
+
+ /**
+ * Returns the property value from the object instance, nested properties are possible.
+ */
+ public static <T> @Nullable T getPropertyValue(@Nullable Object instance, String propertyPath, Class<T> resultClass)
+ throws GardenaException {
+ String[] properties = propertyPath.split("\\.");
+ return getPropertyValue(instance, properties, resultClass, 0);
+ }
+
+ /**
+ * Iterates through the nested properties and returns the field value.
+ */
+ @SuppressWarnings("unchecked")
+ private static <T> @Nullable T getPropertyValue(@Nullable Object instance, String[] properties,
+ Class<T> resultClass, int nestedIndex) throws GardenaException {
+ if (instance == null) {
+ return null;
+ }
+ try {
+ String propertyName = properties[nestedIndex];
+ Field field = instance.getClass().getField(propertyName);
+ Object result = field.get(instance);
+ if (nestedIndex + 1 < properties.length) {
+ return getPropertyValue(result, properties, resultClass, nestedIndex + 1);
+ }
+ return (T) result;
+ } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
+ throw new GardenaException(ex.getMessage(), ex);
+ }
+ }
+}
--- /dev/null
+/**
+ * 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.gardena.internal.util;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Some String operations from commons lang.
+ *
+ * @author Gerhard Riegler - Initial contribution
+ */
+@NonNullByDefault
+public class StringUtils {
+ /**
+ * Gets the substring before the first occurrence of a separator.
+ */
+ public static @Nullable String substringBefore(@Nullable String str, String separator) {
+ if (str != null && !str.isEmpty()) {
+ int pos = str.indexOf(separator);
+ return pos == -1 ? str : str.substring(0, pos);
+ } else {
+ return str;
+ }
+ }
+
+ /**
+ * Gets the substring before the last occurrence of a separator.
+ */
+ public static @Nullable String substringBeforeLast(@Nullable String str, String separator) {
+ if (str != null && !str.isEmpty()) {
+ int pos = str.lastIndexOf(separator);
+ return pos == -1 ? str : str.substring(0, pos);
+ } else {
+ return str;
+ }
+ }
+
+ /**
+ * Gets the substring after the last occurrence of a separator.
+ */
+ public static @Nullable String substringAfterLast(@Nullable String str, String separator) {
+ if (str != null && !str.isEmpty()) {
+ int pos = str.lastIndexOf(separator);
+ return pos != -1 && pos != str.length() - separator.length() ? str.substring(pos + separator.length()) : "";
+ } else {
+ return str;
+ }
+ }
+}
import java.util.ArrayList;
import java.util.List;
+import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.gardena.internal.handler.GardenaDeviceConfig;
-import org.openhab.binding.gardena.internal.model.Device;
+import org.openhab.binding.gardena.internal.model.dto.Device;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
*
* @author Gerhard Riegler - Initial contribution
*/
+@NonNullByDefault
public class UidUtils {
/**
* Generates the ThingUID for the given device in the given account.
*/
public static ThingUID generateThingUID(Device device, Bridge account) {
- ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, device.getCategory());
- return new ThingUID(thingTypeUID, account.getUID(), device.getId());
+ ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, device.deviceType);
+ return new ThingUID(thingTypeUID, account.getUID(), device.id);
}
/**
if (deviceId == null) {
deviceId = thing.getUID().getId();
}
- if (deviceId.equals(device.getId())) {
+ if (deviceId.equals(device.id)) {
thingUIDs.add(thing.getUID());
}
}
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>Gardena Smart Home Binding</name>
- <description>This is the binding for Gardena Smart Home.</description>
+ <name>Gardena Smart System Binding</name>
+ <description>This is the binding for Gardena smart system.</description>
</binding:binding>
# binding
-binding.gardena.description=Das ist das Binding für Gardena Smart Home Produkte.
+binding.gardena.description=Das ist das Binding für Gardena Smart System.
# thing types
-thing-type.gardena.bridge.description=ein Gardena Smart Home Online Account
+thing-type.gardena.bridge.description=Ein Gardena Smart System Online Account
-thing-type.config.gardena.bridge.email.description=Email Addresse für den Gardena Smart Home Login
-thing-type.config.gardena.bridge.password.label=Passwort
-thing-type.config.gardena.bridge.password.description=Passwort für den Gardena Smart Home Login
-thing-type.config.gardena.bridge.sessionTimeout.description=Timeout der Gardena Smart Home Session in Minuten
+thing-type.config.gardena.bridge.email.description=Email-Addresse für den Gardena Smart System Login
+thing-type.config.gardena.bridge.password.label=Passwort
+thing-type.config.gardena.bridge.password.description=Passwort für den Gardena Smart System Login
thing-type.config.gardena.bridge.connectionTimeout.label=Verbindungstimeout
-thing-type.config.gardena.bridge.connectionTimeout.description=Timeout des Verbindungsaufbaus zu Gardena Smart Home in Sekunden
-thing-type.config.gardena.bridge.refresh.label=Refreshintervall
-thing-type.config.gardena.bridge.refresh.description=Der Intervall für die Datenabholung von Gardena Smart Home in Sekunden
+thing-type.config.gardena.bridge.connectionTimeout.description=Timeout des Verbindungsaufbaus zum Gardena Smart System in Sekunden
+thing-type.config.gardena.bridge.apiKey.description=Der Gardena Smart System Integrations API Schlüssel
-thing-type.gardena.mower.label=Gardena Smart Sileno Mäher
-thing-type.gardena.mower.description=ein Gardena Smart Sileno Mäher
+thing-type.gardena.mower.label=Gardena Smart SILENO Mäher
+thing-type.gardena.mower.description=Ein Gardena Smart SILENO Mäher
-thing-type.gardena.watering_computer.label=Gardena Smart Bewässerungscomputer
-thing-type.gardena.watering_computer.description=ein Gardena Smart Bewässerungscomputer
+thing-type.gardena.water_control.label=Gardena Smart Bewässerungscomputer
+thing-type.gardena.water_control.description=Ein Gardena Smart Bewässerungscomputer
thing-type.gardena.sensor.label=Gardena Smart Sensor
-thing-type.gardena.sensor.description=ein Gardena Smart Sensor
+thing-type.gardena.sensor.description=Ein Gardena Smart Sensor
-thing-type.gardena.electronic_pressure_pump.label=Gardena Smart Druckpumpe
-thing-type.gardena.electronic_pressure_pump.description=Eine Gardena Smart Druckpumpe
+thing-type.gardena.pump.label=Gardena Smart Druckpumpe
+thing-type.gardena.pump.description=Eine Gardena Smart Druckpumpe
thing-type.gardena.power.label=Gardena Smart Power Zwischenstecker
-thing-type.gardena.power.description=Eine Gardena Smart Power Zwischenstecker
-
-thing-type.gardena.ic24.label=Gardena Smart Bewässerungssteuerung
-thing-type.gardena.ic24.description=Eine Gardena Smart Bewässerungssteuerung
-
-# config
-thing-type.config.gardena.electronic_pressure_pump.leakage_detection.label=Leckage-Erkennung
-thing-type.config.gardena.electronic_pressure_pump.leakage_detection.state.option.watering=Bewässerung
-thing-type.config.gardena.electronic_pressure_pump.leakage_detection.state.option.washing_machine=Waschmaschine
-thing-type.config.gardena.electronic_pressure_pump.leakage_detection.state.option.domestic_water_supply=Häusliche Wasserversorgung
-thing-type.config.gardena.electronic_pressure_pump.leakage_detection.state.option.off=Aus
-
-thing-type.config.gardena.electronic_pressure_pump.operating_mode.label=Betriebsmodus
-thing-type.config.gardena.electronic_pressure_pump.operating_mode.state.option.automatic=Automatisch
-thing-type.config.gardena.electronic_pressure_pump.operating_mode.state.option.scheduled=Zeitgesteuert
-
-thing-type.config.gardena.electronic_pressure_pump.turn_on_pressure.label=Einschaltdruck
+thing-type.gardena.power.description=Ein Gardena Smart Power Zwischenstecker
+
+thing-type.gardena.irrigation_control.label=Gardena Smart Bewässerungssteuerung
+thing-type.gardena.irrigation_control.description=Eine Gardena Smart Bewässerungssteuerung
+
+thing-type.gardena.irrigation_control.group.valveOne.label=Ventil 1 Eigenschaften
+thing-type.gardena.irrigation_control.group.valveOne_commands.label=Ventil 1 Befehle
+thing-type.gardena.irrigation_control.group.valveTwo.label=Ventil 2 Eigenschaften
+thing-type.gardena.irrigation_control.group.valveTwo_commands.label=Ventil 2 Befehle
+thing-type.gardena.irrigation_control.group.valveThree.label=Ventil 3 Eigenschaften
+thing-type.gardena.irrigation_control.group.valveThree_commands.label=Ventil 3 Befehle
+thing-type.gardena.irrigation_control.group.valveFour.label=Ventil 4 Eigenschaften
+thing-type.gardena.irrigation_control.group.valveFour_commands.label=Ventil 4 Befehle
+thing-type.gardena.irrigation_control.group.valveFive.label=Ventil 5 Eigenschaften
+thing-type.gardena.irrigation_control.group.valveFive_commands.label=Ventil 5 Befehle
+thing-type.gardena.irrigation_control.group.valveSix.label=Ventil 6 Eigenschaften
+thing-type.gardena.irrigation_control.group.valveSix_commands.label=Ventil 6 Befehle
# channel group
-channel-group-type.gardena.deviceInfo.label=Geräteinfo
-channel-group-type.gardena.deviceInfo.description=Informationen über das Gerät
-
-channel-group-type.gardena.rechargeableBattery.label=Wiederaufladbare Batterie Info
-channel-group-type.gardena.rechargeableBattery.description=Informationen über die wiederaufladbare Batterie
-
-channel-group-type.gardena.radio.label=Funkverbindung
-channel-group-type.gardena.radio.description=Informationen über die Funkverbindung
-
-channel-group-type.gardena.mower.label=Mähroboter
-channel-group-type.gardena.mower.description=Informationen über den Mähroboter
-
-channel-group-type.gardena.internalTemperature.label=Interne Temperatur
-channel-group-type.gardena.internalTemperature.description=Die interne Temperatur des Mähroboters
-
-channel-group-type.gardena.battery.label=Batterieinfo
-channel-group-type.gardena.battery.description=Informationen über die Batterie
-
-channel-group-type.gardena.outlet.label=Wasserauslass
-channel-group-type.gardena.outlet.description=Informationen über den Wasserauslass
-
-channel-group-type.gardena.ambientTemperature.label=Umgebungstemperatur
-channel-group-type.gardena.ambientTemperature.description=Informationen über die Umgebungstemperatur
-
-channel-group-type.gardena.soilTemperature.label=Bodentemperatur
-channel-group-type.gardena.soilTemperature.description=Die Temperatur des Bodens
-
-channel-group-type.gardena.humidity.label=Feuchtigkeit
-channel-group-type.gardena.humidity.description=Die Feuchtigkeit
-
-channel-group-type.gardena.light.label=Helligkeit
-channel-group-type.gardena.light.description=Die Helligkeit
-
-channel-group-type.gardena.firmware.label=Firmware
-channel-group-type.gardena.firmware.description=Informationen über die Firmware
-
-channel-group-type.gardena.outletTemperature.label=Auslauftemperatur
-channel-group-type.gardena.outletTemperature.description=Die Auslauftemperatur
-
-channel-group-type.gardena.pump.label=Pumpe
-channel-group-type.gardena.pump.description=Die Pumpe
-
-channel-group-type.gardena.outletPressure.label=Auslassdruck
-channel-group-type.gardena.outletPressure.description=Der Auslassdruck
-
-channel-group-type.gardena.outletPressureMax.label=Maximum Auslassdruck
-channel-group-type.gardena.outletPressureMax.description=Der maximale Auslassdruck
-
-channel-group-type.gardena.flow.label=Förderung
-channel-group-type.gardena.flow.description=Die Förderung
-
-channel-group-type.gardena.manualWatering.label=Manuelle Bewässerung
-channel-group-type.gardena.manualWatering.description=Die manuelle Bewässerung
-
-channel-group-type.gardena.scheduling.label=Zeitplan
-channel-group-type.gardena.scheduling.description=Der Zeitplan
-
-channel-group-type.gardena.mowerStats.label=Mähroboter Statistiken
-channel-group-type.gardena.mowerStats.description=Statistiken über den Mähroboter
-
-channel-group-type.gardena.power.label=Energie
-channel-group-type.gardena.power.description=Energie
-
-channel-group-type.gardena.watering.label=Bewässerung
-channel-group-type.gardena.watering.description=Die Bewässerung
-
-channel-group-type.gardena.ic24.label=Bewässerungssteuerung
-channel-group-type.gardena.ic24.description=Die Bewässerungssteuerung
-
-channel-group-type.gardena.ic24scheduling.label=Zeitplanung
-channel-group-type.gardena.ic24scheduling.description=Die Zeitplanung
+channel-group-type.gardena.valveSetProperties.label=Ventilsatz Eigenschaften
+channel-group-type.gardena.valveSetProperties.description=Eigenschaften eines Ventilsatzes
+
+channel-group-type.gardena.valveSetProperties.channel.state_timestamp.label=Status Zeitstempel
+channel-group-type.gardena.valveSetProperties.channel.lastErrorCode_timestamp.label=Letzte Fehlermeldung Zeitstempel
+
+channel-group-type.gardena.valveProperties.label=Ventil Eigenschaften
+channel-group-type.gardena.valveProperties.description=Eigenschaften eines Ventils
+channel-group-type.gardena.valveProperties.channel.state_timestamp.label=Status Zeitstempel
+channel-group-type.gardena.valveProperties.channel.activity_timestamp.label=Aktivität Zeitstempel
+channel-group-type.gardena.valveProperties.channel.lastErrorCode_timestamp.label=Letzte Fehlermeldung Zeitstempel
+
+channel-group-type.gardena.sensorProperties.label=Sensor Eigenschaften
+channel-group-type.gardena.sensorProperties.description=Eigenschaften eines Sensors
+channel-group-type.gardena.sensorProperties.channel.soilHumidity_timestamp.label=Bodenfeuchtigkeit Zeitstempel
+channel-group-type.gardena.sensorProperties.channel.soilTemperature.label=Bodentemperatur
+channel-group-type.gardena.sensorProperties.channel.soilTemperature_timestamp.label=Bodentemperatur Zeitstempel
+channel-group-type.gardena.sensorProperties.channel.ambientTemperature.label=Umgebungstemperatur
+channel-group-type.gardena.sensorProperties.channel.ambientTemperature_timestamp.label=Umgebungstemperatur Zeitstempel
+channel-group-type.gardena.sensorProperties.channel.lightIntensity_timestamp.label=Lichtintensität Zeitstempel
+
+channel-group-type.gardena.powerSocketProperties.label=Zwischenstecker Eigenschaften
+channel-group-type.gardena.powerSocketProperties.description=Eigenschaften eines Zwischensteckers
+channel-group-type.gardena.powerSocketProperties.channel.state_timestamp.label=Status Zeitstempel
+channel-group-type.gardena.powerSocketProperties.channel.activity_timestamp.label=Aktivität Zeitstempel
+channel-group-type.gardena.powerSocketProperties.channel.lastErrorCode_timestamp.label=Letzte Fehlermeldung Zeitstempel
+channel-group-type.gardena.powerSocketProperties.channel.duration_timestamp.label=Dauer Zeitstempel
+
+channel-group-type.gardena.mowerProperties.label=Mäher Eigenschaften
+channel-group-type.gardena.mowerProperties.description=Eigenschaften eines Mähers
+channel-group-type.gardena.mowerProperties.channel.state_timestamp.label=Status Zeitstempel
+channel-group-type.gardena.mowerProperties.channel.activity_timestamp.label=Aktivität Zeitstempel
+channel-group-type.gardena.mowerProperties.channel.lastErrorCode_timestamp.label=Letzte Fehlermeldung Zeitstempel
+
+channel-group-type.gardena.commonProperties.label=Gemeinsame Eigenschaften
+channel-group-type.gardena.commonProperties.description=Eigenschaften, die bei allen Geräten gleich sind
+channel-group-type.gardena.commonProperties.channel.batteryLevel_timestamp.label=Batterie Zeitstempel
+channel-group-type.gardena.commonProperties.channel.batteryState_timestamp.label=Batteriestatus Zeitstempel
+channel-group-type.gardena.commonProperties.channel.rfLinkLevel_timestamp.label=Signalstärke Zeitstempel
+channel-group-type.gardena.commonProperties.channel.rfLinkState_timestamp.label=Verbindungsstatus Zeitstempel
+channel-group-type.gardena.commonProperties.channel.lastUpdate_timestamp.label=Letzte Aktualisierung Zeitstempel
+
+channel-group-type.gardena.mowerCommands.label=Mäher Befehle
+channel-group-type.gardena.mowerCommands.description=Befehle für einen Mäher
+channel-group-type.gardena.mowerCommands.channel.commandDuration.label=Mähdauer
+channel-group-type.gardena.mowerCommands.channel.start_seconds_to_override.label=Starte Mähen mit Dauer
+channel-group-type.gardena.mowerCommands.channel.start_seconds_to_override.description=Manueller Betrieb, verwendet Mähdauer um die Dauer zu definieren
+channel-group-type.gardena.mowerCommands.channel.start_dont_override.label=Zeitplan Starten
+channel-group-type.gardena.mowerCommands.channel.start_dont_override.description=Automatischer Betrieb
+channel-group-type.gardena.mowerCommands.channel.park_until_next_task.label=Parken
+channel-group-type.gardena.mowerCommands.channel.park_until_next_task.description=Abbruch der aktuellen Operation und Rückkehr zur Ladestation
+channel-group-type.gardena.mowerCommands.channel.park_until_further_notice.label=Zeitplan Pausieren
+channel-group-type.gardena.mowerCommands.channel.park_until_further_notice.description=Abbruch der aktuellen Operation, Rückkehr zur Ladestation, Zeitplan ignorieren
+
+channel-group-type.gardena.valveCommands.label=Ventil Befehle
+channel-group-type.gardena.valveCommands.description=Befehle für ein Ventil
+channel-group-type.gardena.valveCommands.channel.commandDuration.label=Bewässerungsdauer
+channel-group-type.gardena.valveCommands.channel.start_seconds_to_override.label=Starte Bewässerung mit Dauer
+channel-group-type.gardena.valveCommands.channel.start_seconds_to_override.description=Manueller Betrieb, verwendet Bewässerunsdauer um die Dauer zu definieren
+channel-group-type.gardena.valveCommands.channel.stop_until_next_task.label=Ventil Schließen, Zeitplan Fortfahren
+channel-group-type.gardena.valveCommands.channel.stop_until_next_task.description=Die aktuelle Bewässerung abbrechen, mit dem Zeitplan fortfahren
+channel-group-type.gardena.valveCommands.channel.pause.label=Pause bis zur angegebenen Zeit
+channel-group-type.gardena.valveCommands.channel.pause.description=Den Automatikbetrieb bis zur angegebenen Zeit überspringen. Der derzeit aktive Vorgang kann abgebrochen werden oder auch nicht (abhängig vom Gerätemodell)
+channel-group-type.gardena.valveCommands.channel.unpause.label=Zeitplan Starten
+channel-group-type.gardena.valveCommands.channel.unpause.description=Wiederherstellen des automatischen Betriebs, wenn er angehalten wurde
+
+channel-group-type.gardena.valveSetCommands.label=Ventilsatz Befehle
+channel-group-type.gardena.valveSetCommands.description=Befehle für einen Ventilsatz
+channel-group-type.gardena.valveSetCommands.channel.stop_until_next_task.label=Alle Ventile Schließen
+channel-group-type.gardena.valveSetCommands.channel.stop_until_next_task.description=Alle Ventile sofort schließen
+
+channel-group-type.gardena.powerSocketCommands.label=Zwischenstecker Befehle
+channel-group-type.gardena.powerSocketCommands.description=Befehle für einen Zwischenstecker
+channel-group-type.gardena.powerSocketCommands.channel.commandDuration.label=Einschaltdauer
+channel-group-type.gardena.powerSocketCommands.channel.start_seconds_to_override.label=Einschalten mit Dauer
+channel-group-type.gardena.powerSocketCommands.channel.start_seconds_to_override.description=Manueller Betrieb, verwendet Einschaltdauer um die Dauer zu definieren
+channel-group-type.gardena.powerSocketCommands.channel.start_override.label=Manuelles Einschalten
+channel-group-type.gardena.powerSocketCommands.channel.start_override.description=Manuelles Ein
+channel-group-type.gardena.powerSocketCommands.channel.stop_until_next_task.label=Aus, Weiter mit Zeitplan
+channel-group-type.gardena.powerSocketCommands.channel.stop_until_next_task.description=Sofort abschalten, mit dem Zeitplan fortfahren
+channel-group-type.gardena.powerSocketCommands.channel.pause.label=Pause bis zur angegebenen Zeit
+channel-group-type.gardena.powerSocketCommands.channel.pause.description=Automatischen Betrieb bis zur angegebenen Zeit überspringen. Die derzeit aktive Operation wird NICHT abgebrochen
+channel-group-type.gardena.powerSocketCommands.channel.unpause.label=Zeitplan Starten
+channel-group-type.gardena.powerSocketCommands.channel.unpause.description=Wiederherstellen des automatischen Betriebs, wenn er angehalten wurde
# channel types
-channel-type.gardena.mowerDurationProperty.label=Mähdauer
-channel-type.gardena.mowerDurationProperty.description=Mähdauer in Minuten
-
-channel-type.gardena.parkUntilFurtherNotice.label=Parken und pausieren
-channel-type.gardena.parkUntilFurtherNotice.description=Parken und alle Zeitpläne pausieren
-
-channel-type.gardena.parkUntilNextTimer.label=Parken
-channel-type.gardena.parkUntilNextTimer.description=Bis zum nächsten regulären Zeitplan parken
-
-channel-type.gardena.startResumeSchedule.label=Zeitpläne fortsetzen
-channel-type.gardena.startResumeSchedule.description=Alle Zeitpläne werden fortgesetze
-
-channel-type.gardena.startOverrideTimer.label=Manuelles mähen
-channel-type.gardena.startOverrideTimer.description=Sofort mähen für die eingestellte Mähdauer
-
-channel-type.gardena.lastTimeOnline.label=Zuletzt online
-channel-type.gardena.lastTimeOnline.description=Die Zeit wann das Gerät zuletzt online war
-
-channel-type.gardena.rechargeableBatteryStatus.label=Akkuzustand
-channel-type.gardena.rechargeableBatteryStatus.description=Der Zustand des Akkus
-channel-type.gardena.rechargeableBatteryStatus.state.option.weak=Schwach
-channel-type.gardena.rechargeableBatteryStatus.state.option.undefined=Undefiniert
-
-channel-type.gardena.charging.label=Batterie wird geladen
-channel-type.gardena.charging.description=Batterie wird geladen
-
-channel-type.gardena.radioQuality.label=Verbindungsqualität
-channel-type.gardena.radioQuality.description=Die Qualität der Funk Verbindung
-
-channel-type.gardena.connectionStatus.label=Verbindungsstatus
-channel-type.gardena.connectionStatus.description=Der Status der Verbindung
-channel-type.gardena.connectionStatus.state.option.status_device_unreachable=Nicht erreichbar
-channel-type.gardena.connectionStatus.state.option.status_device_alive=Erreichbar
-channel-type.gardena.connectionStatus.state.option.unknown=Unbekannt
-
-channel-type.gardena.radioState.label=Verbindungszustand
-channel-type.gardena.radioState.description=Der Zustand der Funk Verbindung
-channel-type.gardena.radioState.state.option.poor=Schlecht
-channel-type.gardena.radioState.state.option.good=Gut
-channel-type.gardena.radioState.state.option.excellent=Exzellent
-channel-type.gardena.radioState.state.option.undefined=Undefiniert
-
-channel-type.gardena.manualOperation.label=Manuelle Steuerung
-channel-type.gardena.manualOperation.description=Der Mäher wird manuell gesteuert
-
-channel-type.gardena.status.label=Mäherstatus
-channel-type.gardena.status.description=Der Status des Mähers
-channel-type.gardena.status.state.option.uninitialised=Nicht initialisiert
-channel-type.gardena.status.state.option.paused=Pausiert
-channel-type.gardena.status.state.option.ok_cutting=Mähen
-channel-type.gardena.status.state.option.ok_searching=Suche Ladestation
-channel-type.gardena.status.state.option.ok_charging=Lädt
-channel-type.gardena.status.state.option.ok_leaving=Mähen
-channel-type.gardena.status.state.option.wait_updating=Wird aktualisiert ...
-channel-type.gardena.status.state.option.wait_power_up=Wird eingeschaltet ...
-channel-type.gardena.status.state.option.parked_timer=Geparkt nach Zeitplan
-channel-type.gardena.status.state.option.parked_park_selected=Geparkt
-channel-type.gardena.status.state.option.off_disabled=Der Mäher ist ausgeschaltet
-channel-type.gardena.status.state.option.off_hatch_open=Deaktiviert. Abdeckung ist offen oder PIN-Code erforderlich
-channel-type.gardena.status.state.option.unknown=Unbekannter Status
-channel-type.gardena.status.state.option.error=Fehler
-channel-type.gardena.status.state.option.error_at_power_up=Neustart ...
-channel-type.gardena.status.state.option.off_hatch_closed=Deaktiviert. Manueller Start erforderlich
-channel-type.gardena.status.state.option.ok_cutting_timer_overridden=Manuelles Mähen
-channel-type.gardena.status.state.option.parked_autotimer=Geparkt durch SensorControl
-channel-type.gardena.status.state.option.parked_daily_limit_reached=Abgeschlossen
-channel-type.gardena.status.state.option.undefined=Undefiniert
-
-channel-type.gardena.error.label=Mäherfehler
-channel-type.gardena.error.description=Der Fehler des Mähers
-channel-type.gardena.error.state.option.no_message=Kein Fehler
-channel-type.gardena.error.state.option.outside_working_area=Außerhalb des Arbeitsbereichs
-channel-type.gardena.error.state.option.no_loop_signal=Kein Schleifensignal
-channel-type.gardena.error.state.option.wrong_loop_signal=Falsches Schleifensignal
-channel-type.gardena.error.state.option.loop_sensor_problem_front=Problem Schleifensensor, vorne
-channel-type.gardena.error.state.option.loop_sensor_problem_rear=Problem Schleifensensor, hinten
-channel-type.gardena.error.state.option.loop_sensor_problem_left=Problem Schleifensensor, links
-channel-type.gardena.error.state.option.loop_sensor_problem_right=Problem Schleifensensor, rechts
-channel-type.gardena.error.state.option.wrong_pin_code=Falscher PIN Code
-channel-type.gardena.error.state.option.trapped=Eingeschlossen
-channel-type.gardena.error.state.option.upside_down=Steht auf dem Kopf
-channel-type.gardena.error.state.option.low_battery=Niedriger Batteriestand
-channel-type.gardena.error.state.option.empty_battery=empty_battery
-channel-type.gardena.error.state.option.no_drive=no_drive
-channel-type.gardena.error.state.option.temporarily_lifted=Vorübergehend angehoben
-channel-type.gardena.error.state.option.lifted=Angehoben
-channel-type.gardena.error.state.option.stuck_in_charging_station=Eingeklemmt in Ladestation
-channel-type.gardena.error.state.option.charging_station_blocked=Ladestation blockiert
-channel-type.gardena.error.state.option.collision_sensor_problem_rear=Problem Stoßsensor hinten
-channel-type.gardena.error.state.option.collision_sensor_problem_front=Problem Stoßsensor vorne
-channel-type.gardena.error.state.option.wheel_motor_blocked_right=Radmotor rechts blockiert
-channel-type.gardena.error.state.option.wheel_motor_blocked_left=Radmotor links blockiert
-channel-type.gardena.error.state.option.wheel_drive_problem_right=Problem Antrieb, rechts
-channel-type.gardena.error.state.option.wheel_drive_problem_left=Problem Antrieb, links
-channel-type.gardena.error.state.option.cutting_motor_drive_defect=Schneidmotorantrieb defekt
-channel-type.gardena.error.state.option.cutting_system_blocked=Schneidsystem blockiert
-channel-type.gardena.error.state.option.invalid_sub_device_combination=Fehlerhafte Verbindung
-channel-type.gardena.error.state.option.settings_restored=Standardeinstellungen
-channel-type.gardena.error.state.option.memory_circuit_problem=Speicherschaltkreisproblem
-channel-type.gardena.error.state.option.slope_too_steep=Steigung zu steil
-channel-type.gardena.error.state.option.charging_system_problem=Problem Ladesystem
-channel-type.gardena.error.state.option.stop_button_problem=Stop button Problem
-channel-type.gardena.error.state.option.tilt_sensor_problem=Kippsensorproblem
-channel-type.gardena.error.state.option.mower_tilted=Mäher gekippt
-channel-type.gardena.error.state.option.wheel_motor_overloaded_right=Rechter Radmotor überlastet
-channel-type.gardena.error.state.option.wheel_motor_overloaded_left=Linker Radmotor überlastet
-channel-type.gardena.error.state.option.charging_current_too_high=Ladestrom zu hoch
-channel-type.gardena.error.state.option.electronic_problem=Elektronisches Problem
-channel-type.gardena.error.state.option.cutting_motor_problem=Schneidmotorisches Problem
-channel-type.gardena.error.state.option.limited_cutting_height_range=Begrenzter Schneidhöhenbereich
-channel-type.gardena.error.state.option.unexpected_cutting_height_adj=Unerwartete Schnitthöhe
-channel-type.gardena.error.state.option.cutting_height_problem_drive=Schnitthöhenproblem Antrieb
-channel-type.gardena.error.state.option.cutting_height_problem_curr=Schnitthöhenproblem
-channel-type.gardena.error.state.option.cutting_height_problem_dir=Schnitthöhenproblem
-channel-type.gardena.error.state.option.cutting_height_blocked=Schnitthöhe blockiert
-channel-type.gardena.error.state.option.cutting_height_problem=Schnitthöhenproblem
-channel-type.gardena.error.state.option.no_response_from_charger=Keine Antwort vom Ladegerät
-channel-type.gardena.error.state.option.ultrasonic_problem=Ultraschallproblem
-channel-type.gardena.error.state.option.temporary_problem=Vorübergehendes Problem
-channel-type.gardena.error.state.option.guide_1_not_found=SK 1 nicht gefunden
-channel-type.gardena.error.state.option.guide_2_not_found=SK 2 nicht gefunden
-channel-type.gardena.error.state.option.guide_3_not_found=SK 3 nicht gefunden
-channel-type.gardena.error.state.option.gps_tracker_module_error=GPS tracker Modul Fehler
-channel-type.gardena.error.state.option.weak_gps_signal=Schwaches GPS Signal
-channel-type.gardena.error.state.option.difficult_finding_home=Problem die Ladestation zu finden
-channel-type.gardena.error.state.option.guide_calibration_accomplished=Kalibration des Suchkabels beendet
-channel-type.gardena.error.state.option.guide_calibration_failed=Kalibration des Suchkabels fehlgeschlagen
-channel-type.gardena.error.state.option.temporary_battery_problem=Kurzzeitiges Batterieproblem
-channel-type.gardena.error.state.option.battery_problem=Batterieproblem
-channel-type.gardena.error.state.option.too_many_batteries=Zu viele Batterien
-channel-type.gardena.error.state.option.alarm_mower_switched_off=Alarm! Mäher ausgeschalten
-channel-type.gardena.error.state.option.alarm_mower_stopped=Alarm! Mäher gestoppt
-channel-type.gardena.error.state.option.alarm_mower_lifted=Alarm! Mäher angehoben
-channel-type.gardena.error.state.option.alarm_mower_tilted=Alarm! Mäher gekippt
-channel-type.gardena.error.state.option.alarm_mower_in_motion=Alarm! Mäher in Bewegung
-channel-type.gardena.error.state.option.alarm_outside_geofence=Alarm! Außerhalb des Geofence
-channel-type.gardena.error.state.option.connection_changed=Verbindung geändert
-channel-type.gardena.error.state.option.connection_not_changed=Verbindung nicht geändert
-channel-type.gardena.error.state.option.com_board_not_available=COM board nicht verfügbar
-channel-type.gardena.error.state.option.slipped=Rutscht
-channel-type.gardena.error.state.option.invalid_battery_combination=Ungültige Batterie Kombination
-channel-type.gardena.error.state.option.imbalanced_cutting_disc=Unwuchte Schneidscheibe
-channel-type.gardena.error.state.option.safety_function_faulty=Sicherheitsfunktion fehlerhaft
-
-channel-type.gardena.sourceForNextStart.label=Quelle für den nächsten Start
-channel-type.gardena.sourceForNextStart.description=Die Quelle für den nächsten Start
-channel-type.gardena.sourceForNextStart.state.option.no_source=Keine Quelle
-channel-type.gardena.sourceForNextStart.state.option.completed_cutting_daily_limit=Tägliches Mählimit erreicht
-channel-type.gardena.sourceForNextStart.state.option.week_timer=Wochen timer
-channel-type.gardena.sourceForNextStart.state.option.countdown_timer=Countdown timer
-channel-type.gardena.sourceForNextStart.state.option.mower_charging=Mäher lädt
-channel-type.gardena.sourceForNextStart.state.option.completed_cutting_autotimer=Fertig gemäht
-channel-type.gardena.sourceForNextStart.state.option.undefined=Undefiniert
-
-channel-type.gardena.timestampNextStart.label=Nächster Startzeitpunkt
-channel-type.gardena.timestampNextStart.description=Zeitpunkt des nächsten Starts
-
-channel-type.gardena.overrideEndTime.label=Überschreiben der Endzeit
-channel-type.gardena.overrideEndTime.description=Überschreiben der Endzeit
+channel-type.gardena.gardenaCommand.label=Befehl
+channel-type.gardena.gardenaCommand.description=Ein Befehl für ein Gerät
+
+channel-type.gardena.powerCommandDuration.label=Einschaltdauer
+channel-type.gardena.powerCommandDuration.description=Die Einschaltdauer in Minuten
+
+channel-type.gardena.valveCommandDuration.label=Einschaltdauer
+channel-type.gardena.valveCommandDuration.description=Die Einschaltdauer in Minuten
+
+channel-type.gardena.mowerCommandDuration.label=Einschaltdauer
+channel-type.gardena.mowerCommandDuration.description=Die Einschaltdauer in Minuten
+
+channel-type.gardena.state.label=Status
+channel-type.gardena.state.description=Der Gerätestatus
+
+channel-type.gardena.state.option.WARNING=Warnung
+channel-type.gardena.state.state.option.ERROR=Fehler
+channel-type.gardena.state.state.option.UNAVAILABLE=Nicht verfügbar
+
+channel-type.gardena.lastErrorCode.label=Letzte Fehlermeldung
+channel-type.gardena.lastErrorCode.description=Die letzte Fehlermeldung
+
+channel-type.gardena.lastErrorCode.state.option.UNKNOWN=Unbekannt
+channel-type.gardena.lastErrorCode.state.option.NO_MESSAGE=Kein Fehler
+channel-type.gardena.lastErrorCode.state.option.OUTSIDE_WORKING_AREA=Außerhalb des Arbeitsbereichs
+channel-type.gardena.lastErrorCode.state.option.NO_LOOP_SIGNAL=Kein Schleifensignal
+channel-type.gardena.lastErrorCode.state.option.WRONG_LOOP_SIGNAL=Falsches Schleifensignal
+channel-type.gardena.lastErrorCode.state.option.LOOP_SENSOR_PROBLEM_FRONT=Problem Schleifensensor, vorne
+channel-type.gardena.lastErrorCode.state.option.LOOP_SENSOR_PROBLEM_REAR=Problem Schleifensensor, hinten
+channel-type.gardena.lastErrorCode.state.option.LOOP_SENSOR_PROBLEM_LEFT=Problem Schleifensensor, links
+channel-type.gardena.lastErrorCode.state.option.LOOP_SENSOR_PROBLEM_RIGHT=Problem Schleifensensor, rechts
+channel-type.gardena.lastErrorCode.state.option.WRONG_PIN_CODE=Falscher PIN Code
+channel-type.gardena.lastErrorCode.state.option.TRAPPED=Eingeschlossen
+channel-type.gardena.lastErrorCode.state.option.UPSIDE_DOWN=Steht auf dem Kopf
+channel-type.gardena.lastErrorCode.state.option.EMPTY_BATTERY=Leere Batteriestand
+channel-type.gardena.lastErrorCode.state.option.NO_DRIVE=Kein Antrieb
+channel-type.gardena.lastErrorCode.state.option.TEMPORARILY_LIFTED=Vorübergehend angehoben
+channel-type.gardena.lastErrorCode.state.option.LIFTED=Angehoben
+channel-type.gardena.lastErrorCode.state.option.STUCK_IN_CHARGING_STATION=In Ladestation eingeklemmt
+channel-type.gardena.lastErrorCode.state.option.CHARGING_STATION_BLOCKED=Ladestation blockiert
+channel-type.gardena.lastErrorCode.state.option.COLLISION_SENSOR_PROBLEM_REAR=Problem Stoßsensor hinten
+channel-type.gardena.lastErrorCode.state.option.COLLISION_SENSOR_PROBLEM_FRONT=Problem Stoßsensor vorne
+channel-type.gardena.lastErrorCode.state.option.WHEEL_MOTOR_BLOCKED_RIGHT=Radmotor rechts blockiert
+channel-type.gardena.lastErrorCode.state.option.WHEEL_MOTOR_BLOCKED_LEFT=Radmotor links blockiert
+channel-type.gardena.lastErrorCode.state.option.WHEEL_DRIVE_PROBLEM_RIGHT=Problem Antrieb, rechts
+channel-type.gardena.lastErrorCode.state.option.WHEEL_DRIVE_PROBLEM_LEFT=Problem Antrieb, links
+channel-type.gardena.lastErrorCode.state.option.CUTTING_MOTOR_DRIVE_DEFECT=Schneidmotorantrieb defekt
+channel-type.gardena.lastErrorCode.state.option.CUTTING_SYSTEM_BLOCKED=Schneidsystem blockiert
+channel-type.gardena.lastErrorCode.state.option.INVALID_SUB_DEVICE_COMBINATION=Fehlerhafte Verbindung
+channel-type.gardena.lastErrorCode.state.option.MEMORY_CIRCUIT_PROBLEM=Speicherschaltkreisproblem
+channel-type.gardena.lastErrorCode.state.option.CHARGING_SYSTEM_PROBLEM=Problem Ladesystem
+channel-type.gardena.lastErrorCode.state.option.STOP_BUTTON_PROBLEM=Stop button Problem
+channel-type.gardena.lastErrorCode.state.option.TILT_SENSOR_PROBLEM=Kippsensorproblem
+channel-type.gardena.lastErrorCode.state.option.MOWER_TILTED=Mäher gekippt
+channel-type.gardena.lastErrorCode.state.option.WHEEL_MOTOR_OVERLOADED_RIGHT=Rechter Radmotor überlastet
+channel-type.gardena.lastErrorCode.state.option.WHEEL_MOTOR_OVERLOADED_LEFT=Linker Radmotor überlastet
+channel-type.gardena.lastErrorCode.state.option.CHARGING_CURRENT_TOO_HIGH=Ladestrom zu hoch
+channel-type.gardena.lastErrorCode.state.option.ELECTRONIC_PROBLEM=Elektronisches Problem
+channel-type.gardena.lastErrorCode.state.option.CUTTING_MOTOR_PROBLEM=Schneidmotorisches Problem
+channel-type.gardena.lastErrorCode.state.option.LIMITED_CUTTING_HEIGHT_RANGE=Begrenzter Schneidhöhenbereich
+channel-type.gardena.lastErrorCode.state.option.CUTTING_HEIGHT_PROBLEM_DRIVE=Schnitthöhenproblem Antrieb
+channel-type.gardena.lastErrorCode.state.option.CUTTING_HEIGHT_PROBLEM_CURR=Schnitthöhenproblem
+channel-type.gardena.lastErrorCode.state.option.CUTTING_HEIGHT_PROBLEM_DIR=Schnitthöhenproblem Richtung
+channel-type.gardena.lastErrorCode.state.option.CUTTING_HEIGHT_BLOCKED=Schnitthöhe blockiert
+channel-type.gardena.lastErrorCode.state.option.CUTTING_HEIGHT_PROBLEM=Schnitthöhenproblem
+channel-type.gardena.lastErrorCode.state.option.BATTERY_PROBLEM=Batterieproblem
+channel-type.gardena.lastErrorCode.state.option.TOO_MANY_BATTERIES=Zu viele Batterien
+channel-type.gardena.lastErrorCode.state.option.ALARM_MOWER_SWITCHED_OFF=Alarm! Mäher ausgeschalten
+channel-type.gardena.lastErrorCode.state.option.ALARM_MOWER_STOPPED=Alarm! Mäher gestoppt
+channel-type.gardena.lastErrorCode.state.option.ALARM_MOWER_LIFTED=Alarm! Mäher angehoben
+channel-type.gardena.lastErrorCode.state.option.ALARM_MOWER_TILTED=Alarm! Mäher gekippt
+channel-type.gardena.lastErrorCode.state.option.ALARM_MOWER_IN_MOTION=Alarm! Mäher in Bewegung
+channel-type.gardena.lastErrorCode.state.option.ALARM_OUTSIDE_GEOFENCE=Alarm! Außerhalb des Geofence
+channel-type.gardena.lastErrorCode.state.option.SLIPPED=Mäher rutscht
+channel-type.gardena.lastErrorCode.state.option.INVALID_BATTERY_COMBINATION=Ungültige Batterie Kombination
+channel-type.gardena.lastErrorCode.state.option.UNINITIALISED=Unbekannter Status des Mähers
+channel-type.gardena.lastErrorCode.state.option.WAIT_UPDATING=Wird aktualisiert ...
+channel-type.gardena.lastErrorCode.state.option.WAIT_POWER_UP=Wird eingeschaltet ...
+channel-type.gardena.lastErrorCode.state.option.OFF_DISABLED=Der Mäher ist ausgeschaltet
+channel-type.gardena.lastErrorCode.state.option.OFF_HATCH_OPEN=Deaktiviert. Abdeckung ist offen oder PIN-Code erforderlich
+channel-type.gardena.lastErrorCode.state.option.OFF_HATCH_CLOSED=Deaktiviert. Manueller Start erforderlich
+channel-type.gardena.lastErrorCode.state.option.PARKED_DAILY_LIMIT_REACHED=Mähwerk hat den Mähvorgang aufgrund der erreichten Tagesgrenze abgeschlossen
+channel-type.gardena.lastErrorCode.state.option.TIMER_CANCELLED=Timer abgebrochen
+channel-type.gardena.lastErrorCode.state.option.CONCURRENT_LIMIT_REACHED=Ventil kann nicht geöffnet werden, weil höchstens 2 Ventile gleichzeitig geöffnet sein können
+channel-type.gardena.lastErrorCode.state.option.NOT_CONNECTED=Es wurde kein Ventil angeschlossen
+channel-type.gardena.lastErrorCode.state.option.VALVE_CURRENT_MAX_EXCEEDED=Das Ventil wurde geschlossen, weil das Ventil mehr Strom als erlaubt abzieht
+channel-type.gardena.lastErrorCode.state.option.TOTAL_CURRENT_MAX_EXCEEDED=Das Ventil wurde geschlossen, weil der verwendete Gesamtstrom über dem zulässigen Maximum lag.
+channel-type.gardena.lastErrorCode.state.option.WATERING_CANCELED=Bewässerung wurde abgebrochen
+channel-type.gardena.lastErrorCode.state.option.MASTER_VALVE=Hauptventil ist nicht angeschlossen
+channel-type.gardena.lastErrorCode.state.option.WATERING_DURATION_TOO_SHORT=Bewässerungsdauer zu kurz
+channel-type.gardena.lastErrorCode.state.option.VALVE_BROKEN=Die elektrische Verbindung zum Ventil ist unterbrochen, oder der Induktor ist beschädigt
+channel-type.gardena.lastErrorCode.state.option.FROST_PREVENTS_STARTING=Wegen Frost bleibt das Ventil geschlossen
+channel-type.gardena.lastErrorCode.state.option.LOW_BATTERY_PREVENTS_STARTING=Wegen niedriger Batterie bleibt das Ventil geschlossen
+channel-type.gardena.lastErrorCode.state.option.VALVE_POWER_SUPPLY_FAILED=Stromversorgung ausgefallen
+channel-type.gardena.lastErrorCode.state.option.VOLTAGE_DROP=Es wurde ein Spannungsabfall an der Stromversorgung festgestellt
+channel-type.gardena.lastErrorCode.state.option.WRONG_POWER_SUPPLY=Falsches Netzteil angeschlossen
+channel-type.gardena.lastErrorCode.state.option.NO_MCU_CONNECTION=Kommunikation mit sekundärer MCU nicht möglich
+
+channel-type.gardena.timestamp.label=Zeitstempel
+channel-type.gardena.timestamp.description=Zeitstempel
+channel-type.gardena.timestampRefresh.label=Zeitstempel
+channel-type.gardena.timestampRefresh.description=Zeitstempel
+
+channel-type.gardena.name.label=Name
+channel-type.gardena.name.description=Der Name des Gerätes
+
+channel-type.gardena.activity.label=Aktivität
+channel-type.gardena.activity.description=Die Aktivität des Gerätes
+channel-type.gardena.activity.state.option.OFF=Aus
+channel-type.gardena.activity.state.option.FOREVER_ON=Einschalten durch eine manuelle Aktion, kein Ausschalten geplant
+channel-type.gardena.activity.state.option.TIME_LIMITED_ON=Einschalten durch eine manuelle Aktion, das Ausschalten ist geplant
+channel-type.gardena.activity.state.option.SCHEDULED_ON=Betrieb nach Zeitplan, aktueller Stand ist 'ein'
+channel-type.gardena.activity.state.option.CLOSED=Ventil geschlossen
+channel-type.gardena.activity.state.option.MANUAL_WATERING=Bewässerung aktiv
+channel-type.gardena.activity.state.option.SCHEDULED_WATERING=Bewässerung aktiv
+channel-type.gardena.activity.state.option.PAUSED=In einem Wartezustand mit geschlossener Luke
+channel-type.gardena.activity.state.option.OK_CUTTING=Mähen
+channel-type.gardena.activity.state.option.OK_CUTTING_TIMER_OVERRIDDEN=Mähen
+channel-type.gardena.activity.state.option.OK_SEARCHING=Suche Ladestation
+channel-type.gardena.activity.state.option.OK_LEAVING=Verlasse Ladestation
+channel-type.gardena.activity.state.option.OK_CHARGING=Lädt
+channel-type.gardena.activity.state.option.PARKED_TIMER=Nach Timer geparkt
+channel-type.gardena.activity.state.option.PARKED_PARK_SELECTED=Bis auf weiteres geparkt
+channel-type.gardena.activity.state.option.PARKED_AUTOTIMER=Wegen zu geringer Grashöhe geparkt
+channel-type.gardena.activity.state.option.NONE=Keine
+
+channel-type.gardena.duration.label=Dauer
+channel-type.gardena.duration.description=Die Dauer in Minuten
+
+channel-type.gardena.soilHumidity.label=Bodenfeuchtigkeit
+channel-type.gardena.soilHumidity.description=Die Bodenfeuchtigkeit in Prozent
+
+channel-type.gardena.lightIntensity.label=Lichtintensität
+channel-type.gardena.lightIntensity.description=Die Lichtintensität in Lux
channel-type.gardena.temperature.label=Temperatur
-channel-type.gardena.temperature.description=Die Temperatur
-
-channel-type.gardena.disposableBatteryStatus.label=Einweg Batterie Status
-channel-type.gardena.disposableBatteryStatus.description=Der Status der Einweg Batterie
-channel-type.gardena.disposableBatteryStatus.state.option.out_of_operation=Ausser Betrieb
-channel-type.gardena.disposableBatteryStatus.state.option.replace_now=Kritischer Batteriestand, wechseln Sie jetzt
-channel-type.gardena.disposableBatteryStatus.state.option.low=Niedrig
-channel-type.gardena.disposableBatteryStatus.state.option.ok=OK
-channel-type.gardena.disposableBatteryStatus.state.option.undefined=Undefiniert
-
-channel-type.gardena.valveOpen.label=Ventil geöffnet
-channel-type.gardena.valveOpen.description=Ventil geöffnet
-
-channel-type.gardena.manualOverride.label=Manuelle Steuerung
-channel-type.gardena.manualOverride.description=Manuelle Steuerung
-channel-type.gardena.manualOverride.state.option.inactive=Inaktiv
-channel-type.gardena.manualOverride.state.option.open=Offen
-channel-type.gardena.manualOverride.state.option.undefined=Undefiniert
-
-channel-type.gardena.buttonManualOverrideTime.label=Zeit manuelle Steuerung
-channel-type.gardena.buttonManualOverrideTime.description=Die Zeit die das Ventil geöffnet ist, wenn der Knopf am Gerät gedrückt wird
-
-channel-type.gardena.outletCommand.label=Ventil
-channel-type.gardena.outletCommand.description=Öffnen und Schließen des Ventils
-
-channel-type.gardena.frostWarning.label=Frostwarnung
-channel-type.gardena.frostWarning.description=Frostwarnung
-channel-type.gardena.frostWarning.state.option.no_frost=Kein Frost
-channel-type.gardena.frostWarning.state.option.frost=Frost
-channel-type.gardena.frostWarning.state.option.undefined=Undefiniert
-
-channel-type.gardena.humidity.label=Feuchtigkeit
-channel-type.gardena.humidity.description=Die Feuchtigkeit
-
-channel-type.gardena.light.label=Helligkeit
-channel-type.gardena.light.description=Die Helligkeit
-
-channel-type.gardena.firmwareStatus.label=Status
-channel-type.gardena.firmwareStatus.description=Der Firmware Status
-
-channel-type.gardena.firmwareUploadProgress.label=Upload Fortschritt
-channel-type.gardena.firmwareUploadProgress.description=Der Firmware upload Fortschritt
-
-channel-type.gardena.firmwareAvailableVersion.label=Verfügbare Version
-channel-type.gardena.firmwareAvailableVersion.description=Verfügbare Firmware Version
-
-channel-type.gardena.pumpMode.label=Modus
-channel-type.gardena.pumpMode.description=Der Modus
-channel-type.gardena.pumpMode.state.option.off=Aus
-channel-type.gardena.pumpMode.state.option.auto=Automatik
-
-channel-type.gardena.pumpOnOff.label=Pumpe
-channel-type.gardena.pumpOnOff.description=Die Pumpe
-channel-type.gardena.pumpOnOff.state.option.off=Aus
-channel-type.gardena.pumpOnOff.state.option.on=Ein
-
-channel-type.gardena.turnOnPressure.label=Einschaltdruck
-channel-type.gardena.turnOnPressure.description=Der Einschaltdruck
-
-channel-type.gardena.operatingMode.label=Betriebsmodus
-channel-type.gardena.operatingMode.description=Der Betriebsmodus
-channel-type.gardena.operatingMode.state.option.scheduled=Zeitgesteuert
-channel-type.gardena.operatingMode.state.option.automatic=Automatisch
-
-channel-type.gardena.counter.label=Zähler
-channel-type.gardena.counter.description=Der Zähler
-
-channel-type.gardena.outletPressure.label=Auslassdruck
-channel-type.gardena.outletPressure.description=Der Auslassdruck
-
-channel-type.gardena.flowRate.label=Fördermenge
-channel-type.gardena.flowRate.description=Die Fördermenge
-
-channel-type.gardena.flowSinceLastReset.label=Durchfluss seit letztem Reset
-channel-type.gardena.flowSinceLastReset.description=Der Durchfluss seit letztem Reset
-
-channel-type.gardena.flowTotal.label=Gesamtdurchfluss
-channel-type.gardena.flowTotal.description=Der Gesamtdurchfluss
-
-channel-type.gardena.drippingAlert.label=Leckage-Erkennung
-channel-type.gardena.drippingAlert.description=Die Leckage-Erkennung
-channel-type.gardena.drippingAlert.state.option.sixty=Sechzig
-channel-type.gardena.drippingAlert.state.option.two=Zwei
-channel-type.gardena.drippingAlert.state.option.off=Aus
-
-channel-type.gardena.manualWatering.label=Manuelle Bewässerung
-channel-type.gardena.manualWatering.description=Die manuelle Bewässerung
-
-channel-type.gardena.manualWateringTimer.label=Manueller Bewässerungs-Timer
-channel-type.gardena.manualWateringTimer.description=Der manuelle Bewässerungs-Timer
-channel-type.gardena.manualWateringTimer.state.option.0=Aus
-
-
-channel-type.gardena.lastManualOverrideTime.label=Letzte manuelle Betätigung
-channel-type.gardena.lastManualOverrideTime.description=Die letzte manuelle Betätigung
-
-channel-type.gardena.adaptiveSchedulingLastDecision.label=Intelligente Zeitplanung
-channel-type.gardena.adaptiveSchedulingLastDecision.description=Die letzte Entscheidung der intelligenten Zeitplanung
-channel-type.gardena.adaptiveSchedulingLastDecision.state.option.undefined=Undefiniert
-channel-type.gardena.adaptiveSchedulingLastDecision.state.option.watered_sensor_timeout=Keine Sensordaten
-channel-type.gardena.adaptiveSchedulingLastDecision.state.option.skipped=Zeiplan ausgesetzt
-channel-type.gardena.adaptiveSchedulingLastDecision.state.option.watered=Nach Zeitplan
-
-channel-type.gardena.temperatureMin.label=Minimum Temperatur
-channel-type.gardena.temperatureMin.description=Die minimale Temperatur
-channel-type.gardena.temperatureMax.label=Maximum Temperatur
-channel-type.gardena.temperatureMax.description=Die maximale Temperatur
-
-channel-type.gardena.errorCounter.label=Fehlerzähler
-channel-type.gardena.errorCounter.description=Der Fehlerzähler
-
-channel-type.gardena.errorCounterOne.label=Fehlerzähler 1
-channel-type.gardena.errorCounterOne.description=Der 1 Fehlerzähler
-
-channel-type.gardena.errorCounterTwo.label=Fehlerzähler 2
-channel-type.gardena.errorCounterTwo.description=Der 2 Fehlerzähler
-
-channel-type.gardena.errorCounterThree.label=Fehlerzähler 3
-channel-type.gardena.errorCounterThree.description=Der 3 Fehlerzähler
-
-channel-type.gardena.errorCounterFour.label=Fehlerzähler 4
-channel-type.gardena.errorCounterFour.description=Der vierte Fehlerzähler
-
-channel-type.gardena.operatingDays.label=Betriebstage
-channel-type.gardena.operatingDays.description=Die Betriebstage
-
-channel-type.gardena.scheduledWateringEnd.label=Geplantes Bewässerungsende
-channel-type.gardena.scheduledWateringEnd.description=Das geplante Bewässerungsende
-
-channel-type.gardena.scheduledWateringNextStart.label=Nächste geplante Bewässerung
-channel-type.gardena.scheduledWateringNextStart.description=Die nächste geplante Bewässerung
-
-channel-type.gardena.cuttingTime.label=Mähdauer
-channel-type.gardena.cuttingTime.description=Die gesamte Mähdauer
-
-channel-type.gardena.chargingCycles.label=Ladezyklen
-channel-type.gardena.chargingCycles.description=Die Anzahl der Ladezyklen
-
-channel-type.gardena.collisions.label=Kollisionen
-channel-type.gardena.collisions.description=Die Anzahl der Kollisionen
-
-channel-type.gardena.runningTime.label=Laufzeit
-channel-type.gardena.runningTime.description=Die gesamte Laufzeit
-
-channel-type.gardena.powerTimer.label=Einschaltzeit
-channel-type.gardena.powerTimer.description=Die Einschaltzeit
-channel-type.gardena.powerTimer.state.option.on=Ein
-channel-type.gardena.powerTimer.state.option.off=Aus
-channel-type.gardena.powerTimer.state.option.60=1 Minute
-channel-type.gardena.powerTimer.state.option.300=5 Minuten
-channel-type.gardena.powerTimer.state.option.900=15 Minuten
-channel-type.gardena.powerTimer.state.option.3600=1 Stunde
-channel-type.gardena.powerTimer.state.option.7200=2 Stunden
-channel-type.gardena.powerTimer.state.option.18000=5 Stunden
-channel-type.gardena.powerTimer.state.option.43200=12 Stunden
-channel-type.gardena.powerTimer.state.option.86400=1 Tag
-channel-type.gardena.powerTimer.state.option.259200=3 Tage
-channel-type.gardena.powerTimer.state.option.604800=1 Woche
-channel-type.gardena.powerTimer.state.option.1209600=2 Wochen
-
-channel-type.gardena.powerError.label=Fehler
-channel-type.gardena.powerError.description=Fehler
-channel-type.gardena.powerError.state.option.ok=OK
-channel-type.gardena.powerError.state.option.timer_cancelled=Timer abgebrochen
-channel-type.gardena.powerError.state.option.unknown=Unbekannt
-
-channel-type.gardena.wateringTimer.label=Bewässerungstimer
-channel-type.gardena.wateringTimer.description=Der Bewässerungstimer
-
-channel-type.gardena.valvesConnected.label=Angeschlossenen Ventile
-channel-type.gardena.valvesConnected.description=Die angeschlossenen Ventile
-
-channel-type.gardena.valvesMasterConfig.label=Ventile Hauptkonfiguration
-channel-type.gardena.valvesMasterConfig.description=Die Hauptkonfiguration der Ventile
-
-channel-type.gardena.ic24error.label=Bewässerungssteuerung Fehler
-channel-type.gardena.ic24error.description=Der Fehler der Bewässerungssteuerung
-channel-type.gardena.ic24error.state.option.voltage_drop=Spannungsabfall
-channel-type.gardena.ic24error.state.option.wrong_power_supply=Falsche Stromversorgung
-channel-type.gardena.ic24error.state.option.no_mcu_connection=Keine MCU verbunden
-channel-type.gardena.ic24error.state.option.unknown=Unbekannt
+channel-type.gardena.temperature.description=Die Temperatur in Grad Celsius
+
+channel-type.gardena.operatingHours.label=Betriebsstunden
+channel-type.gardena.operatingHours.description=Die Betriebsstunden
+
+channel-type.gardena.batteryState.label=Batteriestatus
+channel-type.gardena.batteryState.description=Der Batteriestatus
+channel-type.gardena.batteryState.state.option.OK=OK
+channel-type.gardena.batteryState.state.option.LOW=Niedrig
+channel-type.gardena.batteryState.state.option.REPLACE_NOW=Jetzt ersetzen
+channel-type.gardena.batteryState.state.option.OUT_OF_OPERATION=Außer Betrieb
+channel-type.gardena.batteryState.state.option.CHARGING=Laden
+channel-type.gardena.batteryState.state.option.NO_BATTERY=Keine Batterie
+channel-type.gardena.batteryState.state.option.UNKNOWN=Unbekannt
+
+channel-type.gardena.rfLinkState.label=Verbinungsstatus
+channel-type.gardena.rfLinkState.description=Der Verbinungsstatus
+channel-type.gardena.rfLinkState.state.option.ONLINE=Online
+channel-type.gardena.rfLinkState.state.option.OFFLINE=Offline
+channel-type.gardena.rfLinkState.state.option.UNKNOWN=Unbekannt
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">
- <!-- Gardena Smart Home Account -->
+ <!-- Gardena smart system Account -->
<bridge-type id="account">
- <label>Gardena Smart Home Account</label>
- <description>The Gardena Smart Home Account</description>
+ <label>Gardena Smart System Account</label>
+ <description>The Gardena smart system account</description>
<config-description>
<parameter name="email" type="text" required="true">
<label>Email</label>
- <description>Email address for logging in to Gardena Smart Home</description>
+ <description>Email address for logging in to Gardena smart system</description>
</parameter>
<parameter name="password" type="text" required="true">
<label>Password</label>
<context>password</context>
- <description>Password for logging in to Gardena Smart Home</description>
+ <description>Password for logging in to Gardena smart system</description>
</parameter>
- <parameter name="sessionTimeout" type="integer">
- <label>Session Timeout</label>
- <description>The timeout in minutes for a session to Gardena Smart Home</description>
- <advanced>true</advanced>
- <default>30</default>
+ <parameter name="apiKey" type="text" required="true">
+ <label>API Key</label>
+ <description>The Gardena smart system integration API key</description>
</parameter>
- <parameter name="connectionTimeout" type="integer">
+ <parameter name="connectionTimeout" type="integer" min="0" unit="s">
<label>Connection Timeout</label>
- <description>The timeout in seconds for connections to Gardena Smart Home</description>
- <advanced>true</advanced>
+ <description>The timeout in seconds for connections to Gardena smart system integration API</description>
+ <unitLabel>seconds</unitLabel>
<default>10</default>
</parameter>
- <parameter name="refresh" type="integer">
- <label>Refresh Interval</label>
- <description>The interval in seconds for refreshing the data from Gardena Smart Home</description>
- <advanced>true</advanced>
- <default>60</default>
- </parameter>
</config-description>
</bridge-type>
</thing:thing-descriptions>
<bridge-type-ref id="account"/>
</supported-bridge-type-refs>
- <label>Gardena Smart Sileno Mower</label>
- <description>A Gardena Smart Sileno mower</description>
+ <label>Gardena Smart SILENO Mower</label>
+ <description>Represents any Gardena smart SILENO mower model</description>
<channel-groups>
- <channel-group id="device_info" typeId="deviceInfo"/>
- <channel-group id="battery" typeId="rechargeableBattery"/>
- <channel-group id="radio" typeId="radio"/>
- <channel-group id="mower" typeId="mower"/>
- <channel-group id="mower_stats" typeId="mowerStats"/>
- <channel-group id="firmware" typeId="firmware"/>
+ <channel-group id="common" typeId="commonProperties"/>
+ <channel-group id="mower" typeId="mowerProperties"/>
+ <channel-group id="mower_commands" typeId="mowerCommands"/>
</channel-groups>
<properties>
- <!-- device info -->
- <property name="manufacturer"/>
- <property name="product"/>
- <property name="serialNumber"/>
- <property name="sgtin"/>
- <property name="version"/>
- <property name="category"/>
+ <!-- common -->
+ <property name="serial"/>
+ <property name="modelType"/>
</properties>
</thing-type>
- <channel-group-type id="deviceInfo">
- <label>Device Info</label>
- <description>Information about the device</description>
- <channels>
- <channel id="last_time_online" typeId="lastTimeOnline"/>
- </channels>
- </channel-group-type>
-
- <channel-group-type id="rechargeableBattery">
- <label>Battery Info</label>
- <description>Information about the rechargeable battery</description>
- <channels>
- <channel id="level" typeId="system.battery-level"/>
- <channel id="charging" typeId="charging"/>
- </channels>
- </channel-group-type>
-
- <channel-group-type id="radio">
- <label>Radio Link</label>
- <description>Information about the radio link</description>
- <channels>
- <channel id="quality" typeId="radioQuality"/>
- <channel id="state" typeId="system.signal-strength"/>
- </channels>
- </channel-group-type>
-
- <channel-group-type id="mower">
- <label>Robotic Mower</label>
- <description>Information about the robotic mower</description>
- <channels>
- <channel id="manual_operation" typeId="manualOperation"/>
- <channel id="status" typeId="status"/>
- <channel id="error" typeId="error"/>
- <channel id="source_for_next_start" typeId="sourceForNextStart"/>
- <channel id="timestamp_next_start" typeId="timestampNextStart"/>
- <channel id="override_end_time" typeId="overrideEndTime"/>
- <channel id="park_until_further_notice" typeId="parkUntilFurtherNotice"/>
- <channel id="park_until_next_timer" typeId="parkUntilNextTimer"/>
- <channel id="start_override_timer" typeId="startOverrideTimer"/>
- <channel id="start_resume_schedule" typeId="startResumeSchedule"/>
- <channel id="duration_property" typeId="mowerDurationProperty"/>
- </channels>
- </channel-group-type>
-
- <channel-type id="mowerDurationProperty">
- <item-type>Number</item-type>
- <label>Mowing Time</label>
- <description>Mowing time in minutes</description>
- <state pattern="%d min" min="1" max="1440" step="1"/>
- </channel-type>
-
- <channel-type id="parkUntilFurtherNotice">
- <item-type>Switch</item-type>
- <label>Park and Pause All Timers</label>
- <description>Park and pause all timers</description>
- </channel-type>
-
- <channel-type id="parkUntilNextTimer">
- <item-type>Switch</item-type>
- <label>Park Until Next Timer</label>
- <description>Park until next timer</description>
- </channel-type>
-
- <channel-type id="startResumeSchedule">
- <item-type>Switch</item-type>
- <label>Start Resume Schedule</label>
- <description>Start resume schedule</description>
- </channel-type>
-
- <channel-type id="startOverrideTimer">
- <item-type>Switch</item-type>
- <label>Override Timer</label>
- <description>Starts the mower for the specified duration</description>
- </channel-type>
-
- <channel-type id="lastTimeOnline" advanced="true">
- <item-type>DateTime</item-type>
- <label>Last Time Online</label>
- <description>Last time the device was online</description>
- <state readOnly="true"/>
- </channel-type>
-
- <channel-type id="charging" advanced="true">
- <item-type>Switch</item-type>
- <label>Battery Charging</label>
- <description>Battery Charging</description>
- <state readOnly="true"/>
- </channel-type>
+ <!-- ############################### Watering Computer ############################### -->
- <channel-type id="radioQuality" advanced="true">
- <item-type>Number</item-type>
- <label>Radio Link Quality</label>
- <description>Radio Link Quality</description>
- <state pattern="%d %%" readOnly="true"/>
- </channel-type>
+ <thing-type id="water_control">
+ <supported-bridge-type-refs>
+ <bridge-type-ref id="account"/>
+ </supported-bridge-type-refs>
- <channel-type id="manualOperation" advanced="true">
- <item-type>String</item-type>
- <label>Manual Operation</label>
- <description>The mower is controlled manually</description>
- <state readOnly="true"/>
- </channel-type>
+ <label>Gardena Smart Water Control</label>
+ <description>Represents a Gardena smart Water Control</description>
- <channel-type id="status">
- <item-type>String</item-type>
- <label>Mower Status</label>
- <description>The mower status</description>
- <state readOnly="true">
- <options>
- <option value="uninitialised">Uninitialised</option>
- <option value="paused">Paused</option>
- <option value="ok_cutting">Mowing</option>
- <option value="ok_searching">Searching charging station</option>
- <option value="ok_charging">Charging</option>
- <option value="ok_leaving">Mowing</option>
- <option value="wait_updating">Updating ...</option>
- <option value="wait_power_up">Powering up ...</option>
- <option value="parked_timer">Parked on schedule</option>
- <option value="parked_park_selected">Parked</option>
- <option value="off_disabled">The mower is turned off</option>
- <option value="off_hatch_open">Disabled. Hatch open</option>
- <option value="unknown">Unknown</option>
- <option value="error">Error</option>
- <option value="error_at_power_up">Error</option>
- <option value="off_hatch_closed">Disabled. Manual start needed</option>
- <option value="ok_cutting_timer_overridden">Manual mowing</option>
- <option value="parked_autotimer">Parked due to SensorControl</option>
- <option value="parked_daily_limit_reached">Completed</option>
- <option value="undefined">Undefined</option>
- </options>
- </state>
- </channel-type>
-
- <channel-type id="error" advanced="true">
- <item-type>String</item-type>
- <label>Mower Error</label>
- <description>Mower Error</description>
- <state readOnly="true">
- <options>
- <option value="no_message">No message</option>
- <option value="outside_working_area">Outside working area</option>
- <option value="no_loop_signal">No loop signal</option>
- <option value="wrong_loop_signal">Wrong loop signal</option>
- <option value="loop_sensor_problem_front">Loop sensor problem, front</option>
- <option value="loop_sensor_problem_rear">Loop sensor problem, rear</option>
- <option value="loop_sensor_problem_left">Loop sensor problem, left</option>
- <option value="loop_sensor_problem_right">Loop sensor problem, right</option>
- <option value="wrong_pin_code">Wrong pin code</option>
- <option value="trapped">Trapped</option>
- <option value="upside_down">Upside down</option>
- <option value="low_battery">Low battery</option>
- <option value="empty_battery">Empty battery</option>
- <option value="no_drive">Disabled. Hatch open</option>
- <option value="temporarily_lifted">Temporary lifted</option>
- <option value="lifted">Lifted</option>
- <option value="stuck_in_charging_station">Stuck in charging station</option>
- <option value="charging_station_blocked">Charging station blocked</option>
- <option value="collision_sensor_problem_rear">Collision sensor problem, rear</option>
- <option value="collision_sensor_problem_front">Collision sensor problem, front</option>
- <option value="wheel_motor_blocked_right">Wheel motor blocked, right</option>
- <option value="wheel_motor_blocked_left">Wheel motor blocked, left</option>
- <option value="wheel_drive_problem_right">Wheel drive problem, right</option>
- <option value="wheel_drive_problem_left">Wheel drive problem, left</option>
- <option value="cutting_motor_drive_defect">Cutting motor drive defect</option>
- <option value="cutting_system_blocked">Cutting system blocked</option>
- <option value="invalid_sub_device_combination">Invalid sub-device combination</option>
- <option value="settings_restored">Settings restored</option>
- <option value="memory_circuit_problem">Memory circuit problem</option>
- <option value="slope_too_steep">Slope too steep</option>
- <option value="charging_system_problem">Charging system problem</option>
- <option value="stop_button_problem">Stop button problem</option>
- <option value="tilt_sensor_problem">Tilt sensor problem</option>
- <option value="mower_tilted">Mower tilted</option>
- <option value="wheel_motor_overloaded_right">Wheel motor overloaded, right</option>
- <option value="wheel_motor_overloaded_left">Wheel motor overloaded, left</option>
- <option value="charging_current_too_high">Charging current too high</option>
- <option value="electronic_problem">Electronic problem</option>
- <option value="cutting_motor_problem">Cutting motor problem</option>
- <option value="limited_cutting_height_range">Limited cutting height range</option>
- <option value="unexpected_cutting_height_adj">Unexpected cutting height adj</option>
- <option value="cutting_height_problem_drive">Cutting height problem drive</option>
- <option value="cutting_height_problem_curr">Cutting height problem curr</option>
- <option value="cutting_height_problem_dir">Cutting height problem dir</option>
- <option value="cutting_height_blocked">Cutting height blocked</option>
- <option value="cutting_height_problem">Cutting height problem</option>
- <option value="no_response_from_charger">No response from charger</option>
- <option value="ultrasonic_problem">Ultrasonic problem</option>
- <option value="temporary_problem">Temporary problem</option>
- <option value="guide_1_not_found">Guide 1 not found</option>
- <option value="guide_2_not_found">Guide 2 not found</option>
- <option value="guide_3_not_found">Guide 3 not found</option>
- <option value="gps_tracker_module_error">GPS tracker module error</option>
- <option value="weak_gps_signal">Weak gps signal</option>
- <option value="difficult_finding_home">Difficult finding home</option>
- <option value="guide_calibration_accomplished">Guide calibration accomplished</option>
- <option value="guide_calibration_failed">Guide calibration failed</option>
- <option value="temporary_battery_problem">Temporary battery problem</option>
- <option value="battery_problem">Battery problem</option>
- <option value="too_many_batteries">Too many batteries</option>
- <option value="alarm_mower_switched_off">Alarm! Mower switched off</option>
- <option value="alarm_mower_stopped">Alarm! Mower stopped</option>
- <option value="alarm_mower_lifted">Alarm! Mower lifted</option>
- <option value="alarm_mower_tilted">Alarm! Mower tilted</option>
- <option value="alarm_mower_in_motion">Alarm! Mower in motion</option>
- <option value="alarm_outside_geofence">Alarm outside geofence</option>
- <option value="connection_changed">Connection changed</option>
- <option value="connection_not_changed">Connection not changed</option>
- <option value="com_board_not_available">COM board not available</option>
- <option value="slipped">Slipped</option>
- <option value="invalid_battery_combination">Invalid battery combination</option>
- <option value="imbalanced_cutting_disc">Imbalanced cutting disc</option>
- <option value="safety_function_faulty">Safety function faulty</option>
- </options>
- </state>
- </channel-type>
+ <channel-groups>
+ <channel-group id="common" typeId="commonProperties"/>
+ <channel-group id="valve" typeId="valveProperties"/>
+ <channel-group id="valve_commands" typeId="valveCommands"/>
+ <channel-group id="valveSet" typeId="valveSetProperties"/>
+ <channel-group id="valveSet_commands" typeId="valveSetCommands"/>
+ </channel-groups>
+ <properties>
+ <!-- common -->
+ <property name="serial"/>
+ <property name="modelType"/>
+ </properties>
+ </thing-type>
- <channel-type id="sourceForNextStart" advanced="true">
- <item-type>String</item-type>
- <label>Source for Next Start</label>
- <description>The source for the next start</description>
- <state readOnly="true">
- <options>
- <option value="no_source">No source</option>
- <option value="completed_cutting_daily_limit">Completed cutting daily limit</option>
- <option value="week_timer">Week timer</option>
- <option value="countdown_timer">Countdown timer</option>
- <option value="mower_charging">Mower charging</option>
- <option value="completed_cutting_autotimer">Completed cutting autotimer</option>
- <option value="undefined">Undefined</option>
- </options>
- </state>
- </channel-type>
+ <!-- ############################### Sensor ############################### -->
- <channel-type id="timestampNextStart">
- <item-type>DateTime</item-type>
- <label>Timestamp Next Start</label>
- <description>Timestamp of the next start</description>
- <state readOnly="true"/>
- </channel-type>
+ <thing-type id="sensor">
+ <supported-bridge-type-refs>
+ <bridge-type-ref id="account"/>
+ </supported-bridge-type-refs>
- <channel-type id="overrideEndTime" advanced="true">
- <item-type>DateTime</item-type>
- <label>Override End Time</label>
- <description>Override End Time</description>
- <state readOnly="true"/>
- </channel-type>
+ <label>Gardena Smart Sensor</label>
+ <description>Represents a Gardena smart Sensor</description>
- <channel-type id="temperature" advanced="true">
- <item-type>Number</item-type>
- <label>Temperature</label>
- <description>Temperature</description>
- <state pattern="%d °C" readOnly="true"/>
- </channel-type>
+ <channel-groups>
+ <channel-group id="common" typeId="commonProperties"/>
+ <channel-group id="sensor" typeId="sensorProperties"/>
+ </channel-groups>
+ <properties>
+ <!-- common -->
+ <property name="serial"/>
+ <property name="modelType"/>
+ </properties>
+ </thing-type>
- <channel-group-type id="firmware">
- <label>Firmware</label>
- <description>Information about the firmware</description>
- <channels>
- <channel id="firmware_status" typeId="firmwareStatus"/>
- <channel id="firmware_upload_progress" typeId="firmwareUploadProgress"/>
- <channel id="firmware_available_version" typeId="firmwareAvailableVersion"/>
- </channels>
- </channel-group-type>
+ <!-- ############################### Pressure Pump ############################### -->
- <channel-type id="firmwareStatus" advanced="true">
- <item-type>String</item-type>
- <label>Status</label>
- <description>The firmware status</description>
- <state readOnly="true"/>
- </channel-type>
+ <thing-type id="pump">
+ <supported-bridge-type-refs>
+ <bridge-type-ref id="account"/>
+ </supported-bridge-type-refs>
- <channel-type id="firmwareUploadProgress" advanced="true">
- <item-type>Number</item-type>
- <label>Upload Progress</label>
- <description>Firmware upload in progress</description>
- <state readOnly="true"/>
- </channel-type>
+ <label>Gardena Smart Pressure Pump</label>
+ <description>Represents a Gardena smart Pressure Pump</description>
- <channel-type id="firmwareAvailableVersion" advanced="true">
- <item-type>String</item-type>
- <label>Available Version</label>
- <description>Available firmware version</description>
- <state readOnly="true"/>
- </channel-type>
+ <channel-groups>
+ <channel-group id="common" typeId="commonProperties"/>
+ <channel-group id="valve" typeId="valveProperties"/>
+ <channel-group id="valve_commands" typeId="valveCommands"/>
+ </channel-groups>
+ <properties>
+ <!-- common -->
+ <property name="serial"/>
+ <property name="modelType"/>
+ </properties>
+ </thing-type>
- <channel-group-type id="mowerStats">
- <label>Robotic Mower Statistics</label>
- <description>Statistics about the robotic mower</description>
- <channels>
- <channel id="cutting_time" typeId="cuttingTime"/>
- <channel id="charging_cycles" typeId="chargingCycles"/>
- <channel id="collisions" typeId="collisions"/>
- <channel id="running_time" typeId="runningTime"/>
- </channels>
- </channel-group-type>
+ <!-- ############################### Power ############################### -->
- <channel-type id="cuttingTime" advanced="true">
- <item-type>Number</item-type>
- <label>Cutting Time</label>
- <description>The cutting time</description>
- <state readOnly="true" pattern="%d h"/>
- </channel-type>
+ <thing-type id="power">
+ <supported-bridge-type-refs>
+ <bridge-type-ref id="account"/>
+ </supported-bridge-type-refs>
- <channel-type id="chargingCycles" advanced="true">
- <item-type>Number</item-type>
- <label>Charging Cycles</label>
- <description>The charging cycles</description>
- <state readOnly="true"/>
- </channel-type>
+ <label>Gardena Smart Power Adapter</label>
+ <description>Represents a Gardena smart Power Adapter</description>
- <channel-type id="collisions" advanced="true">
- <item-type>Number</item-type>
- <label>Collisions</label>
- <description>The number of collisions</description>
- <state readOnly="true"/>
- </channel-type>
+ <channel-groups>
+ <channel-group id="common" typeId="commonProperties"/>
+ <channel-group id="powerSocket" typeId="powerSocketProperties"/>
+ <channel-group id="powerSocket_commands" typeId="powerSocketCommands"/>
+ </channel-groups>
+ <properties>
+ <!-- common -->
+ <property name="serial"/>
+ <property name="modelType"/>
+ </properties>
+ </thing-type>
- <channel-type id="runningTime" advanced="true">
- <item-type>Number</item-type>
- <label>Running Time</label>
- <description>Running time</description>
- <state readOnly="true" pattern="%d h"/>
- </channel-type>
+ <!-- ############################### Irrigation Control ############################### -->
- <!-- ############################### Watering Computer ############################### -->
- <thing-type id="watering_computer">
+ <thing-type id="irrigation_control">
<supported-bridge-type-refs>
<bridge-type-ref id="account"/>
</supported-bridge-type-refs>
- <label>Gardena Smart Watering Computer</label>
- <description>Represents a Gardena Smart Watering Computer</description>
+ <label>Gardena Smart Irrigation Control</label>
+ <description>Represents a Gardena smart Irrigation Control</description>
<channel-groups>
- <channel-group id="device_info" typeId="deviceInfo"/>
- <channel-group id="battery" typeId="battery"/>
- <channel-group id="radio" typeId="radio"/>
- <channel-group id="outlet" typeId="outlet"/>
- <channel-group id="ambient_temperature" typeId="ambientTemperature"/>
+ <channel-group id="common" typeId="commonProperties"/>
+ <channel-group id="valveOne" typeId="valveProperties">
+ <label>Valve 1 Properties</label>
+ </channel-group>
+ <channel-group id="valveOne_commands" typeId="valveCommands">
+ <label>Valve 1 Commands</label>
+ </channel-group>
+ <channel-group id="valveTwo" typeId="valveProperties">
+ <label>Valve 2 Properties</label>
+ </channel-group>
+ <channel-group id="valveTwo_commands" typeId="valveCommands">
+ <label>Valve 2 Commands</label>
+ </channel-group>
+ <channel-group id="valveThree" typeId="valveProperties">
+ <label>Valve 3 Properties</label>
+ </channel-group>
+ <channel-group id="valveThree_commands" typeId="valveCommands">
+ <label>Valve 3 Commands</label>
+ </channel-group>
+ <channel-group id="valveFour" typeId="valveProperties">
+ <label>Valve 4 Properties</label>
+ </channel-group>
+ <channel-group id="valveFour_commands" typeId="valveCommands">
+ <label>Valve 4 Commands</label>
+ </channel-group>
+ <channel-group id="valveFive" typeId="valveProperties">
+ <label>Valve 5 Properties</label>
+ </channel-group>
+ <channel-group id="valveFive_commands" typeId="valveCommands">
+ <label>Valve 5 Commands</label>
+ </channel-group>
+ <channel-group id="valveSix" typeId="valveProperties">
+ <label>Valve 6 Properties</label>
+ </channel-group>
+ <channel-group id="valveSix_commands" typeId="valveCommands">
+ <label>Valve 6 Commands</label>
+ </channel-group>
+ <channel-group id="valveSet" typeId="valveSetProperties"/>
+ <channel-group id="valveSet_commands" typeId="valveSetCommands"/>
</channel-groups>
<properties>
- <!-- device info -->
- <property name="manufacturer"/>
- <property name="product"/>
- <property name="serialNumber"/>
- <property name="sgtin"/>
- <property name="version"/>
- <property name="category"/>
+ <!-- common -->
+ <property name="serial"/>
+ <property name="modelType"/>
</properties>
</thing-type>
- <channel-group-type id="battery">
- <label>Battery Info</label>
- <description>Information about the battery</description>
+ <!-- ############################### channels and groups ############################### -->
+
+ <channel-group-type id="valveSetProperties">
+ <label>Valve Set Properties</label>
+ <description>Properties of a valve set</description>
<channels>
- <channel id="level" typeId="system.battery-level"/>
- <channel id="disposable_battery_status" typeId="disposableBatteryStatus"/>
+ <channel id="state" typeId="state"/>
+ <channel id="state_timestamp" typeId="timestamp">
+ <label>State Timestamp</label>
+ </channel>
+ <channel id="lastErrorCode" typeId="lastErrorCode"/>
+ <channel id="lastErrorCode_timestamp" typeId="timestamp">
+ <label>Last Error Code Timestamp</label>
+ </channel>
</channels>
</channel-group-type>
- <channel-group-type id="outlet">
- <label>Watering Outlet</label>
- <description>Information about the watering outlet</description>
+ <channel-group-type id="valveProperties">
+ <label>Valve Properties</label>
+ <description>Properties of a valve</description>
<channels>
- <channel id="valve_open" typeId="valveOpen"/>
- <channel id="manual_override" typeId="manualOverride"/>
- <channel id="button_manual_override_time" typeId="buttonManualOverrideTime"/>
+ <channel id="name" typeId="name"/>
+ <channel id="state" typeId="state"/>
+ <channel id="state_timestamp" typeId="timestamp">
+ <label>State Timestamp</label>
+ </channel>
+ <channel id="activity" typeId="activity"/>
+ <channel id="activity_timestamp" typeId="timestamp">
+ <label>Activity Timestamp</label>
+ </channel>
+ <channel id="lastErrorCode" typeId="lastErrorCode"/>
+ <channel id="lastErrorCode_timestamp" typeId="timestamp">
+ <label>Last Error Code Timestamp</label>
+ </channel>
+ <channel id="duration" typeId="duration"/>
</channels>
</channel-group-type>
- <channel-group-type id="ambientTemperature">
- <label>Ambient Temperature</label>
- <description>Ambient Temperature</description>
+ <channel-group-type id="sensorProperties">
+ <label>Sensor Properties</label>
+ <description>Properties of a sensor</description>
<channels>
- <channel id="temperature" typeId="temperature">
+ <channel id="soilHumidity" typeId="soilHumidity"/>
+ <channel id="soilHumidity_timestamp" typeId="timestamp">
+ <label>Soil Humidity Timestamp</label>
+ </channel>
+ <channel id="soilTemperature" typeId="temperature">
+ <label>Soil Temperature</label>
+ </channel>
+ <channel id="soilTemperature_timestamp" typeId="timestamp">
+ <label>Soil Temperature Timestamp</label>
+ </channel>
+ <channel id="ambientTemperature" typeId="temperature">
<label>Ambient Temperature</label>
</channel>
- <channel id="frost_warning" typeId="frostWarning"/>
+ <channel id="ambientTemperature_timestamp" typeId="timestamp">
+ <label>Ambient Temperature Timestamp</label>
+ </channel>
+ <channel id="lightIntensity" typeId="lightIntensity"/>
+ <channel id="lightIntensity_timestamp" typeId="timestamp">
+ <label>Light Intensity Timestamp</label>
+ </channel>
</channels>
</channel-group-type>
- <channel-type id="disposableBatteryStatus" advanced="true">
- <item-type>String</item-type>
- <label>Disposable Battery Status</label>
- <description>The status of the disposable battery</description>
- <state readOnly="true">
- <options>
- <option value="out_of_operation">Out of operation</option>
- <option value="replace_now">Critical battery level, replace now</option>
- <option value="low">Low</option>
- <option value="ok">OK</option>
- <option value="undefined">Undefined</option>
- </options>
- </state>
- </channel-type>
-
- <channel-type id="valveOpen">
- <item-type>Switch</item-type>
- <label>Valve Open</label>
- <description>Valve Open</description>
- </channel-type>
-
- <channel-type id="manualOverride" advanced="true">
- <item-type>String</item-type>
- <label>Manual Override</label>
- <description>Manual Override</description>
- <state readOnly="true">
- <options>
- <option value="inactive">Inactive</option>
- <option value="open">Open</option>
- <option value="undefined">Undefined</option>
- </options>
- </state>
- </channel-type>
-
- <channel-type id="buttonManualOverrideTime" advanced="true">
- <item-type>Number</item-type>
- <label>Button Manual Override</label>
- <description>The time the valve is open when you press the button on the device</description>
- <state pattern="%d min" min="1" max="59" step="1"/>
- </channel-type>
-
- <channel-type id="frostWarning" advanced="true">
- <item-type>String</item-type>
- <label>Frost Warning</label>
- <description>Frost Warning</description>
- <state readOnly="true">
- <options>
- <option value="no_frost">No frost</option>
- <option value="frost">Frost</option>
- <option value="undefined">Undefined</option>
- </options>
- </state>
- </channel-type>
-
- <!-- ############################### Sensor ############################### -->
- <thing-type id="sensor">
- <supported-bridge-type-refs>
- <bridge-type-ref id="account"/>
- </supported-bridge-type-refs>
-
- <label>Gardena Smart Sensor</label>
- <description>Represents a Gardena Smart Sensor</description>
-
- <channel-groups>
- <channel-group id="device_info" typeId="deviceInfo"/>
- <channel-group id="battery" typeId="battery"/>
- <channel-group id="radio" typeId="radio"/>
- <channel-group id="ambient_temperature" typeId="ambientTemperature"/>
- <channel-group id="soil_temperature" typeId="soilTemperature"/>
- <channel-group id="humidity" typeId="humidity"/>
- <channel-group id="light" typeId="light"/>
- </channel-groups>
- <properties>
- <!-- device info -->
- <property name="manufacturer"/>
- <property name="product"/>
- <property name="serialNumber"/>
- <property name="sgtin"/>
- <property name="version"/>
- <property name="category"/>
- </properties>
- </thing-type>
-
- <channel-group-type id="soilTemperature">
- <label>Soil Temperature</label>
- <description>The soil temperature</description>
+ <channel-group-type id="powerSocketProperties">
+ <label>Power Socket Properties</label>
+ <description>Properties of a power socket</description>
<channels>
- <channel id="temperature" typeId="temperature">
- <label>Soil Temperature</label>
+ <channel id="state" typeId="state"/>
+ <channel id="state_timestamp" typeId="timestamp">
+ <label>State Timestamp</label>
+ </channel>
+ <channel id="activity" typeId="activity"/>
+ <channel id="activity_timestamp" typeId="timestamp">
+ <label>Activity Timestamp</label>
+ </channel>
+ <channel id="lastErrorCode" typeId="lastErrorCode"/>
+ <channel id="lastErrorCode_timestamp" typeId="timestamp">
+ <label>Last Error Code Timestamp</label>
+ </channel>
+ <channel id="duration" typeId="duration"/>
+ <channel id="duration_timestamp" typeId="timestamp">
+ <label>Duration Timestamp</label>
</channel>
</channels>
</channel-group-type>
- <channel-group-type id="humidity">
- <label>Humidity</label>
- <description>The humidity</description>
+ <channel-group-type id="mowerProperties">
+ <label>Mower Properties</label>
+ <description>Properties of a mower</description>
<channels>
- <channel id="humidity" typeId="humidity"/>
+ <channel id="state" typeId="state"/>
+ <channel id="state_timestamp" typeId="timestamp">
+ <label>State Timestamp</label>
+ </channel>
+ <channel id="activity" typeId="activity"/>
+ <channel id="activity_timestamp" typeId="timestamp">
+ <label>Activity Timestamp</label>
+ </channel>
+ <channel id="lastErrorCode" typeId="lastErrorCode"/>
+ <channel id="lastErrorCode_timestamp" typeId="timestamp">
+ <label>Last Error Code Timestamp</label>
+ </channel>
+ <channel id="operatingHours" typeId="operatingHours"/>
</channels>
</channel-group-type>
- <channel-group-type id="light">
- <label>Brightness</label>
- <description>The brightness</description>
+ <channel-group-type id="commonProperties">
+ <label>Common Properties</label>
+ <description>Properties that are common across devices</description>
<channels>
- <channel id="light" typeId="light"/>
+ <channel id="name" typeId="name"/>
+ <channel id="batteryLevel" typeId="system.battery-level"/>
+ <channel id="batteryLevel_timestamp" typeId="timestamp">
+ <label>Battery Level Timestamp</label>
+ </channel>
+ <channel id="batteryState" typeId="batteryState"/>
+ <channel id="batteryState_timestamp" typeId="timestamp">
+ <label>Battery State Timestamp</label>
+ </channel>
+ <channel id="rfLinkLevel" typeId="system.signal-strength"/>
+ <channel id="rfLinkLevel_timestamp" typeId="timestamp">
+ <label>RF Link Level Timestamp</label>
+ </channel>
+ <channel id="rfLinkState" typeId="rfLinkState"/>
+ <channel id="rfLinkState_timestamp" typeId="timestamp">
+ <label>RF Link State Timestamp</label>
+ </channel>
+ <channel id="lastUpdate_timestamp" typeId="timestampRefresh">
+ <label>LastUpdate Timestamp</label>
+ </channel>
</channels>
</channel-group-type>
- <channel-type id="humidity">
- <item-type>Number</item-type>
- <label>Humidity</label>
- <description>The humidity</description>
- <state pattern="%d %%" readOnly="true"/>
- </channel-type>
-
- <channel-type id="light">
- <item-type>Number</item-type>
- <label>Brightness</label>
- <description>The brightness</description>
- <state pattern="%d lx" readOnly="true"/>
- </channel-type>
-
- <!-- ############################### Pressure Pump ############################### -->
-
- <thing-type id="electronic_pressure_pump">
- <supported-bridge-type-refs>
- <bridge-type-ref id="account"/>
- </supported-bridge-type-refs>
-
- <label>Gardena Smart Pressure Pump</label>
- <description>Represents a Gardena Smart Pressure Pump</description>
-
- <channel-groups>
- <channel-group id="device_info" typeId="deviceInfo"/>
- <channel-group id="radio" typeId="radio"/>
-
- <channel-group id="outlet_temperature" typeId="outletTemperature"/>
- <channel-group id="pump" typeId="pump"/>
- <channel-group id="outlet_pressure" typeId="outletPressure"/>
- <channel-group id="flow" typeId="flow"/>
- <channel-group id="manual_watering" typeId="manualWatering"/>
- <channel-group id="scheduling" typeId="scheduling"/>
- </channel-groups>
-
- <properties>
- <!-- device info -->
- <property name="manufacturer"/>
- <property name="product"/>
- <property name="serialNumber"/>
- <property name="sgtin"/>
- <property name="version"/>
- <property name="category"/>
- </properties>
-
- <config-description>
- <parameter name="leakage_detection" type="text">
- <label>Leakage Detection</label>
- <options>
- <option value="watering">Watering</option>
- <option value="washing_machine">Washing machine</option>
- <option value="domestic_water_supply">Domestic water supply</option>
- <option value="off">Off</option>
- </options>
- </parameter>
- <parameter name="operating_mode" type="text">
- <label>Operating Mode</label>
- <options>
- <option value="scheduled">Scheduled</option>
- <option value="automatic">Automatic</option>
- </options>
- </parameter>
- <parameter name="turn_on_pressure" type="text">
- <label>Turn On Pressure</label>
- <options>
- <option value="2.0">2.0</option>
- <option value="2.2">2.2</option>
- <option value="2.4">2.4</option>
- <option value="2.6">2.6</option>
- <option value="2.8">2.8</option>
- </options>
- </parameter>
- </config-description>
- </thing-type>
-
- <channel-group-type id="outletTemperature">
- <label>Outlet Temperature</label>
- <description>The outlet temperature</description>
+ <channel-group-type id="mowerCommands">
+ <label>Mower Commands</label>
+ <description>Commands to control a mower</description>
<channels>
- <channel id="temperature" typeId="temperature"/>
- <channel id="temperature_min" typeId="temperatureMin"/>
- <channel id="temperature_max" typeId="temperatureMax"/>
- <channel id="frost_warning" typeId="frostWarning"/>
+ <channel id="commandDuration" typeId="mowerCommandDuration">
+ <label>Mowing Duration</label>
+ </channel>
+ <channel id="start_seconds_to_override" typeId="gardenaCommand">
+ <label>Start Mowing with Duration</label>
+ <description>Manual operation, use Mowing Duration to define duration</description>
+ </channel>
+ <channel id="start_dont_override" typeId="gardenaCommand">
+ <label>Start Schedule</label>
+ <description>Automatic operation</description>
+ </channel>
+ <channel id="park_until_next_task" typeId="gardenaCommand">
+ <label>Park</label>
+ <description>Cancel the current operation and return to charging station</description>
+ </channel>
+ <channel id="park_until_further_notice" typeId="gardenaCommand">
+ <label>Pause Schedule</label>
+ <description>Cancel the current operation, return to charging station, ignore schedule</description>
+ </channel>
</channels>
</channel-group-type>
- <channel-type id="temperatureMin" advanced="true">
- <item-type>Number</item-type>
- <label>Minimum Temperature</label>
- <description>The minimum outlet temperature</description>
- <state pattern="%d °C" readOnly="true"/>
- </channel-type>
-
- <channel-type id="temperatureMax" advanced="true">
- <item-type>Number</item-type>
- <label>Maximum Temperature</label>
- <description>The maximum outlet temperature</description>
- <state pattern="%d °C" readOnly="true"/>
- </channel-type>
-
- <channel-group-type id="pump">
- <label>Pump</label>
- <description>The pump</description>
+ <channel-group-type id="valveCommands">
+ <label>Valve Commands</label>
+ <description>Commands to control a valve</description>
<channels>
- <channel id="mode" typeId="pumpMode"/>
- <channel id="pump_on_off" typeId="pumpOnOff"/>
- <channel id="turn_on_pressure" typeId="turnOnPressure"/>
- <channel id="operating_mode" typeId="operatingMode"/>
- <channel id="error" typeId="errorCounter"/>
- <channel id="operating_days" typeId="operatingDays"/>
- <channel id="error1_count" typeId="errorCounterOne"/>
- <channel id="error2_count" typeId="errorCounterTwo"/>
- <channel id="error3_count" typeId="errorCounterThree"/>
- <channel id="error4_count" typeId="errorCounterFour"/>
+ <channel id="commandDuration" typeId="valveCommandDuration">
+ <label>Irrigation Duration</label>
+ </channel>
+ <channel id="start_seconds_to_override" typeId="gardenaCommand">
+ <label>Open Valve with Duration</label>
+ <description>Manual operation, use Irrigation Duration to define duration</description>
+ </channel>
+ <channel id="stop_until_next_task" typeId="gardenaCommand">
+ <label>Close Valve, Continue Schedule</label>
+ <description>Cancel the current watering, continue with the schedule</description>
+ </channel>
+ <channel id="pause" typeId="gardenaCommand">
+ <label>Pause Until Specified Time</label>
+ <description>Skip automatic operation until specified time. The currently active operation might or might not be
+ cancelled (depends on device model)</description>
+ </channel>
+ <channel id="unpause" typeId="gardenaCommand">
+ <label>Start Schedule</label>
+ <description>Restore automatic operation if it was paused</description>
+ </channel>
</channels>
</channel-group-type>
- <channel-type id="errorCounter" advanced="true">
- <item-type>Number</item-type>
- <label>Error Counter</label>
- <description>The error counter</description>
- <state readOnly="true"/>
- </channel-type>
+ <channel-group-type id="valveSetCommands">
+ <label>Valve Set Commands</label>
+ <description>Commands to control a valve set</description>
+ <channels>
+ <channel id="stop_until_next_task" typeId="gardenaCommand">
+ <label>Close All Valves</label>
+ <description>Immediately close all valves</description>
+ </channel>
+ </channels>
+ </channel-group-type>
- <channel-type id="errorCounterOne" advanced="true">
- <item-type>Number</item-type>
- <label>Error Counter One</label>
- <description>The error counter one</description>
- <state readOnly="true"/>
- </channel-type>
+ <channel-group-type id="powerSocketCommands">
+ <label>Power Socket Commands</label>
+ <description>Commands to control a power socket</description>
+ <channels>
+ <channel id="commandDuration" typeId="powerCommandDuration">
+ <label>Switch on Duration</label>
+ </channel>
+ <channel id="start_seconds_to_override" typeId="gardenaCommand">
+ <label>Manual On with Duration</label>
+ <description>Manual operation, use Switch on Duration to define duration</description>
+ </channel>
+ <channel id="start_override" typeId="gardenaCommand">
+ <label>Manual On</label>
+ <description>Manual on</description>
+ </channel>
+ <channel id="stop_until_next_task" typeId="gardenaCommand">
+ <label>Off, Continue Schedule</label>
+ <description>Immediately switch off, continue with the schedule</description>
+ </channel>
+ <channel id="pause" typeId="gardenaCommand">
+ <label>Pause Until Specified Time</label>
+ <description>Skip automatic operation until specified time. The currently active operation will NOT be cancelled</description>
+ </channel>
+ <channel id="unpause" typeId="gardenaCommand">
+ <label>Start Schedule</label>
+ <description>Restore automatic operation if it was paused</description>
+ </channel>
+ </channels>
+ </channel-group-type>
- <channel-type id="errorCounterTwo" advanced="true">
- <item-type>Number</item-type>
- <label>Error Counter Two</label>
- <description>The error counter two</description>
- <state readOnly="true"/>
+ <channel-type id="gardenaCommand">
+ <item-type>Switch</item-type>
+ <label>Command</label>
+ <description>A command for a device</description>
</channel-type>
- <channel-type id="errorCounterThree" advanced="true">
- <item-type>Number</item-type>
- <label>Error Counter Three</label>
- <description>The error counter three</description>
- <state readOnly="true"/>
+ <channel-type id="powerCommandDuration">
+ <item-type>Number:Time</item-type>
+ <label>Command Duration</label>
+ <description>A duration in minutes for a command</description>
+ <state pattern="%d min" min="1" max="59" step="1"/>
</channel-type>
- <channel-type id="errorCounterFour" advanced="true">
- <item-type>Number</item-type>
- <label>Error Counter Four</label>
- <description>The error counter Four</description>
- <state readOnly="true"/>
+ <channel-type id="valveCommandDuration">
+ <item-type>Number:Time</item-type>
+ <label>Command Duration</label>
+ <description>A duration in minutes for a command</description>
+ <state pattern="%d min" min="1" max="600" step="1"/>
</channel-type>
- <channel-type id="operatingDays" advanced="true">
- <item-type>Number</item-type>
- <label>Operating Days</label>
- <description>The operating days</description>
- <state readOnly="true" pattern="%d days"/>
+ <channel-type id="mowerCommandDuration">
+ <item-type>Number:Time</item-type>
+ <label>Command Duration</label>
+ <description>A duration in minutes for a command</description>
+ <state pattern="%d min" min="1" max="1440" step="1"/>
</channel-type>
- <channel-type id="pumpMode">
+ <channel-type id="state">
<item-type>String</item-type>
- <label>Mode</label>
- <description>The mode</description>
+ <label>State</label>
+ <description>The state of the device</description>
<state readOnly="true">
<options>
- <option value="off">Off</option>
- <option value="auto">Auto</option>
- </options>
- </state>
- </channel-type>
-
- <channel-type id="pumpOnOff">
- <item-type>Switch</item-type>
- <label>Pump</label>
- <description>Pump</description>
- <state readOnly="true"/>
- </channel-type>
-
- <channel-type id="turnOnPressure">
- <item-type>Number</item-type>
- <label>Turn On Pressure</label>
- <description>Turn on pressure</description>
- <state readOnly="true" pattern="%d bar">
- <options>
- <option value="2.0">2.0</option>
- <option value="2.2">2.2</option>
- <option value="2.4">2.4</option>
- <option value="2.6">2.6</option>
- <option value="2.8">2.8</option>
+ <option value="OK">OK</option>
+ <option value="WARNING">Warning</option>
+ <option value="ERROR">Error</option>
+ <option value="UNAVAILABLE">Unavailable</option>
</options>
</state>
</channel-type>
- <channel-type id="operatingMode">
+ <channel-type id="lastErrorCode">
<item-type>String</item-type>
- <label>Operating Mode</label>
- <description>The operating mode</description>
+ <label>Last Error Code</label>
+ <description>The last error code</description>
<state readOnly="true">
<options>
- <option value="scheduled">Scheduled</option>
- <option value="automatic">Automatic</option>
+ <!-- common -->
+ <option value="UNKNOWN">Unknown</option>
+ <option value="NO_MESSAGE">No Message</option>
+
+ <!-- mower -->
+ <option value="OUTSIDE_WORKING_AREA">Outside working area</option>
+ <option value="NO_LOOP_SIGNAL">No loop signal</option>
+ <option value="WRONG_LOOP_SIGNAL">Wrong loop signal</option>
+ <option value="LOOP_SENSOR_PROBLEM_FRONT">Loop sensor problem, front</option>
+ <option value="LOOP_SENSOR_PROBLEM_REAR">Loop sensor problem, rear</option>
+ <option value="LOOP_SENSOR_PROBLEM_LEFT">Loop sensor problem, left</option>
+ <option value="LOOP_SENSOR_PROBLEM_RIGHT">Loop sensor problem, right</option>
+ <option value="WRONG_PIN_CODE">Wrong PIN code</option>
+ <option value="TRAPPED">Trapped</option>
+ <option value="UPSIDE_DOWN">Upside down</option>
+ <option value="EMPTY_BATTERY">Empty battery</option>
+ <option value="NO_DRIVE">No drive</option>
+ <option value="TEMPORARILY_LIFTED">Mower lifted</option>
+ <option value="LIFTED">Lifted</option>
+ <option value="STUCK_IN_CHARGING_STATION">Stuck in charging station</option>
+ <option value="CHARGING_STATION_BLOCKED">Charging station blocked</option>
+ <option value="COLLISION_SENSOR_PROBLEM_REAR">Collision sensor problem, rear</option>
+ <option value="COLLISION_SENSOR_PROBLEM_FRONT">Collision sensor problem, front</option>
+ <option value="WHEEL_MOTOR_BLOCKED_RIGHT">Wheel motor blocked, right</option>
+ <option value="WHEEL_MOTOR_BLOCKED_LEFT">Wheel motor blocked, left</option>
+ <option value="WHEEL_DRIVE_PROBLEM_RIGHT">Wheel drive problem, right</option>
+ <option value="WHEEL_DRIVE_PROBLEM_LEFT">Wheel drive problem, left</option>
+ <option value="CUTTING_MOTOR_DRIVE_DEFECT">Cutting motor drive blocked</option>
+ <option value="CUTTING_SYSTEM_BLOCKED">Cutting system blocked</option>
+ <option value="INVALID_SUB_DEVICE_COMBINATION">Invalid sub-device combination</option>
+ <option value="MEMORY_CIRCUIT_PROBLEM">Memory circuit problem</option>
+ <option value="CHARGING_SYSTEM_PROBLEM">Charging system problem</option>
+ <option value="STOP_BUTTON_PROBLEM">STOP button problem</option>
+ <option value="TILT_SENSOR_PROBLEM">Tilt sensor problem</option>
+ <option value="MOWER_TILTED">Mower tilted</option>
+ <option value="WHEEL_MOTOR_OVERLOADED_RIGHT">Wheel motor overloaded, right</option>
+ <option value="WHEEL_MOTOR_OVERLOADED_LEFT">Wheel motor overloaded, left</option>
+ <option value="CHARGING_CURRENT_TOO_HIGH">Charging current too high</option>
+ <option value="ELECTRONIC_PROBLEM">Electronic problem</option>
+ <option value="CUTTING_MOTOR_PROBLEM">Cutting motor problem</option>
+ <option value="LIMITED_CUTTING_HEIGHT_RANGE">Limited cutting height range</option>
+ <option value="CUTTING_HEIGHT_PROBLEM_DRIVE">Cutting height problem, drive</option>
+ <option value="CUTTING_HEIGHT_PROBLEM_CURR">Cutting height problem, current</option>
+ <option value="CUTTING_HEIGHT_PROBLEM_DIR">Cutting height problem, direction</option>
+ <option value="CUTTING_HEIGHT_BLOCKED">Cutting height blocked</option>
+ <option value="CUTTING_HEIGHT_PROBLEM">Cutting height problem</option>
+ <option value="BATTERY_PROBLEM">Battery problem</option>
+ <option value="TOO_MANY_BATTERIES">Battery problem</option>
+ <option value="ALARM_MOWER_SWITCHED_OFF">Alarm! Mower switched off</option>
+ <option value="ALARM_MOWER_STOPPED">Alarm! Mower stopped</option>
+ <option value="ALARM_MOWER_LIFTED">Alarm! Mower lifted</option>
+ <option value="ALARM_MOWER_TILTED">Alarm! Mower tilted</option>
+ <option value="ALARM_MOWER_IN_MOTION">Alarm! Mower in motion</option>
+ <option value="ALARM_OUTSIDE_GEOFENCE">Alarm! Outside geofence</option>
+ <option value="SLIPPED">Mower has slipped</option>
+ <option value="INVALID_BATTERY_COMBINATION">Invalid battery combination</option>
+ <option value="UNINITIALISED">Unknown status of the mower</option>
+ <option value="WAIT_UPDATING">Mower waiting, updating firmware</option>
+ <option value="WAIT_POWER_UP">Mower powering up</option>
+ <option value="OFF_DISABLED">Mower disabled on main switch</option>
+ <option value="OFF_HATCH_OPEN">Mower in waiting state with hatch open</option>
+ <option value="OFF_HATCH_CLOSED">Mower in waiting state with hatch closed</option>
+ <option value="PARKED_DAILY_LIMIT_REACHED">Mower has completed cutting due to daily limit reached</option>
+
+ <!-- power socket -->
+ <option value="TIMER_CANCELLED">Time cancelled</option>
+
+ <!-- valve -->
+ <option value="CONCURRENT_LIMIT_REACHED">Can't open valve because at most 2 valves can be open at the same time</option>
+ <option value="NOT_CONNECTED">No valve was connected</option>
+ <option value="VALVE_CURRENT_MAX_EXCEEDED">The valve has been closed because the valve is draining more current than allowed</option>
+ <option value="TOTAL_CURRENT_MAX_EXCEEDED">The valve has been closed because the total current used was more than the allowed maximum</option>
+ <option value="WATERING_CANCELED">Watering was canceled</option>
+ <option value="MASTER_VALVE">Master valve is not connected</option>
+ <option value="WATERING_DURATION_TOO_SHORT">Watering duration to short</option>
+ <option value="VALVE_BROKEN">Electrical connection to the valve is broken, or the inductor is damaged</option>
+ <option value="FROST_PREVENTS_STARTING">Because of frost valve stays closed</option>
+ <option value="LOW_BATTERY_PREVENTS_STARTING">Because of low battery valve stays closed</option>
+ <option value="VALVE_POWER_SUPPLY_FAILED">Power supply failed</option>
+
+ <!-- valve set -->
+ <option value="VOLTAGE_DROP">A voltage drop was detected at the power supply</option>
+ <option value="WRONG_POWER_SUPPLY">Wrong power supply is connected</option>
+ <option value="NO_MCU_CONNECTION">Unable to communicate with secondary MCU</option>
</options>
</state>
</channel-type>
- <channel-group-type id="outletPressure">
- <label>Outlet Pressure</label>
- <description>The outlet pressure</description>
- <channels>
- <channel id="outlet_pressure" typeId="outletPressure"/>
- <channel id="outlet_pressure_max" typeId="outletPressureMax"/>
- </channels>
- </channel-group-type>
-
- <channel-type id="outletPressure">
- <item-type>Number</item-type>
- <label>Outlet Pressure</label>
- <description>The outlet pressure</description>
- <state readOnly="true" pattern="%d bar"/>
- </channel-type>
-
- <channel-type id="outletPressureMax">
- <item-type>Number</item-type>
- <label>Outlet Pressure Maximum</label>
- <description>The maximum outlet pressure</description>
- <state readOnly="true" pattern="%d bar"/>
- </channel-type>
-
- <channel-group-type id="flow">
- <label>Flow</label>
- <description>The flow</description>
- <channels>
- <channel id="flow_rate" typeId="flowRate"/>
- <channel id="flow_since_last_reset" typeId="flowSinceLastReset"/>
- <channel id="flow_total" typeId="flowTotal"/>
- <channel id="dripping_alert" typeId="drippingAlert"/>
- </channels>
- </channel-group-type>
-
- <channel-type id="flowRate" advanced="true">
- <item-type>Number</item-type>
- <label>Flow Rate</label>
- <description>The flow rate</description>
- <state readOnly="true" pattern="%d l/h"/>
+ <channel-type id="timestamp" advanced="true">
+ <item-type>DateTime</item-type>
+ <label>Timestamp</label>
+ <description>Timestamp</description>
+ <state readOnly="true"/>
</channel-type>
- <channel-type id="flowSinceLastReset" advanced="true">
- <item-type>Number</item-type>
- <label>Flow Since Last Reset</label>
- <description>The flow since last reset</description>
- <state readOnly="true" pattern="%d m3"/>
+ <channel-type id="timestampRefresh">
+ <item-type>DateTime</item-type>
+ <label>Timestamp</label>
+ <description>Timestamp</description>
</channel-type>
- <channel-type id="flowTotal" advanced="true">
- <item-type>Number</item-type>
- <label>Total Flow</label>
- <description>The total flow</description>
- <state readOnly="true" pattern="%d m3"/>
+ <channel-type id="name">
+ <item-type>String</item-type>
+ <label>Name</label>
+ <description>The name of the device</description>
+ <state readOnly="true"/>
</channel-type>
- <channel-type id="drippingAlert">
+ <channel-type id="activity">
<item-type>String</item-type>
- <label>Dripping Alert</label>
- <description>The dripping alert</description>
+ <label>Activity</label>
+ <description>The activity of the device</description>
<state readOnly="true">
<options>
- <option value="sixty">Sixty</option>
- <option value="two">Two</option>
- <option value="off">Off</option>
+ <!-- power socker -->
+ <option value="OFF">Off</option>
+ <option value="FOREVER_ON">Switched on by a manual action, no switch off scheduled</option>
+ <option value="TIME_LIMITED_ON">Switched on by a manual action, the switch off is scheduled</option>
+ <option value="SCHEDULED_ON">Operating by schedule, current state is 'on'</option>
+
+ <!-- valve -->
+ <option value="CLOSED">Valve closed</option>
+ <option value="MANUAL_WATERING">Irrigation active</option>
+ <option value="SCHEDULED_WATERING">Irrigation active</option>
+
+ <!-- mower -->
+ <option value="PAUSED">In a waiting state with hatch closed</option>
+ <option value="OK_CUTTING">Mowing</option>
+ <option value="OK_CUTTING_TIMER_OVERRIDDEN">Mowing</option>
+ <option value="OK_SEARCHING">Searching charging station</option>
+ <option value="OK_LEAVING">Leaving charging station</option>
+ <option value="OK_CHARGING">Charging</option>
+ <option value="PARKED_TIMER">Parked according to timer</option>
+ <option value="PARKED_PARK_SELECTED">Parked until further notice</option>
+ <option value="PARKED_AUTOTIMER">Parked because of insufficient grass height</option>
+
+ <option value="NONE">None</option>
</options>
</state>
</channel-type>
- <channel-group-type id="manualWatering">
- <label>Manual Watering</label>
- <description>Manual Watering</description>
- <channels>
- <channel id="manual_watering_timer" typeId="manualWateringTimer"/>
- <channel id="last_manual_override_time" typeId="lastManualOverrideTime"/>
- </channels>
- </channel-group-type>
-
- <channel-type id="manualWateringTimer">
- <item-type>Number</item-type>
- <label>Manual Watering Timer</label>
- <description>The manual watering timer</description>
- <state pattern="%d min" min="0" max="59" step="1">
- <options>
- <option value="0">Off</option>
- <option value="1">1 min</option>
- <option value="3">3 min</option>
- <option value="5">5 min</option>
- <option value="10">10 min</option>
- <option value="15">15 min</option>
- <option value="20">20 min</option>
- <option value="25">25 min</option>
- <option value="30">30 min</option>
- <option value="35">35 min</option>
- <option value="40">40 min</option>
- <option value="45">45 min</option>
- <option value="50">50 min</option>
- <option value="55">55 min</option>
- <option value="59">59 min</option>
- </options>
- </state>
- </channel-type>
-
- <channel-type id="lastManualOverrideTime" advanced="true">
- <item-type>Number</item-type>
- <label>Last Manual Override Timer</label>
- <description>The last manual override timer</description>
+ <channel-type id="duration">
+ <item-type>Number:Time</item-type>
+ <label>Duration</label>
+ <description>Duration in minutes</description>
<state readOnly="true" pattern="%d min"/>
</channel-type>
- <channel-group-type id="scheduling">
- <label>Manual Watering</label>
- <description>Manual Watering</description>
- <channels>
- <channel id="scheduled_watering_end" typeId="scheduledWateringEnd"/>
- <channel id="scheduled_watering_next_start" typeId="scheduledWateringNextStart"/>
- <channel id="adaptive_scheduling_last_decision" typeId="adaptiveSchedulingLastDecision"/>
- </channels>
- </channel-group-type>
-
- <channel-type id="scheduledWateringEnd" advanced="true">
- <item-type>DateTime</item-type>
- <label>Scheduled Watering End</label>
- <description>The scheduled watering end</description>
- <state readOnly="true"/>
+ <channel-type id="soilHumidity">
+ <item-type>Number:Dimensionless</item-type>
+ <label>Soil Humidity</label>
+ <description>Soil humidity in percent</description>
+ <state readOnly="true" pattern="%d %unit%"/>
</channel-type>
- <channel-type id="scheduledWateringNextStart" advanced="true">
- <item-type>DateTime</item-type>
- <label>Scheduled Watering Next Start</label>
- <description>The scheduled watering next start</description>
- <state readOnly="true"/>
+ <channel-type id="lightIntensity">
+ <item-type>Number:Illuminance</item-type>
+ <label>Light Intensity</label>
+ <description>Light intensity in Lux</description>
+ <state readOnly="true" pattern="%d lux"/>
</channel-type>
- <channel-type id="adaptiveSchedulingLastDecision" advanced="true">
- <item-type>String</item-type>
- <label>Adaptive Scheduling Last Decision</label>
- <description>The adaptive scheduling last decision</description>
- <state readOnly="true">
- <options>
- <option value="undefined">Undefined</option>
- <option value="watered_sensor_timeout">watered_sensor_timeout</option>
- <option value="skipped">skipped</option>
- <option value="watered">watered</option>
- </options>
- </state>
+ <channel-type id="temperature">
+ <item-type>Number:Temperature</item-type>
+ <label>Temperature</label>
+ <description>The temperature</description>
+ <state readOnly="true" pattern="%d %unit%"/>
</channel-type>
- <!-- ############################### Power ############################### -->
- <thing-type id="power">
- <supported-bridge-type-refs>
- <bridge-type-ref id="account"/>
- </supported-bridge-type-refs>
-
- <label>Gardena Smart Power Plug</label>
- <description>A Gardena Smart Power Plug</description>
-
- <channel-groups>
- <channel-group id="device_info" typeId="deviceInfo"/>
- <channel-group id="radio" typeId="radio"/>
- <channel-group id="power" typeId="power"/>
- <channel-group id="firmware" typeId="firmware"/>
- </channel-groups>
- <properties>
- <!-- device info -->
- <property name="manufacturer"/>
- <property name="product"/>
- <property name="serialNumber"/>
- <property name="sgtin"/>
- <property name="version"/>
- <property name="category"/>
- </properties>
- </thing-type>
-
- <channel-group-type id="power">
- <label>Power</label>
- <description>Power</description>
- <channels>
- <channel id="power_timer" typeId="powerTimer"/>
- <channel id="error" typeId="powerError"/>
- </channels>
- </channel-group-type>
-
- <channel-type id="powerTimer">
- <item-type>String</item-type>
- <label>Power Timer</label>
- <description>The power timer</description>
- <state>
- <options>
- <option value="on">On</option>
- <option value="off">Off</option>
- <option value="60">1 Minute</option>
- <option value="300">5 Minutes</option>
- <option value="900">15 Minutes</option>
- <option value="3600">1 Hour</option>
- <option value="7200">2 Hours</option>
- <option value="18000">5 Hours</option>
- <option value="43200">12 Hours</option>
- <option value="86400">1 Day</option>
- <option value="259200">3 Days</option>
- <option value="604800">1 Week</option>
- <option value="1209600">2 Weeks</option>
- </options>
- </state>
+ <channel-type id="operatingHours">
+ <item-type>Number:Time</item-type>
+ <label>Operating Hours</label>
+ <description>The operating hours</description>
+ <state readOnly="true" pattern="%d %unit%"/>
</channel-type>
- <channel-type id="powerError">
+ <channel-type id="batteryState">
<item-type>String</item-type>
- <label>Power Error</label>
- <description>Power Error</description>
+ <label>Battery State</label>
+ <description>The state of the battery</description>
<state readOnly="true">
<options>
- <option value="ok">OK</option>
- <option value="timer_cancelled">Timer Cancelled</option>
- <option value="unknown">Unknown</option>
- </options>
- </state>
- </channel-type>
-
-
- <!-- ############################### Irrigation Control ############################### -->
-
- <thing-type id="ic24">
- <supported-bridge-type-refs>
- <bridge-type-ref id="account"/>
- </supported-bridge-type-refs>
-
- <label>Gardena Smart Irrigation Control</label>
- <description>A Gardena Smart Irrigation Control</description>
-
- <channel-groups>
- <channel-group id="device_info" typeId="deviceInfo"/>
- <channel-group id="radio" typeId="radio"/>
-
- <channel-group id="watering" typeId="watering"/>
- <channel-group id="ic24" typeId="ic24"/>
- <channel-group id="scheduling" typeId="ic24scheduling"/>
-
- <channel-group id="firmware" typeId="firmware"/>
- </channel-groups>
- <properties>
- <!-- device info -->
- <property name="manufacturer"/>
- <property name="product"/>
- <property name="serialNumber"/>
- <property name="sgtin"/>
- <property name="version"/>
- <property name="category"/>
- </properties>
- </thing-type>
-
- <channel-group-type id="watering">
- <label>Watering</label>
- <description>The watering</description>
- <channels>
- <channel id="watering_timer_1" typeId="wateringTimer">
- <label>Watering Timer 1</label>
- <description>The watering timer for valve 1</description>
- </channel>
- <channel id="watering_timer_2" typeId="wateringTimer">
- <label>Watering Timer 2</label>
- <description>The watering timer for valve 2</description>
- </channel>
- <channel id="watering_timer_3" typeId="wateringTimer">
- <label>Watering Timer 3</label>
- <description>The watering timer for valve 3</description>
- </channel>
- <channel id="watering_timer_4" typeId="wateringTimer">
- <label>Watering Timer 4</label>
- <description>The watering timer for valve 4</description>
- </channel>
- <channel id="watering_timer_5" typeId="wateringTimer">
- <label>Watering Timer 5</label>
- <description>The watering timer for valve 5</description>
- </channel>
- <channel id="watering_timer_6" typeId="wateringTimer">
- <label>Watering Timer 6</label>
- <description>The watering timer for valve 6</description>
- </channel>
- </channels>
- </channel-group-type>
-
- <channel-type id="wateringTimer">
- <item-type>Number</item-type>
- <label>Watering Timer</label>
- <state pattern="%d min">
- <options>
- <option value="0">Off</option>
- <option value="1">1 min</option>
- <option value="3">3 min</option>
- <option value="5">5 min</option>
- <option value="10">10 min</option>
- <option value="15">15 min</option>
- <option value="20">20 min</option>
- <option value="25">25 min</option>
- <option value="30">30 min</option>
- <option value="35">35 min</option>
- <option value="40">40 min</option>
- <option value="45">45 min</option>
- <option value="50">50 min</option>
- <option value="55">55 min</option>
- <option value="59">59 min</option>
+ <option value="OK">OK</option>
+ <option value="LOW">Low</option>
+ <option value="REPLACE_NOW">Replace now</option>
+ <option value="OUT_OF_OPERATION">Out of operation</option>
+ <option value="CHARGING">Charging</option>
+ <option value="NO_BATTERY">No battery</option>
+ <option value="UNKNOWN">Unknown</option>
</options>
</state>
</channel-type>
- <channel-group-type id="ic24">
- <label>Irrigation Control</label>
- <description>The irrigation control</description>
- <channels>
- <channel id="valves_connected" typeId="valvesConnected"/>
- <channel id="valves_master_config" typeId="valvesMasterConfig"/>
- <channel id="error" typeId="ic24error"/>
- <channel id="valve_error_0" typeId="valveError">
- <label>Valve Error 0</label>
- <description>The error for valve 0</description>
- </channel>
- <channel id="valve_error_1" typeId="valveError">
- <label>Valve Error 1</label>
- <description>The error for valve 1</description>
- </channel>
- <channel id="valve_error_2" typeId="valveError">
- <label>Valve Error 2</label>
- <description>The error for valve 2</description>
- </channel>
- <channel id="valve_error_3" typeId="valveError">
- <label>Valve Error 3</label>
- <description>The error for valve 3</description>
- </channel>
- <channel id="valve_error_4" typeId="valveError">
- <label>Valve Error 4</label>
- <description>The error for valve 4</description>
- </channel>
- <channel id="valve_error_5" typeId="valveError">
- <label>Valve Error 5</label>
- <description>The error for valve 5</description>
- </channel>
- <channel id="valve_error_6" typeId="valveError">
- <label>Valve Error 6</label>
- <description>The error for valve 6</description>
- </channel>
- </channels>
- </channel-group-type>
-
- <channel-type id="valvesConnected" advanced="true">
- <item-type>String</item-type>
- <label>Valves Connected</label>
- <description>The connected valves</description>
- <state readOnly="true"/>
- </channel-type>
-
- <channel-type id="valvesMasterConfig" advanced="true">
- <item-type>String</item-type>
- <label>Valves Master Config</label>
- <description>The valves master config</description>
- <state readOnly="true"/>
- </channel-type>
-
- <channel-type id="ic24error" advanced="true">
+ <channel-type id="rfLinkState">
<item-type>String</item-type>
- <label>Irrigation Control Error</label>
- <description>The irrigation control error</description>
+ <label>RF Link State</label>
+ <description>The state of the RF link</description>
<state readOnly="true">
<options>
- <option value="ok">OK</option>
- <option value="voltage_drop">Voltage Drop</option>
- <option value="wrong_power_supply">Wrong Power Supply</option>
- <option value="no_mcu_connection">No Mcu Connection</option>
- <option value="unknown">Unknown</option>
+ <option value="ONLINE">Online</option>
+ <option value="OFFLINE">Offline</option>
+ <option value="UNKNOWN">Unknown</option>
</options>
</state>
</channel-type>
- <channel-type id="valveError" advanced="true">
- <item-type>String</item-type>
- <label>Valve Error</label>
- <state readOnly="true"/>
- </channel-type>
-
- <channel-group-type id="ic24scheduling">
- <label>Scheduling</label>
- <description>The scheduled watering</description>
- <channels>
- <channel id="scheduled_watering_next_start_1" typeId="ic24ScheduledWatering">
- <label>Scheduled Watering Next Start 1</label>
- <description>The scheduled watering next start for valve 1</description>
- </channel>
- <channel id="scheduled_watering_end_1" typeId="ic24ScheduledWatering">
- <label>Scheduled Watering End 1</label>
- <description>The scheduled watering end for valve 1</description>
- </channel>
- <channel id="scheduled_watering_next_start_2" typeId="ic24ScheduledWatering">
- <label>Scheduled Watering Next Start 2</label>
- <description>The scheduled watering next start for valve 2</description>
- </channel>
- <channel id="scheduled_watering_end_2" typeId="ic24ScheduledWatering">
- <label>Scheduled Watering End 2</label>
- <description>The scheduled watering end for valve 2</description>
- </channel>
- <channel id="scheduled_watering_next_start_3" typeId="ic24ScheduledWatering">
- <label>Scheduled Watering Next Start 3</label>
- <description>The scheduled watering next start for valve 3</description>
- </channel>
- <channel id="scheduled_watering_end_3" typeId="ic24ScheduledWatering">
- <label>Scheduled Watering End 3</label>
- <description>The scheduled watering end for valve 3</description>
- </channel>
- <channel id="scheduled_watering_next_start_4" typeId="ic24ScheduledWatering">
- <label>Scheduled Watering Next Start 4</label>
- <description>The scheduled watering next start for valve 4</description>
- </channel>
- <channel id="scheduled_watering_end_4" typeId="ic24ScheduledWatering">
- <label>Scheduled Watering End 4</label>
- <description>The scheduled watering end for valve 4</description>
- </channel>
- <channel id="scheduled_watering_next_start_5" typeId="ic24ScheduledWatering">
- <label>Scheduled Watering Next Start 5</label>
- <description>The scheduled watering next start for valve 5</description>
- </channel>
- <channel id="scheduled_watering_end_5" typeId="ic24ScheduledWatering">
- <label>Scheduled Watering End 5</label>
- <description>The scheduled watering end for valve 5</description>
- </channel>
- <channel id="scheduled_watering_next_start_6" typeId="ic24ScheduledWatering">
- <label>Scheduled Watering Next Start 6</label>
- <description>The scheduled watering next start for valve 6</description>
- </channel>
- <channel id="scheduled_watering_end_6" typeId="ic24ScheduledWatering">
- <label>Scheduled Watering End 6</label>
- <description>The scheduled watering end for valve 6</description>
- </channel>
- </channels>
- </channel-group-type>
-
- <channel-type id="ic24ScheduledWatering" advanced="true">
- <item-type>DateTime</item-type>
- <label>Scheduled Watering</label>
- <state readOnly="true"/>
- </channel-type>
-
-
</thing:thing-descriptions>