diff --git a/bundles/org.openhab.binding.gardena/README.md b/bundles/org.openhab.binding.gardena/README.md index 410d7e8c969..bdadb044845 100644 --- a/bundles/org.openhab.binding.gardena/README.md +++ b/bundles/org.openhab.binding.gardena/README.md @@ -1,23 +1,21 @@ # 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 @@ -27,109 +25,94 @@ An account must be specified, all things for an account are discovered automatic 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 + +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_ + +## Examples ### Things Minimal Thing configuration: ```java -Bridge gardena:account:home [ email="...", password="..." ] -``` - -Configuration with refresh: - -```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 - -In the console: - -```shell -smarthome:send ITEM_NAME REFRESH -``` - -In scripts: - -``` -import org.openhab.core.types.RefreshType -... -sendCommand(ITEM_NAME, RefreshType.REFRESH) -``` - -## Examples +### Example configuration ``` // 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" } +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" } -// smart Power Plug -String Power_Timer "Power Timer [%s]" { channel="gardena:power:home:myPowerplug:power#power_timer" } +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" } -// 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: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) ``` +All channels are read-only, except the command group and the lastUpdate timestamp + +``` +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 ``` -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 +If you send a REFRESH command to the last update timestamp (no matter which thing), **ALL** items from **ALL** things are updated +``` +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 @@ -146,12 +129,21 @@ Set the logging back to normal 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. + diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaBindingConstants.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaBindingConstants.java index c47086205dd..232db4d6771 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaBindingConstants.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaBindingConstants.java @@ -16,32 +16,25 @@ import org.eclipse.jdt.annotation.NonNullByDefault; 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_SERIALNUMBER = "serial"; + public static final String PROPERTY_MODELTYPE = "modelType"; - 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 CONNECTION_STATUS_ONLINE = "ONLINE"; - public static final String ABILITY_DEVICE_INFO = "device_info"; - public static final String ABILITY_RADIO = "radio"; - - 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"; } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmart.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmart.java index 9bc62886652..0541f663623 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmart.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmart.java @@ -12,60 +12,50 @@ */ 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. + * Returns all devices from all locations. */ - public void loadAllDevices() throws GardenaException; - - /** - * Returns all locations. - */ - public Set getLocations(); + public Collection 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; - - /** - * Sends a setting to Gardena Smart Home. - */ - public void sendSetting(Setting setting, Object value) throws GardenaException; + public void sendCommand(DataItem dataItem, GardenaCommand gardenaCommand) throws GardenaException; /** * Returns the id. */ public String getId(); + + /** + * Restarts all WebSocket. + */ + public void restartWebsockets(); } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartCommandName.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartCommandName.java deleted file mode 100644 index 810ab359608..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartCommandName.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * 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 -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartEventListener.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartEventListener.java index 224502c0c9e..b815e0640ec 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartEventListener.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartEventListener.java @@ -12,13 +12,15 @@ */ 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 { /** @@ -32,17 +34,7 @@ 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(); } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartImpl.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartImpl.java index 53b7ebce7ef..edccf6a945d 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartImpl.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartImpl.java @@ -12,179 +12,375 @@ */ 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 java.util.*; +import java.util.concurrent.*; -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.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 Gson gson = new GsonBuilder().registerTypeAdapter(DataItem.class, new DataItemDeserializer()).create(); - 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 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 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 String mowerDuration = DEFAULT_MOWER_DURATION; - private Session session; - private GardenaConfig config; private String id; - + private GardenaConfig config; private ScheduledExecutorService scheduler; - private ScheduledFuture refreshThreadFuture; - private RefreshDevicesThread refreshDevicesThread = new RefreshDevicesThread(); - - private GardenaSmartEventListener eventListener; private Map allDevicesById = new HashMap<>(); - private Set allLocations = new HashSet<>(); + private LocationsResponse locationsResponse; + private GardenaSmartEventListener eventListener; - @Override - public void init(String id, GardenaConfig config, GardenaSmartEventListener eventListener, - ScheduledExecutorService scheduler) throws GardenaException { + private HttpClient httpClient; + private List webSockets = new ArrayList<>(); + private @Nullable PostOAuth2Response token; + private boolean initialized = false; + private WebSocketFactory webSocketFactory; + + private Set 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(); + + // initially load access token + verifyToken(); + locationsResponse = loadLocations(); + + // 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(); + } + + startWebsockets(); + initialized = true; } catch (Exception ex) { throw new GardenaException(ex.getMessage(), ex); } + } - loadAllDevices(); + /** + * Starts the websockets for each location. + */ + 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 all websockets. + */ + private void stopWebsockets() { + for (GardenaSmartWebSocket webSocket : webSockets) { + webSocket.stop(); + } + webSockets.clear(); + } + + /** + * Communicates with Gardena smart home system and parses the result. + */ + private T executeRequest(HttpMethod method, String url, @Nullable Object content, @Nullable Class 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)); + } + } + + if (logger.isTraceEnabled()) { + logger.trace(">>> {} {}, data: {}", method, url, content == null ? null : gson.toJson(content)); + } + + Request request = httpClient.newRequest(url).method(method).header(HttpHeader.CONTENT_TYPE, contentType) + .header(HttpHeader.ACCEPT, "application/vnd.api+json").header(HttpHeader.ACCEPT_ENCODING, "gzip"); + + 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()); + } + + request.content(contentProvider); + ContentResponse contentResponse = request.send(); + int status = contentResponse.getStatus(); + if (logger.isTraceEnabled()) { + logger.trace("<<< status:{}, {}", status, contentResponse.getContentAsString()); + } + + if (status != 200 && status != 204 && status != 201 && status != 202) { + throw new GardenaException(String.format("Error %s %s, %s", status, contentResponse.getReason(), + contentResponse.getContentAsString())); + } + + if (result == null) { + return (T) null; + } + return (T) gson.fromJson(contentResponse.getContentAsString(), result); + } catch (InterruptedException | TimeoutException | ExecutionException ex) { + throw new GardenaException(ex.getMessage(), ex); + } + } + + /** + * 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()); + } + + /** + * Loads all locations. + */ + private LocationsResponse loadLocations() throws GardenaException { + return executeRequest(HttpMethod.GET, URL_API_LOCATIONS, null, LocationsResponse.class); + } + + /** + * Loads all devices for a given location. + */ + private LocationResponse loadLocation(String locationId) throws GardenaException { + return executeRequest(HttpMethod.GET, URL_API_LOCATIONS + "/" + locationId, null, LocationResponse.class); + } + + /** + * Returns the websocket url for a given location. + */ + private WebSocketCreatedResponse getWebsocketInfo(String locationId) throws GardenaException { + return executeRequest(HttpMethod.POST, URL_API_WEBSOCKET, new CreateWebSocketRequest(locationId), + WebSocketCreatedResponse.class); + } + + /** + * Stops the client. + */ + public void dispose() { + logger.debug("Disposing GardenaSmart"); + + final ScheduledFuture newDeviceFuture = this.newDeviceFuture; + if (newDeviceFuture != null) { + newDeviceFuture.cancel(true); + } + + 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; + } + + /** + * Restarts all websockets. + */ + @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(); + } + } + + /** + * Sets the dataItem from the websocket event into the correct 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); + } + } + }, 3, TimeUnit.SECONDS); + } + } + + if (device != null) { + device.setDataItem(dataItem); + } } @Override - public void dispose() { - stopRefreshThread(true); - if (httpClient != null) { - try { - httpClient.stop(); - } catch (Exception e) { - // ignore + public void onWebSocketClose() { + restartWebsockets(); + } + + @Override + public void onWebSocketError() { + eventListener.onError(); + } + + @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 notifyIterator = devicesToNotify.iterator(); + while (notifyIterator.hasNext()) { + eventListener.onDeviceUpdated(notifyIterator.next()); + notifyIterator.remove(); + } + }, 1, TimeUnit.SECONDS); + } + } } - httpClient.destroy(); + } catch (GardenaException | JsonSyntaxException ex) { + logger.warn("Ignoring message: {}", ex.getMessage()); } - allLocations.clear(); - allDevicesById.clear(); } - /** - * Schedules the device refresh thread. - */ - private void startRefreshThread() { - refreshThreadFuture = scheduler.scheduleWithFixedDelay(refreshDevicesThread, 6, config.getRefresh(), - TimeUnit.SECONDS); + @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; } - /** - * Stops the device refresh thread. - */ - private void stopRefreshThread(boolean force) { - if (refreshThreadFuture != null) { - refreshThreadFuture.cancel(force); - } + @Override + public void sendCommand(DataItem dataItem, GardenaCommand gardenaCommand) throws GardenaException { + executeRequest(HttpMethod.PUT, URL_API_COMMAND + "/" + dataItem.id, new GardenaCommandRequest(gardenaCommand), + null); } @Override @@ -193,381 +389,7 @@ public class GardenaSmartImpl implements GardenaSmart { } @Override - public Set 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(); - } - } - - /** - * Loads all devices for the location, adds virtual properties for commands. - */ - 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))); - } - } - } - } - for (Setting setting : device.getSettings()) { - setting.setDevice(device); - } - - 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")); - - mower.addProperty(new Property(GardenaSmartCommandName.DURATION_PROPERTY, mowerDuration)); - } - } - 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)); - - executeSetProperty(device, ABILITY_MANUAL_WATERING, PROPERTY_MANUAL_WATERING_TIMER, prop); - break; - default: - throw new GardenaException("Unknown command " + commandName); - } - - if (command != null) { - stopRefreshThread(false); - executeRequest(HttpMethod.POST, getCommandUrl(device, ability), command, NoResult.class); - startRefreshThread(); - } - } - - private Integer getIntegerValue(Object value) { - try { - return Integer.valueOf(ObjectUtils.toString(value)); - } catch (NumberFormatException ex) { - return null; - } - } - - /** - * Sends the new property value for the ability. - */ - 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(); - } - - /** - * Returns the command url. - */ - private String getCommandUrl(Device device, Ability ability) throws GardenaException { - return String.format(URL_COMMAND, device.getId(), ability.getName(), device.getLocation().getId()); - } - - /** - * Returns the settings url. - */ - private String getSettingUrl(Setting setting) { - Device device = setting.getDevice(); - return String.format(URL_SETTING, device.getId(), setting.getId(), device.getLocation().getId()); - } - - /** - * Communicates with Gardena Smart Home and parses the result. - */ - private synchronized T executeRequest(HttpMethod method, String url, Object contentObject, Class 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())); - } - - if (result == NoResult.class) { - return null; - } - - 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); - } - } - - /** - * Verifies the Gardena Smart Home session and reconnects if necessary. - */ - 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(); - } - } - - /** - * Thread which refreshes the data from Gardena Smart Home. - */ - private class RefreshDevicesThread implements Runnable { - private boolean connectionLost = false; - - @Override - public void run() { - try { - logger.debug("Refreshing gardena device data"); - final Map 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); - } - } - } - - if (connectionLost) { - connectionLost = false; - logger.info("Connection resumed to Gardena Smart Home with id '{}'", id); - eventListener.onConnectionResumed(); - } - - // determine deleted devices - Collection deletedDevices = allDevicesById.values().stream() - .filter(d -> !newDevicesById.values().contains(d)).collect(Collectors.toSet()); - - // determine new devices - Collection newDevices = newDevicesById.values().stream() - .filter(d -> !allDevicesById.values().contains(d)).collect(Collectors.toSet()); - - // determine updated devices - Collection updatedDevices = allDevicesById.values().stream().distinct() - .filter(newDevicesById.values()::contains).collect(Collectors.toSet()); - - allDevicesById = newDevicesById; - - for (Device deletedDevice : deletedDevices) { - eventListener.onDeviceDeleted(deletedDevice); - } - - for (Device newDevice : newDevices) { - eventListener.onNewDevice(newDevice); - } - - for (Device updatedDevice : updatedDevices) { - eventListener.onDeviceUpdated(updatedDevice); - } - - } 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(); - } - } - } + public Collection getAllDevices() { + return allDevicesById.values(); } } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartWebSocket.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartWebSocket.java new file mode 100644 index 00000000000..227d8181e2f --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartWebSocket.java @@ -0,0 +1,175 @@ +/** + * 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()); + } + } +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartWebSocketListener.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartWebSocketListener.java new file mode 100644 index 00000000000..d6283ecfcc3 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/GardenaSmartWebSocketListener.java @@ -0,0 +1,42 @@ +/** + * 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); +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/config/GardenaConfig.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/config/GardenaConfig.java index 6a8e3795ebd..2e0effd1360 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/config/GardenaConfig.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/config/GardenaConfig.java @@ -12,30 +12,23 @@ */ 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() { } @@ -46,87 +39,78 @@ public class 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(); } } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/config/GardenaConfigDataWrapper.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/config/GardenaConfigDataWrapper.java deleted file mode 100644 index 2f3c8fbf34c..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/config/GardenaConfigDataWrapper.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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; - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/config/GardenaConfigWrapper.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/config/GardenaConfigWrapper.java deleted file mode 100644 index be0ded0fb8f..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/config/GardenaConfigWrapper.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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); - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/discovery/GardenaDeviceDiscoveryService.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/discovery/GardenaDeviceDiscoveryService.java index 9dcba16fcc7..15ba8be028e 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/discovery/GardenaDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/discovery/GardenaDeviceDiscoveryService.java @@ -25,8 +25,8 @@ import org.eclipse.jdt.annotation.Nullable; 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; @@ -41,18 +41,19 @@ import org.slf4j.Logger; 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())), @@ -94,6 +95,7 @@ public class GardenaDeviceDiscoveryService extends AbstractDiscoveryService @Override public void stopScan() { logger.debug("Stopping Gardena discovery scan"); + final Future scanFuture = this.scanFuture; if (scanFuture != null) { scanFuture.cancel(true); } @@ -101,18 +103,15 @@ public class GardenaDeviceDiscoveryService extends AbstractDiscoveryService } /** - * 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()) { @@ -123,11 +122,7 @@ public class GardenaDeviceDiscoveryService extends AbstractDiscoveryService } } - 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()); } @@ -141,6 +136,7 @@ public class GardenaDeviceDiscoveryService extends AbstractDiscoveryService * 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 { @@ -158,19 +154,19 @@ public class GardenaDeviceDiscoveryService extends AbstractDiscoveryService * Generates the DiscoveryResult from a Gardena device. */ public void deviceDiscovered(Device device) { - ThingUID accountUID = accountHandler.getThing().getUID(); - ThingUID thingUID = UidUtils.generateThingUID(device, accountHandler.getThing()); + if (device.active) { + 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); - } - - /** - * 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()); + } + } } } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/exception/GardenaDeviceNotFoundException.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/exception/GardenaDeviceNotFoundException.java index 1cf9ec03d9f..b399138a98f 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/exception/GardenaDeviceNotFoundException.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/exception/GardenaDeviceNotFoundException.java @@ -12,12 +12,15 @@ */ 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; diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/exception/GardenaException.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/exception/GardenaException.java index cadb6b2d44b..7f729daa11d 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/exception/GardenaException.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/exception/GardenaException.java @@ -14,11 +14,15 @@ package org.openhab.binding.gardena.internal.exception; 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; @@ -31,7 +35,7 @@ public class GardenaException extends IOException { super(ex); } - public GardenaException(String message, Throwable cause) { + public GardenaException(@Nullable String message, Throwable cause) { super(message, cause); } } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/AccountHandlerNotAvailableException.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/AccountHandlerNotAvailableException.java index fc7d183b9cf..4824cc86a89 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/AccountHandlerNotAvailableException.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/AccountHandlerNotAvailableException.java @@ -12,11 +12,14 @@ */ 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; diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaAccountHandler.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaAccountHandler.java index 2e07e040ff7..8d729890c62 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaAccountHandler.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaAccountHandler.java @@ -16,21 +16,19 @@ import java.util.Collection; 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; @@ -39,31 +37,31 @@ import org.slf4j.Logger; 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(); } @@ -78,26 +76,35 @@ public class GardenaAccountHandler extends BaseBridgeHandler implements GardenaS 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); } @@ -112,16 +119,20 @@ public class GardenaAccountHandler extends BaseBridgeHandler implements GardenaS */ 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; } @@ -142,27 +153,27 @@ public class GardenaAccountHandler extends BaseBridgeHandler implements GardenaS @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); } @@ -170,19 +181,9 @@ public class GardenaAccountHandler extends BaseBridgeHandler implements GardenaS } @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(); } } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaDeviceConfig.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaDeviceConfig.java index b6bcbaafbb1..9c8814aeb50 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaDeviceConfig.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaDeviceConfig.java @@ -12,12 +12,15 @@ */ 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; } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaHandlerFactory.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaHandlerFactory.java index 0df66b460ca..6581ec91dd7 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaHandlerFactory.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaHandlerFactory.java @@ -12,23 +12,43 @@ */ 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) { @@ -36,11 +56,11 @@ public class GardenaHandlerFactory extends BaseThingHandlerFactory { } @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); } } } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaThingHandler.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaThingHandler.java index 4f1cbb26757..7cf42a813c5 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaThingHandler.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/handler/GardenaThingHandler.java @@ -13,41 +13,37 @@ 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; @@ -57,13 +53,15 @@ 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 @@ -71,7 +69,6 @@ public class GardenaThingHandler extends BaseThingHandler { try { Device device = getDevice(); updateProperties(device); - updateSettings(device); updateStatus(device); } catch (GardenaException ex) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, ex.getMessage()); @@ -80,28 +77,13 @@ public class GardenaThingHandler extends BaseThingHandler { } } - /** - * 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(); } /** @@ -109,22 +91,15 @@ public class GardenaThingHandler extends BaseThingHandler { */ protected void updateProperties(Device device) throws GardenaException { Map 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 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 @@ -142,177 +117,163 @@ public class GardenaThingHandler extends BaseThingHandler { * 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) { @@ -320,32 +281,22 @@ public class GardenaThingHandler extends BaseThingHandler { } } - @Override - public void handleConfigurationUpdate(Map configurationParameters) - throws ConfigValidationException { - validateConfigurationParameters(configurationParameters); - - try { - GardenaSmart gardena = getGardenaSmart(); - Device device = gardena.getDevice(UidUtils.getGardenaDeviceId(getThing())); - - for (Entry 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()); } /** @@ -356,17 +307,20 @@ public class GardenaThingHandler extends BaseThingHandler { } /** - * 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!"); } } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Ability.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Ability.java deleted file mode 100644 index d3f942ef7c3..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Ability.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * 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 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 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; - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/DataItemDeserializer.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/DataItemDeserializer.java new file mode 100644 index 00000000000..4ee3e75fddf --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/DataItemDeserializer.java @@ -0,0 +1,43 @@ +/** + * 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> { + 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); + } + } +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/DataItemFactory.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/DataItemFactory.java new file mode 100644 index 00000000000..d6759a0faf9 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/DataItemFactory.java @@ -0,0 +1,48 @@ +/** + * 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> 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); + } + } +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Device.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Device.java deleted file mode 100644 index 7a39a93ed3e..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Device.java +++ /dev/null @@ -1,147 +0,0 @@ -/** - * 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 abilities = new ArrayList<>(); - @SerializedName("scheduled_events") - private List scheduledEvents = new ArrayList<>(); - private transient Location location; - private List 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 getAbilities() { - return abilities; - } - - /** - * Returns a list of scheduled events of the device. - */ - public List 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 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(); - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Error.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Error.java deleted file mode 100644 index c8f5e1c970f..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Error.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * 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(); - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Errors.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Errors.java deleted file mode 100644 index 13fd38ca2c6..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Errors.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * 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 errors = new ArrayList<>(); - - /** - * Returns a list of Gardena errors. - */ - public List 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(); - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Location.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Location.java deleted file mode 100644 index 404cf211684..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Location.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * 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 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 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(); - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Property.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Property.java deleted file mode 100644 index 98fd07a0f7d..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Property.java +++ /dev/null @@ -1,117 +0,0 @@ -/** - * 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 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 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; - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/PropertyValue.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/PropertyValue.java deleted file mode 100644 index 12af1aa3284..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/PropertyValue.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * 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; - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Recurrence.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Recurrence.java deleted file mode 100644 index 0cf6fea986b..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Recurrence.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * 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 weekdays; - - /** - * Returns the type of the recurrence. - */ - public String getType() { - return type; - } - - /** - * Returns a list of weekdays. - */ - public List getWeekdays() { - return weekdays; - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/ScheduledEvent.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/ScheduledEvent.java deleted file mode 100644 index d7e20e251cd..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/ScheduledEvent.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * 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; - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Session.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Session.java deleted file mode 100644 index f860f1554e4..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Session.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * 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; - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/SessionAttributes.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/SessionAttributes.java deleted file mode 100644 index e4a039c5434..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/SessionAttributes.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * 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; - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/SessionWrapper.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/SessionWrapper.java deleted file mode 100644 index f4f74ac8736..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/SessionWrapper.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * 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; - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Setting.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Setting.java deleted file mode 100644 index ea4e2319146..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Setting.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * 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; - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/Command.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/Command.java deleted file mode 100644 index 26e46adbdcc..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/Command.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * 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; - } - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/MowerStartOverrideTimerCommand.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/MowerStartOverrideTimerCommand.java deleted file mode 100644 index c0ae40697f4..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/MowerStartOverrideTimerCommand.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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); - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SensorMeasureAmbientTemperatureCommand.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SensorMeasureAmbientTemperatureCommand.java deleted file mode 100644 index 0fb44315eeb..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SensorMeasureAmbientTemperatureCommand.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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); - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SensorMeasureSoilHumidityCommand.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SensorMeasureSoilHumidityCommand.java deleted file mode 100644 index 13173843e1f..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SensorMeasureSoilHumidityCommand.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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); - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SensorMeasureSoilTemperatureCommand.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SensorMeasureSoilTemperatureCommand.java deleted file mode 100644 index 075980bdede..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SensorMeasureSoilTemperatureCommand.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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); - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SettingCommand.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SettingCommand.java deleted file mode 100644 index 5602d67e8e4..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SettingCommand.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * 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; - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SettingCommandWrapper.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SettingCommandWrapper.java deleted file mode 100644 index ac815e1c996..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SettingCommandWrapper.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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; - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/WateringManualOverrideCommand.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/WateringManualOverrideCommand.java deleted file mode 100644 index 94542b7327d..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/WateringManualOverrideCommand.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * 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"); - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/deser/DateDeserializer.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/deser/DateDeserializer.java deleted file mode 100644 index 61cd9db2462..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/deser/DateDeserializer.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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 { - - @Override - public Date deserialize(JsonElement element, Type type, JsonDeserializationContext ctx) throws JsonParseException { - return DateUtils.parseToDate(element.getAsString()); - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/deser/PropertyValueDeserializer.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/deser/PropertyValueDeserializer.java deleted file mode 100644 index 8c61bdcff89..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/deser/PropertyValueDeserializer.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * 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 { - 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()); - } - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/Device.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/Device.java new file mode 100644 index 00000000000..484ba2de026 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/Device.java @@ -0,0 +1,161 @@ +/** + * 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 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); + } +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/LocalService.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/LocalService.java new file mode 100644 index 00000000000..fea82c7ce2b --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/LocalService.java @@ -0,0 +1,23 @@ +/** + * 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; +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/CommonService.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/CommonService.java new file mode 100644 index 00000000000..368d9e9c85c --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/CommonService.java @@ -0,0 +1,29 @@ +/** + * 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(); +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SensorMeasureLightCommand.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/CommonServiceDataItem.java similarity index 60% rename from bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SensorMeasureLightCommand.java rename to bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/CommonServiceDataItem.java index 41baf23eb9c..d0b9049ab61 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/SensorMeasureLightCommand.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/CommonServiceDataItem.java @@ -10,18 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.gardena.internal.model.command; +package org.openhab.binding.gardena.internal.model.dto.api; /** - * Sensor command to measure the light. + * Represents a Gardena object that is sent via the Gardena API. * * @author Gerhard Riegler - Initial contribution */ -public class SensorMeasureLightCommand extends Command { - private static final String COMMAND = "measure_light"; - - public SensorMeasureLightCommand() { - super(COMMAND); - } +public class CommonServiceDataItem extends DataItem { } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/WateringCancelOverrideCommand.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/CreateWebSocket.java similarity index 59% rename from bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/WateringCancelOverrideCommand.java rename to bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/CreateWebSocket.java index a41cced2673..0e495e61f6f 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/WateringCancelOverrideCommand.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/CreateWebSocket.java @@ -10,18 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.gardena.internal.model.command; +package org.openhab.binding.gardena.internal.model.dto.api; /** - * Command to cancel the manual watering. + * Represents a Gardena object that is sent via the Gardena API. * * @author Gerhard Riegler - Initial contribution */ -public class WateringCancelOverrideCommand extends Command { - private static final String COMMAND = "cancel_override"; - - public WateringCancelOverrideCommand() { - super(COMMAND); - } +public class CreateWebSocket { + public String locationId; } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/MowerStartResumeScheduleCommand.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/CreateWebSocketDataItem.java similarity index 59% rename from bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/MowerStartResumeScheduleCommand.java rename to bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/CreateWebSocketDataItem.java index adb989eda53..e71b2bd68dc 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/MowerStartResumeScheduleCommand.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/CreateWebSocketDataItem.java @@ -10,18 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.gardena.internal.model.command; +package org.openhab.binding.gardena.internal.model.dto.api; /** - * Command to resume the mower scheduler. + * Represents a Gardena object that is sent via the Gardena API. * * @author Gerhard Riegler - Initial contribution */ -public class MowerStartResumeScheduleCommand extends Command { - private static final String COMMAND = "start_resume_schedule"; - - public MowerStartResumeScheduleCommand() { - super(COMMAND); - } +public class CreateWebSocketDataItem extends DataItem { } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/CreateWebSocketRequest.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/CreateWebSocketRequest.java new file mode 100644 index 00000000000..053c24b4406 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/CreateWebSocketRequest.java @@ -0,0 +1,31 @@ +/** + * 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; + } +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/DataItem.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/DataItem.java new file mode 100644 index 00000000000..ae954a6ed11 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/DataItem.java @@ -0,0 +1,32 @@ +/** + * 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 { + public String id; + public String type; + + public String getDeviceId() { + return StringUtils.substringBeforeLast(id, ":"); + } + + public T attributes; +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/DeviceDataItem.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/DeviceDataItem.java new file mode 100644 index 00000000000..28cd1c85b69 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/DeviceDataItem.java @@ -0,0 +1,22 @@ +/** + * 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 { +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/IntegerValue.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/IntegerValue.java new file mode 100644 index 00000000000..8b834c40796 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/IntegerValue.java @@ -0,0 +1,23 @@ +/** + * 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; +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/NoResult.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/Location.java similarity index 72% rename from bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/NoResult.java rename to bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/Location.java index 25204b06438..482f5e7a9c4 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/NoResult.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/Location.java @@ -10,13 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.gardena.internal.model; +package org.openhab.binding.gardena.internal.model.dto.api; /** - * Empty class that represents a emtpy result from Gardena Smart Home. + * Represents a Gardena object that is sent via the Gardena API. * * @author Gerhard Riegler - Initial contribution */ -public class NoResult { +public class Location { + public String name; } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/LocationDataItem.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/LocationDataItem.java new file mode 100644 index 00000000000..88734effa9a --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/LocationDataItem.java @@ -0,0 +1,22 @@ +/** + * 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 { +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Devices.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/LocationResponse.java similarity index 59% rename from bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Devices.java rename to bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/LocationResponse.java index 332cacc5d57..6618d12f6c0 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Devices.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/LocationResponse.java @@ -10,24 +10,17 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.gardena.internal.model; +package org.openhab.binding.gardena.internal.model.dto.api; -import java.util.ArrayList; import java.util.List; /** - * Represents a List of Gardena devices. + * Represents a Gardena object that is sent via the Gardena API. * * @author Gerhard Riegler - Initial contribution */ -public class Devices { - private List devices = new ArrayList<>(); - - /** - * Returns a list of Gardena devices. - */ - public List getDevices() { - return devices; - } +public class LocationResponse { + public LocationDataItem data; + public List> included; } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/LocationsResponse.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/LocationsResponse.java new file mode 100644 index 00000000000..de75461a647 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/LocationsResponse.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.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 data; +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/exception/GardenaUnauthorizedException.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/MowerService.java similarity index 57% rename from bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/exception/GardenaUnauthorizedException.java rename to bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/MowerService.java index eefa7797f1a..5d27208553e 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/exception/GardenaUnauthorizedException.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/MowerService.java @@ -10,18 +10,17 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.gardena.internal.exception; +package org.openhab.binding.gardena.internal.model.dto.api; /** - * Exception for invalid user and password. + * Represents a Gardena object that is sent via the Gardena API. * * @author Gerhard Riegler - Initial contribution */ -public class GardenaUnauthorizedException extends GardenaException { - private static final long serialVersionUID = 4343137351443555679L; - - public GardenaUnauthorizedException(Throwable ex) { - super(ex); - } +public class MowerService { + public TimestampedStringValue state; + public TimestampedStringValue activity; + public TimestampedStringValue lastErrorCode; + public IntegerValue operatingHours; } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/MowerServiceDataItem.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/MowerServiceDataItem.java new file mode 100644 index 00000000000..7cdc81496db --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/MowerServiceDataItem.java @@ -0,0 +1,22 @@ +/** + * 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 { +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/PostOAuth2Response.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/PostOAuth2Response.java new file mode 100644 index 00000000000..bf5c581f919 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/PostOAuth2Response.java @@ -0,0 +1,71 @@ +/** + * 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()); + } +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/PowerSocketService.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/PowerSocketService.java new file mode 100644 index 00000000000..b43754a5929 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/PowerSocketService.java @@ -0,0 +1,26 @@ +/** + * 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; +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/PowerSocketServiceDataItem.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/PowerSocketServiceDataItem.java new file mode 100644 index 00000000000..de374f96b31 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/PowerSocketServiceDataItem.java @@ -0,0 +1,21 @@ +/** + * 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 { +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/MowerParkUntilFurtherNoticeCommand.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/SensorService.java similarity index 55% rename from bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/MowerParkUntilFurtherNoticeCommand.java rename to bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/SensorService.java index 338e000c0f7..c1e4b511f46 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/MowerParkUntilFurtherNoticeCommand.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/SensorService.java @@ -10,17 +10,16 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.gardena.internal.model.command; +package org.openhab.binding.gardena.internal.model.dto.api; /** - * Command to park a mower until further notice. + * Represents a Gardena object that is sent via the Gardena API. * * @author Gerhard Riegler - Initial contribution */ -public class MowerParkUntilFurtherNoticeCommand extends Command { - private static final String COMMAND = "park_until_further_notice"; - - public MowerParkUntilFurtherNoticeCommand() { - super(COMMAND); - } +public class SensorService { + public TimestampedIntegerValue soilHumidity; + public TimestampedIntegerValue soilTemperature; + public TimestampedIntegerValue ambientTemperature; + public TimestampedIntegerValue lightIntensity; } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/SensorServiceDataItem.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/SensorServiceDataItem.java new file mode 100644 index 00000000000..c11406f5e5f --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/SensorServiceDataItem.java @@ -0,0 +1,21 @@ +/** + * 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 { +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/StringValue.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/StringValue.java new file mode 100644 index 00000000000..00270961bb6 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/StringValue.java @@ -0,0 +1,22 @@ +/** + * 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; +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/TimestampValue.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/TimestampValue.java new file mode 100644 index 00000000000..0a8a57c14b6 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/TimestampValue.java @@ -0,0 +1,21 @@ +/** + * 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 { +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/TimestampedAttribute.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/TimestampedAttribute.java new file mode 100644 index 00000000000..d9441027f56 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/TimestampedAttribute.java @@ -0,0 +1,24 @@ +/** + * 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; +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/TimestampedIntegerValue.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/TimestampedIntegerValue.java new file mode 100644 index 00000000000..26041643422 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/TimestampedIntegerValue.java @@ -0,0 +1,22 @@ +/** + * 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; +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/TimestampedStringValue.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/TimestampedStringValue.java new file mode 100644 index 00000000000..04b8deeb74f --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/TimestampedStringValue.java @@ -0,0 +1,22 @@ +/** + * 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; +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/UserDefinedNameWrapper.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/UserDefinedNameWrapper.java new file mode 100644 index 00000000000..011be0800af --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/UserDefinedNameWrapper.java @@ -0,0 +1,22 @@ +/** + * 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; +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/ValveService.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/ValveService.java new file mode 100644 index 00000000000..b61ce4a5cbe --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/ValveService.java @@ -0,0 +1,26 @@ +/** + * 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; +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/ValveServiceDataItem.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/ValveServiceDataItem.java new file mode 100644 index 00000000000..91f5c1fb5f9 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/ValveServiceDataItem.java @@ -0,0 +1,21 @@ +/** + * 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 { +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/ValveSetService.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/ValveSetService.java new file mode 100644 index 00000000000..165e2b1e91f --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/ValveSetService.java @@ -0,0 +1,23 @@ +/** + * 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; +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/ValveSetServiceDataItem.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/ValveSetServiceDataItem.java new file mode 100644 index 00000000000..88504348936 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/ValveSetServiceDataItem.java @@ -0,0 +1,21 @@ +/** + * 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 { +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/WebSocket.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/WebSocket.java new file mode 100644 index 00000000000..e235014f638 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/WebSocket.java @@ -0,0 +1,23 @@ +/** + * 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; +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/WebSocketCreatedResponse.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/WebSocketCreatedResponse.java new file mode 100644 index 00000000000..e1e2c263012 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/WebSocketCreatedResponse.java @@ -0,0 +1,22 @@ +/** + * 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; +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/WebSocketDataItem.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/WebSocketDataItem.java new file mode 100644 index 00000000000..f15658c531b --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/api/WebSocketDataItem.java @@ -0,0 +1,21 @@ +/** + * 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 { +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/GardenaCommand.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/GardenaCommand.java new file mode 100644 index 00000000000..e611f933cb1 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/GardenaCommand.java @@ -0,0 +1,24 @@ +/** + * 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; +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Locations.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/GardenaCommandAttributes.java similarity index 55% rename from bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Locations.java rename to bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/GardenaCommandAttributes.java index 41c023f8c1b..6b997c175fb 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/Locations.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/GardenaCommandAttributes.java @@ -10,24 +10,19 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.gardena.internal.model; - -import java.util.ArrayList; -import java.util.List; +package org.openhab.binding.gardena.internal.model.dto.command; /** - * Represents a List of Gardena locations. + * Represents a Gardena command object that is sent to the Gardena API. * * @author Gerhard Riegler - Initial contribution */ -public class Locations { +public class GardenaCommandAttributes { + public String command; + public Integer seconds; - private List locations = new ArrayList<>(); - - /** - * Returns a list of Gardena locations. - */ - public List getLocations() { - return locations; + public GardenaCommandAttributes(String command, Integer seconds) { + this.command = command; + this.seconds = seconds; } } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/MowerParkUntilNextTimerCommand.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/GardenaCommandRequest.java similarity index 59% rename from bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/MowerParkUntilNextTimerCommand.java rename to bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/GardenaCommandRequest.java index 03e768f1978..7072bcd45dc 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/command/MowerParkUntilNextTimerCommand.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/GardenaCommandRequest.java @@ -10,18 +10,17 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.gardena.internal.model.command; +package org.openhab.binding.gardena.internal.model.dto.command; /** - * Command to park a mower until next timer. + * Represents a Gardena command object that is sent to the Gardena API. * * @author Gerhard Riegler - Initial contribution */ +public class GardenaCommandRequest { + public GardenaCommand data; -public class MowerParkUntilNextTimerCommand extends Command { - private static final String COMMAND = "park_until_next_timer"; - - public MowerParkUntilNextTimerCommand() { - super(COMMAND); + public GardenaCommandRequest(GardenaCommand gardenaCommand) { + this.data = gardenaCommand; } } diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/MowerCommand.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/MowerCommand.java new file mode 100644 index 00000000000..431edd859e5 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/MowerCommand.java @@ -0,0 +1,35 @@ +/** + * 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); + } +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/PowerSocketCommand.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/PowerSocketCommand.java new file mode 100644 index 00000000000..dbd5daa54eb --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/PowerSocketCommand.java @@ -0,0 +1,36 @@ +/** + * 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); + } +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/ValveCommand.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/ValveCommand.java new file mode 100644 index 00000000000..e978727782c --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/ValveCommand.java @@ -0,0 +1,35 @@ +/** + * 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); + } +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/ValveSetCommand.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/ValveSetCommand.java new file mode 100644 index 00000000000..f7e1461b559 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/dto/command/ValveSetCommand.java @@ -0,0 +1,32 @@ +/** + * 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); + } +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/property/BaseProperty.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/property/BaseProperty.java deleted file mode 100644 index 7f0165e38fb..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/property/BaseProperty.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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(); -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/property/IrrigationControlWateringProperty.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/property/IrrigationControlWateringProperty.java deleted file mode 100644 index ffacb248b21..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/property/IrrigationControlWateringProperty.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * 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; - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/property/PropertyWrapper.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/property/PropertyWrapper.java deleted file mode 100644 index 9e341d142d3..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/property/PropertyWrapper.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * 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; - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/property/StringProperty.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/property/StringProperty.java deleted file mode 100644 index 9f2986566e6..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/model/property/StringProperty.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * 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; - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/util/DateUtils.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/util/DateUtils.java deleted file mode 100644 index 5d99e43ee2a..00000000000 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/util/DateUtils.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * 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; - } -} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/util/PropertyUtils.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/util/PropertyUtils.java new file mode 100644 index 00000000000..4a7ce9247e1 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/util/PropertyUtils.java @@ -0,0 +1,66 @@ +/** + * 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 @Nullable T getPropertyValue(@Nullable Object instance, String propertyPath, Class 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 @Nullable T getPropertyValue(@Nullable Object instance, String[] properties, + Class 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); + } + } +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/util/StringUtils.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/util/StringUtils.java new file mode 100644 index 00000000000..e02635cae15 --- /dev/null +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/util/StringUtils.java @@ -0,0 +1,60 @@ +/** + * 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; + } + } +} diff --git a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/util/UidUtils.java b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/util/UidUtils.java index e4af4b2725c..3c917047f24 100644 --- a/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/util/UidUtils.java +++ b/bundles/org.openhab.binding.gardena/src/main/java/org/openhab/binding/gardena/internal/util/UidUtils.java @@ -17,8 +17,9 @@ import static org.openhab.binding.gardena.internal.GardenaBindingConstants.BINDI 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; @@ -29,14 +30,15 @@ import org.openhab.core.thing.ThingUID; * * @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); } /** @@ -49,7 +51,7 @@ public class UidUtils { if (deviceId == null) { deviceId = thing.getUID().getId(); } - if (deviceId.equals(device.getId())) { + if (deviceId.equals(device.id)) { thingUIDs.add(thing.getUID()); } } diff --git a/bundles/org.openhab.binding.gardena/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.gardena/src/main/resources/OH-INF/binding/binding.xml index 13929457dbe..482b94f82d0 100644 --- a/bundles/org.openhab.binding.gardena/src/main/resources/OH-INF/binding/binding.xml +++ b/bundles/org.openhab.binding.gardena/src/main/resources/OH-INF/binding/binding.xml @@ -3,6 +3,6 @@ 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"> - Gardena Smart Home Binding - This is the binding for Gardena Smart Home. + Gardena Smart System Binding + This is the binding for Gardena smart system. diff --git a/bundles/org.openhab.binding.gardena/src/main/resources/OH-INF/i18n/gardena_de.properties b/bundles/org.openhab.binding.gardena/src/main/resources/OH-INF/i18n/gardena_de.properties index 6317a39cbbe..4f4abe3b185 100644 --- a/bundles/org.openhab.binding.gardena/src/main/resources/OH-INF/i18n/gardena_de.properties +++ b/bundles/org.openhab.binding.gardena/src/main/resources/OH-INF/i18n/gardena_de.properties @@ -1,462 +1,287 @@ # 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.power.description=Ein Gardena Smart Power Zwischenstecker -thing-type.gardena.ic24.label=Gardena Smart Bewässerungssteuerung -thing-type.gardena.ic24.description=Eine Gardena Smart Bewässerungssteuerung +thing-type.gardena.irrigation_control.label=Gardena Smart Bewässerungssteuerung +thing-type.gardena.irrigation_control.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.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.valveSetProperties.label=Ventilsatz Eigenschaften +channel-group-type.gardena.valveSetProperties.description=Eigenschaften eines Ventilsatzes -channel-group-type.gardena.rechargeableBattery.label=Wiederaufladbare Batterie Info -channel-group-type.gardena.rechargeableBattery.description=Informationen über die wiederaufladbare Batterie +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.radio.label=Funkverbindung -channel-group-type.gardena.radio.description=Informationen über die Funkverbindung +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.mower.label=Mähroboter -channel-group-type.gardena.mower.description=Informationen über den Mähroboter +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.internalTemperature.label=Interne Temperatur -channel-group-type.gardena.internalTemperature.description=Die interne Temperatur des Mähroboters +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.battery.label=Batterieinfo -channel-group-type.gardena.battery.description=Informationen über die Batterie +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.outlet.label=Wasserauslass -channel-group-type.gardena.outlet.description=Informationen über den Wasserauslass +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.ambientTemperature.label=Umgebungstemperatur -channel-group-type.gardena.ambientTemperature.description=Informationen über die Umgebungstemperatur +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.soilTemperature.label=Bodentemperatur -channel-group-type.gardena.soilTemperature.description=Die Temperatur des Bodens +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.humidity.label=Feuchtigkeit -channel-group-type.gardena.humidity.description=Die Feuchtigkeit +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.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.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.gardenaCommand.label=Befehl +channel-type.gardena.gardenaCommand.description=Ein Befehl für ein Gerät -channel-type.gardena.parkUntilFurtherNotice.label=Parken und pausieren -channel-type.gardena.parkUntilFurtherNotice.description=Parken und alle Zeitpläne pausieren +channel-type.gardena.powerCommandDuration.label=Einschaltdauer +channel-type.gardena.powerCommandDuration.description=Die Einschaltdauer in Minuten -channel-type.gardena.parkUntilNextTimer.label=Parken -channel-type.gardena.parkUntilNextTimer.description=Bis zum nächsten regulären Zeitplan parken +channel-type.gardena.valveCommandDuration.label=Einschaltdauer +channel-type.gardena.valveCommandDuration.description=Die Einschaltdauer in Minuten -channel-type.gardena.startResumeSchedule.label=Zeitpläne fortsetzen -channel-type.gardena.startResumeSchedule.description=Alle Zeitpläne werden fortgesetze +channel-type.gardena.mowerCommandDuration.label=Einschaltdauer +channel-type.gardena.mowerCommandDuration.description=Die Einschaltdauer in Minuten -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.state.label=Status +channel-type.gardena.state.description=Der Gerätestatus -channel-type.gardena.lastTimeOnline.label=Zuletzt online -channel-type.gardena.lastTimeOnline.description=Die Zeit wann das Gerät zuletzt online war +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.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.lastErrorCode.label=Letzte Fehlermeldung +channel-type.gardena.lastErrorCode.description=Die letzte Fehlermeldung -channel-type.gardena.charging.label=Batterie wird geladen -channel-type.gardena.charging.description=Batterie wird geladen +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.radioQuality.label=Verbindungsqualität -channel-type.gardena.radioQuality.description=Die Qualität der Funk Verbindung +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.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.name.label=Name +channel-type.gardena.name.description=Der Name des Gerätes -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.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.manualOperation.label=Manuelle Steuerung -channel-type.gardena.manualOperation.description=Der Mäher wird manuell gesteuert +channel-type.gardena.duration.label=Dauer +channel-type.gardena.duration.description=Die Dauer in Minuten -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.soilHumidity.label=Bodenfeuchtigkeit +channel-type.gardena.soilHumidity.description=Die Bodenfeuchtigkeit in Prozent -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.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.temperature.description=Die Temperatur in Grad Celsius -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.operatingHours.label=Betriebsstunden +channel-type.gardena.operatingHours.description=Die Betriebsstunden -channel-type.gardena.valveOpen.label=Ventil geöffnet -channel-type.gardena.valveOpen.description=Ventil geöffnet +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.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.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 diff --git a/bundles/org.openhab.binding.gardena/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.gardena/src/main/resources/OH-INF/thing/bridge.xml index b8a91ead4ca..6711fc9e934 100644 --- a/bundles/org.openhab.binding.gardena/src/main/resources/OH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.gardena/src/main/resources/OH-INF/thing/bridge.xml @@ -4,39 +4,31 @@ 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"> - + - - The Gardena Smart Home Account + + The Gardena smart system account - Email address for logging in to Gardena Smart Home + Email address for logging in to Gardena smart system password - Password for logging in to Gardena Smart Home + Password for logging in to Gardena smart system - - - The timeout in minutes for a session to Gardena Smart Home - true - 30 + + + The Gardena smart system integration API key - + - The timeout in seconds for connections to Gardena Smart Home - true + The timeout in seconds for connections to Gardena smart system integration API + seconds 10 - - - The interval in seconds for refreshing the data from Gardena Smart Home - true - 60 - diff --git a/bundles/org.openhab.binding.gardena/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.gardena/src/main/resources/OH-INF/thing/thing-types.xml index bd99aa0f2ae..7584f983f33 100644 --- a/bundles/org.openhab.binding.gardena/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.gardena/src/main/resources/OH-INF/thing/thing-types.xml @@ -10,1162 +10,648 @@ - - A Gardena Smart Sileno mower + + Represents any Gardena smart SILENO mower model - - - - - - + + + - - - - - - - + + + - - - Information about the device - - - - - - - - Information about the rechargeable battery - - - - - - - - - Information about the radio link - - - - - - - - - Information about the robotic mower - - - - - - - - - - - - - - - - - Number - - Mowing time in minutes - - - - - Switch - - Park and pause all timers - - - - Switch - - Park until next timer - - - - Switch - - Start resume schedule - - - - Switch - - Starts the mower for the specified duration - - - - DateTime - - Last time the device was online - - - - - Switch - - Battery Charging - - - - - Number - - Radio Link Quality - - - - - String - - The mower is controlled manually - - - - - String - - The mower status - - - - - - - - - - - - - - - - - - - - - - - - - - - - String - - Mower Error - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - String - - The source for the next start - - - - - - - - - - - - - - - DateTime - - Timestamp of the next start - - - - - DateTime - - Override End Time - - - - - Number - - Temperature - - - - - - Information about the firmware - - - - - - - - - String - - The firmware status - - - - - Number - - Firmware upload in progress - - - - - String - - Available firmware version - - - - - - Statistics about the robotic mower - - - - - - - - - - Number - - The cutting time - - - - - Number - - The charging cycles - - - - - Number - - The number of collisions - - - - - Number - - Running time - - - - + + - - Represents a Gardena Smart Watering Computer + + Represents a Gardena smart Water Control - - - - - + + + + + - - - - - - - + + + - - - Information about the battery - - - - - - - - - Information about the watering outlet - - - - - - - - - - Ambient Temperature - - - - - - - - - - String - - The status of the disposable battery - - - - - - - - - - - - - Switch - - Valve Open - - - - String - - Manual Override - - - - - - - - - - - Number - - The time the valve is open when you press the button on the device - - - - - String - - Frost Warning - - - - - - - - - + - Represents a Gardena Smart Sensor + Represents a Gardena smart Sensor - - - - - - - + + - - - - - - - + + + - - - The soil temperature - - - - - - - - - - The humidity - - - - - - - - The brightness - - - - - - - Number - - The humidity - - - - - Number - - The brightness - - - - + - Represents a Gardena Smart Pressure Pump + Represents a Gardena smart Pressure Pump - - - - - - - - - + + + - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The outlet temperature - - - - - - - - - - Number - - The minimum outlet temperature - - - - - Number - - The maximum outlet temperature - - - - - - The pump - - - - - - - - - - - - - - - - Number - - The error counter - - - - - Number - - The error counter one - - - - - Number - - The error counter two - - - - - Number - - The error counter three - - - - - Number - - The error counter Four - - - - - Number - - The operating days - - - - - String - - The mode - - - - - - - - - - Switch - - Pump - - - - - Number - - Turn on pressure - - - - - - - - - - - - - String - - The operating mode - - - - - - - - - - - The outlet pressure - - - - - - - - Number - - The outlet pressure - - - - - Number - - The maximum outlet pressure - - - - - - The flow - - - - - - - - - - Number - - The flow rate - - - - - Number - - The flow since last reset - - - - - Number - - The total flow - - - - - String - - The dripping alert - - - - - - - - - - - - Manual Watering - - - - - - - - Number - - The manual watering timer - - - - - - - - - - - - - - - - - - - - - - - Number - - The last manual override timer - - - - - - Manual Watering - - - - - - - - - DateTime - - The scheduled watering end - - - - - DateTime - - The scheduled watering next start - - - - - String - - The adaptive scheduling last decision - - - - - - - - - - + - - A Gardena Smart Power Plug + + Represents a Gardena smart Power Adapter - - - - + + + - - - - - - - + + + - - - Power - - - - - - - - String - - The power timer - - - - - - - - - - - - - - - - - - - - - String - - Power Error - - - - - - - - - - - + - A Gardena Smart Irrigation Control + Represents a Gardena smart Irrigation Control - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + - - - The watering + + + + + Properties of a valve set - - - The watering timer for valve 1 + + + - - - The watering timer for valve 2 - - - - The watering timer for valve 3 - - - - The watering timer for valve 4 - - - - The watering timer for valve 5 - - - - The watering timer for valve 6 + + + - - Number - - - - - - - - - - - - - - - - - - - - - - - - - The irrigation control + + + Properties of a valve - - - - - - The error for valve 0 + + + + - - - The error for valve 1 + + + - - - The error for valve 2 + + + - - - The error for valve 3 + + + + + + + Properties of a sensor + + + + - - - The error for valve 4 + + - - - The error for valve 5 + + - - - The error for valve 6 + + + + + + + + + - - String - - The connected valves - + + + Properties of a power socket + + + + + + + + + + + + + + + + + + + + + + + Properties of a mower + + + + + + + + + + + + + + + + + + + + Properties that are common across devices + + + + + + + + + + + + + + + + + + + + + + + + + + + Commands to control a mower + + + + + + + Manual operation, use Mowing Duration to define duration + + + + Automatic operation + + + + Cancel the current operation and return to charging station + + + + Cancel the current operation, return to charging station, ignore schedule + + + + + + + Commands to control a valve + + + + + + + Manual operation, use Irrigation Duration to define duration + + + + Cancel the current watering, continue with the schedule + + + + Skip automatic operation until specified time. The currently active operation might or might not be + cancelled (depends on device model) + + + + Restore automatic operation if it was paused + + + + + + + Commands to control a valve set + + + + Immediately close all valves + + + + + + + Commands to control a power socket + + + + + + + Manual operation, use Switch on Duration to define duration + + + + Manual on + + + + Immediately switch off, continue with the schedule + + + + Skip automatic operation until specified time. The currently active operation will NOT be cancelled + + + + Restore automatic operation if it was paused + + + + + + Switch + + A command for a device - - String - - The valves master config - + + Number:Time + + A duration in minutes for a command + - + + Number:Time + + A duration in minutes for a command + + + + + Number:Time + + A duration in minutes for a command + + + + String - - The irrigation control error + + The state of the device - - - - - + + + + - + String - - + + The last error code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - The scheduled watering - - - - The scheduled watering next start for valve 1 - - - - The scheduled watering end for valve 1 - - - - The scheduled watering next start for valve 2 - - - - The scheduled watering end for valve 2 - - - - The scheduled watering next start for valve 3 - - - - The scheduled watering end for valve 3 - - - - The scheduled watering next start for valve 4 - - - - The scheduled watering end for valve 4 - - - - The scheduled watering next start for valve 5 - - - - The scheduled watering end for valve 5 - - - - The scheduled watering next start for valve 6 - - - - The scheduled watering end for valve 6 - - - - - + DateTime - + + Timestamp + + DateTime + + Timestamp + + + + String + + The name of the device + + + + + String + + The activity of the device + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Number:Time + + Duration in minutes + + + + + Number:Dimensionless + + Soil humidity in percent + + + + + Number:Illuminance + + Light intensity in Lux + + + + + Number:Temperature + + The temperature + + + + + Number:Time + + The operating hours + + + + + String + + The state of the battery + + + + + + + + + + + + + + + String + + The state of the RF link + + + + + + + +