diff --git a/bundles/org.openhab.binding.automower/README.md b/bundles/org.openhab.binding.automower/README.md index a9f14b184edeb..0bedccecf7be8 100644 --- a/bundles/org.openhab.binding.automower/README.md +++ b/bundles/org.openhab.binding.automower/README.md @@ -1,141 +1,180 @@ # Automower Binding -This is the binding for [Husqvarna Automower a robotic lawn mowers](https://www.husqvarna.com/uk/products/robotic-lawn-mowers/). -This binding allows you to integrate, view and control Automower lawn mowers in the openHAB environment. +This is the binding for [Husqvarna Automower® robotic lawn mowers](https://www.husqvarna.com/uk/products/robotic-lawn-mowers/). +This binding allows you to integrate, view and control Automower® lawn mowers in the openHAB environment. ## Supported Things -`bridge:` The bridge needs to be configured with credentials and an application key that allows communicating with the Automower Connect API +`bridge:` The bridge needs to be configured with credentials and an application key that allows communicating with the Automower® Connect API -`automower:` A single Husqvarna Automower robot +`automower:` A single Husqvarna Automower® robot -All Husqvarna Automower models with "Automower Connect" should be supported. It was tested only with a Husqvarna Automower 430X and 450X. +All Husqvarna Automower® models with "Automower® Connect" should be supported. It was tested with a Husqvarna Automower® 430X, 450X and 430X NERA. ## Discovery -Once the bridge is created and configured, openHAB will automatically discover all Automowers registered on your account. +Once the bridge is created and configured, openHAB will automatically discover all Automower® registered on your account. ## Thing Configuration `bridge:` -- appKey (mandatory): The Application Key is required to communicate with the Automower Connect API. It can be obtained by registering an Application on [the Husqvarna Website](https://developer.husqvarnagroup.cloud/). This application also needs to be connected to the ["Authentication API" and the "Automower Connect API"](https://developer.husqvarnagroup.cloud/docs/getting-started) -- appSecret (mandatory): The Application Secret is required to communicate with the Automower Connect API. It can be obtained by registering an Application on [the Husqvarna Website](https://developer.husqvarnagroup.cloud/). +- appKey (mandatory): The Application Key is required to communicate with the Automower® Connect API. It can be obtained by registering an Application on [the Husqvarna Website](https://developer.husqvarnagroup.cloud/). This application also needs to be connected to the ["Authentication API" and the "Automower® Connect API"](https://developer.husqvarnagroup.cloud/docs/getting-started) +- appSecret (mandatory): The Application Secret is required to communicate with the Automower® Connect API. It can be obtained by registering an Application on [the Husqvarna Website](https://developer.husqvarnagroup.cloud/). - pollingInterval (optional): How often the bridge state should be queried in seconds. Default is 1h (3600s) Keep in mind that the status of the bridge should not be queried too often. -According to the Husqvarna documentation not more than 10000 requests per month and application key are allowed. +According to the Husqvarna documentation not more than 10000 requests per month / 1 request per second and application key are allowed. With the default value of 1h this would mean ~720 requests per month for the bridge state `automower:` -- mowerId (mandatory): The Id of an automower as used by the Automower Connect Api to identify a mower. This is automatically filled when the thing is discovered -- pollingInterval (optional): How often the current automower state should be polled in seconds. Default is 10min (600s) +- mowerId (mandatory): The Id of an Automower® as used by the Automower® Connect Api to identify a mower. This is automatically filled when the thing is discovered +- pollingInterval (optional): How often the current Automower® state should be polled in seconds. Default is 10min (600s) -Keep in mind that the status of the Automowers should not be queried too often. -According to the Husqvarna documentation, no more than 10000 requests per month and application key are allowed. -With the default value of 10min this would mean ~4300 requests per month per single Automower +Keep in mind that the status of the Automower® should not be queried too often. +According to the Husqvarna documentation not more than 10000 requests per month / 1 request per second and application key are allowed. +With the default value of 10min this would mean ~4300 requests per month per single Automower® ## Channels ### Status Channels -| channel | type | access mode | description | -|-------------------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| mode | String | R | The current mode (MAIN_AREA, SECONDARY_AREA, HOME, DEMO, UNKNOWN) | -| activity | String | R | The current activity (UNKNOWN, NOT_APPLICABLE, MOWING, GOING_HOME, CHARGING, LEAVING, PARKED_IN_CS, STOPPED_IN_GARDEN) | -| state | String | R | The current state (UNKNOWN, NOT_APPLICABLE, PAUSED, IN_OPERATION, WAIT_UPDATING, WAIT_POWER_UP, RESTRICTED_NONE, RESTRICTED_WEEK_SCHEDULE, RESTRICTED_PARK_OVERRIDE, RESTRICTED_SENSOR, RESTRICTED_DAILY_LIMIT, OFF, STOPPED, ERROR, FATAL_ERROR, ERROR_AT_POWER_UP) | -| last-update | DateTime | R | The time when the automower updated its states | -| battery | Number | R | The battery state of charge in percent | -| error-code | Number | R | The current error code | -| error-timestamp | DateTime | R | The timestamp when the current error occurred | -| planner-next-start | DateTime | R | The time for the next auto start. If the mower is charging then the value is the estimated time when it will be leaving the charging station. If the mower is about to start now, the value is NULL. | -| planner-override-action | String | R | The action that overrides current planner operation. | -| calendar-tasks | String | R | The JSON with the information about Automower planner. | - -### Command Channels - -| channel | type | access mode | description | -|-----------------------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| start | Number | W | Starts the automower for a duration | -| resume_schedule | Switch | W | Resumes the Automower schedule | -| pause | Switch | W | Pause the Automower | -| park | Number | W | Park the Automower for a duration | -| park_until_next_schedule | Switch | W | Park the Automower until next schedule | -| park_until_further_notice | Switch | W | Park the Automower until further notice. | +| channel | type | access mode | description | advanced | +|---------------------------------------|----------------------|-----|----------------------------------------------------------------------------------------------------------------|-------| +| status#name | String | R | The name of the Automower® | false | +| status#mode | String | R | The current mode (MAIN_AREA, SECONDARY_AREA, HOME, DEMO, UNKNOWN) | false | +| status#activity | String | R | The current activity (UNKNOWN, NOT_APPLICABLE, MOWING, GOING_HOME, CHARGING, LEAVING, PARKED_IN_CS, STOPPED_IN_GARDEN) | false | +| status#inactive-reason | String | R | The current reason for being inactive (NONE, PLANNING, SEARCHING_FOR_SATELLITES) | false | +| status#state | String | R | The current state (UNKNOWN, NOT_APPLICABLE, PAUSED, IN_OPERATION, WAIT_UPDATING, WAIT_POWER_UP, RESTRICTED_NONE, RESTRICTED_WEEK_SCHEDULE, RESTRICTED_PARK_OVERRIDE, RESTRICTED_SENSOR, RESTRICTED_DAILY_LIMIT, RESTRICTED_FOTA, RESTRICTED_FROST, RESTRICTED_ALL_WORK_AREAS_COMPLETED, RESTRICTED_EXTERNAL, OFF, STOPPED, ERROR, FATAL_ERROR, ERROR_AT_POWER_UP) | false | +| status#work-area-id | Number | R | Id of the active work area | true | +| status#work-area | String | R | Name of the active work area | false | +| status#last-update | DateTime | R | The time when the Automower® updated its states | false | +| status#battery | Number:Dimensionless | R | The battery state of charge in percent | false | +| status#error-code | Number | R/W | The current error code. `sendCommand(0)` to confirm current non fatal error | true | +| status#error-message | String | R | The current error message | false | +| status#error-timestamp | DateTime | R | The timestamp when the current error occurred | false | +| status#error-confirmable | Switch | R | If the mower has an Error Code this attribute states if the error is confirmable | true | +| status#planner-next-start | DateTime | R | The time for the next auto start. If the mower is charging then the value is the estimated time when it will be leaving the charging station. If the mower is about to start now, the value is NULL | false | +| status#planner-override-action | String | R | The action that overrides current planner operation | true | +| status#planner-restricted-reason | String | R | A reason that restrics current planner operation (NONE, WEEK_SCHEDULE, PARK_OVERRIDE, SENSOR, DAILY_LIMIT, FOTA, FROST, ALL_WORK_AREAS_COMPLETED, EXTERNAL) | false | +| status#planner-external-reason | String | R | An external reason set by i.e. IFTTT, Google Assistant or Amazon Alexa that restrics current planner operation | true | + + +### Settings Channels + +| channel | type | access mode | description | advanced | +|------------------------|--------|-------------|-------------------------------------------------------------------------|----------| +| setting#cutting-height | Number | R/W | Prescaled cutting height, Range: 1-9 | false | +| setting#headlight-mode | String | R/W | Headlight Mode (ALWAYS_ON, ALWAYS_OFF, EVENING_ONLY, EVENING_AND_NIGHT) | false | + +### Statistics Channels + +| channel | type | access mode | description | advanced | +|-------------------------------------|----------------------|-------------|---------------------------------------------------------------------------------------------|----------| +| statistic#cutting-blade-usage-time | Number:Time | R/W | The time since the last reset of the cutting blade usage counter. `sendCommand(0)` to reset | false | +| statistic#down-time | Number:Time | R | The time the mower has been disconnected from the cloud | true | +| statistic#number-of-charging-cycles | Number | R | Number of charging cycles | false | +| statistic#number-of-collisions | Number | R | The total number of collisions | false | +| statistic#total-charging-time | Number:Time | R | Total charging time | false | +| statistic#total-cutting-time | Number:Time | R | Total Cutting Time | false | +| statistic#total-cutting-percent | Number:Dimensionless | R | Total cutting time in percent | false | +| statistic#total-drive-distance | Number:Length | R | Total driven distance | false | +| statistic#total-running-time | Number:Time | R | The total running time (the wheel motors have been running) | false | +| statistic#total-searching-time | Number:Length | R | The total searching time | false | +| statistic#total-searching-percent | Number:Dimensionless | R | The total searching time in percent | false | +| statistic#up-time | Number:Time | R | The time the mower has been connected to the cloud | true | + +### Calendar Tasks Channels + +These channels hold the different Calendar Task configurations. Right now a maximum of 10 Calendar Tasks are supported by the binding. + +| channel | type | access mode | description | advanced | +|-------------------------------|-------------|-------------|-----------------------------------------------|----------| +| calendartask#\-start | Number:Time | R/W | Start time relative to midnight | true | +| calendartask#\-duration | Number:Time | R/W | Duration time | true | +| calendartask#\-monday | Switch | R/W | Enabled on Mondays | true | +| calendartask#\-tuesday | Switch | R/W | Enabled on Tuesdays | true | +| calendartask#\-wednesday | Switch | R/W | Enabled on Wednesdays | true | +| calendartask#\-thursday | Switch | R/W | Enabled on Thursdays | true | +| calendartask#\-friday | Switch | R/W | Enabled on Fridays | true | +| calendartask#\-saturday | Switch | R/W | Enabled on Saturdays | true | +| calendartask#\-sunday | Switch | R/W | Enabled on Sundays | true | +| calendartask#\-workAreaId | Number | R | Work Area Id mapped to this calendar | true | +| calendartask#\-workArea | String | R | Name of the Work Area mapped to this calendar | true | + +\ ... 01-10 ### Position Channels -These channels hold the last 50 GPS positions recorded by the Automower, thus describing the path it followed. +These channels hold the last 50 GPS positions recorded by the Automower®, thus describing the path it followed. Position 01 is the latest recorded position, the other positions are pushed back, thus removing the previous position 50 from the list because it is replaced by the previous position 49. Channel `last-position` is always identical with channel `position01` and thus provides more convenient access if only the latest GPS information is required by the user. -| channel | type | access mode | description | -|------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| position01 | Location | R | GPS Position 01 | -| position02 | Location | R | GPS Position 02 | -| position03 | Location | R | GPS Position 03 | -| position04 | Location | R | GPS Position 04 | -| position05 | Location | R | GPS Position 05 | -| position06 | Location | R | GPS Position 06 | -| position07 | Location | R | GPS Position 07 | -| position08 | Location | R | GPS Position 08 | -| position09 | Location | R | GPS Position 09 | -| position10 | Location | R | GPS Position 10 | -| position11 | Location | R | GPS Position 11 | -| position12 | Location | R | GPS Position 12 | -| position13 | Location | R | GPS Position 13 | -| position14 | Location | R | GPS Position 14 | -| position15 | Location | R | GPS Position 15 | -| position16 | Location | R | GPS Position 16 | -| position17 | Location | R | GPS Position 17 | -| position18 | Location | R | GPS Position 18 | -| position19 | Location | R | GPS Position 19 | -| position20 | Location | R | GPS Position 20 | -| position21 | Location | R | GPS Position 21 | -| position22 | Location | R | GPS Position 22 | -| position23 | Location | R | GPS Position 23 | -| position24 | Location | R | GPS Position 24 | -| position25 | Location | R | GPS Position 25 | -| position26 | Location | R | GPS Position 26 | -| position27 | Location | R | GPS Position 27 | -| position28 | Location | R | GPS Position 28 | -| position29 | Location | R | GPS Position 29 | -| position30 | Location | R | GPS Position 30 | -| position31 | Location | R | GPS Position 31 | -| position32 | Location | R | GPS Position 32 | -| position33 | Location | R | GPS Position 33 | -| position34 | Location | R | GPS Position 34 | -| position35 | Location | R | GPS Position 35 | -| position36 | Location | R | GPS Position 36 | -| position37 | Location | R | GPS Position 37 | -| position38 | Location | R | GPS Position 38 | -| position39 | Location | R | GPS Position 39 | -| position40 | Location | R | GPS Position 40 | -| position41 | Location | R | GPS Position 41 | -| position42 | Location | R | GPS Position 42 | -| position43 | Location | R | GPS Position 43 | -| position44 | Location | R | GPS Position 44 | -| position45 | Location | R | GPS Position 45 | -| position46 | Location | R | GPS Position 46 | -| position47 | Location | R | GPS Position 47 | -| position48 | Location | R | GPS Position 48 | -| position49 | Location | R | GPS Position 49 | -| position50 | Location | R | GPS Position 50 | -| last-position | Location | R | Last GPS Position (identical with positions#position01) | +| channel | type | access mode | description | advanced | +|------------------|----------|-------------|---------------------------------------------------------|----------| +| position#last | Location | R | Last GPS Position (identical with positions#position01) | false | +| position#\ | Location | R | GPS Position \ | true | -## Actions +\ ... 01-50 + +### Stayout Zones Channels + +These channels hold the different Stayout Zone configurations. Right now a maximum of 10 Stayout Zones are supported by the binding. + +| channel | type | access mode | description | advanced | +|---------------------------|--------|-------------|------------------------------------------------------------------------------------------------------------------------------------|----------| +| stayoutzone#dirty | Switch | R | If the stay-out zones are synchronized with the Husqvarna cloud. If the map is dirty you can not enable or disable a stay-out zone | true | +| stayoutzone#\-id | String | R | Id of the Stayout zone | true | +| stayoutzone#\-name | String | R | The name of the Stayout zone | true | +| stayoutzone#\-enabled | Switch | R/W | If the Stayout zone is enabled, the Automower® will not access the zone | true | + +\ ... 01-10 -The following actions are available for `automower`things: +### Work Area Channels + +These channels hold the different Work Area configurations. Right now a maximum of 10 Work Areas are supported by the binding. + +| channel | type | access mode | description | advanced | +|------------------------------------|-----------------------|-------------|----------------------------------------------------------------------------------------------------|----------| +| workarea#\-id | Number | R | Id of the Work Area | false | +| workarea#\-name | String | R | Name of the work area | false | +| workarea#\-cutting-height | Number:Dimensionless | R/W | Cutting height in percent. 0-100 | false | +| workarea#\-enabled | Switch | R/W | If the work area is enabled or disabled | false | +| workarea#\-progress | Number | R | The progress on a work area. EPOS mowers and systematic mowing work areas only | true | +| workarea#\-last-time-completed | DateTime | R | Timestamp when the work area was last completed. EPOS mowers and systematic mowing work areas only | true | + +\ ... 01-10 + +### Command Channels + +| channel | type | access mode | description | advanced | +|-----------------------------------|----------|-------------|------------------------------------------|----------| +| command#start | Number | W | Starts the Automower® for a duration | false | +| command#resume_schedule | Switch | W | Resumes the Automower® schedule | false | +| command#pause | Switch | W | Pause the Automower® | false | +| command#park | Number | W | Park the Automower® for a duration | false | +| command#park_until_next_schedule | Switch | W | Park the Automower® until next schedule | false | +| command#park_until_further_notice | Switch | W | Park the Automower® until further notice | false | + +## Actions -| action name | arguments | description | -|------------------------|----------------|------------------------------------------------------------------------------------------------| -| start | duration (int) | Starts the automower for the given duration (minutes), overriding the schedule | -| pause | - | Pauses the automower wherever it is currently located | -| parkUntilNextSchedule | - | Parks the automower, fully charges it and starts afterwards according to the schedule | -| parkUntilFurtherNotice | - | Parks the automower until it is started again by the start action or the schedule gets resumed | -| park | duration (int) | Parks the automower for the given duration (minutes), overriding the schedule | -| resumeSchedule | - | Resumes the schedule for the automower | +The following actions are available for `automower` things: + +| action name | arguments | description | +|----------------------------|------------------|-------------------------------------------------------------------------------------------------| +| start | `duration (int)` | Starts the Automower® for the given duration (minutes), overriding the schedule | +| pause | - | Pauses the Automower® wherever it is currently located | +| parkUntilNextSchedule | - | Parks the Automower®, fully charges it and starts afterwards according to the schedule | +| parkUntilFurtherNotice | - | Parks the Automower® until it is started again by the start action or the schedule gets resumed | +| park | `duration (int)` | Parks the Automower® for the given duration (minutes), overriding the schedule | +| resumeSchedule | - | Resumes the schedule for the Automower® | +| confirmError | - | Confirm current non fatal error | +| resetCuttingBladeUsageTime | - | Reset the cutting blade usage time | +| setSettings | `byte cuttingHeight`
`String headlightMode` | Update Automower® settings | +| setWorkArea | `long workAreaId`
`boolean enable`
`byte cuttingHeight` | Update work area settings | +| setStayOutZone | `String zoneId`
`boolean enable` | Enable or disable stay-out zone | +| setCalendarTask | `Long workAreaId` (optional, set to `null` if the mower doesn't support work areas)
`short[] start`
`short[] duration`
`boolean[] monday`
`boolean[] tuesday`
`boolean[] wednesday`
`boolean[] thursday`
`boolean[] friday`
`boolean[] saturday`
`boolean[] sunday` | Update calendar task settings | ## Full Example @@ -143,7 +182,7 @@ The following actions are available for `automower`things: ```java Bridge automower:bridge:mybridge [ appKey="", userName="", password="" ] { - Thing automower myAutomower [ mowerId="", pollingInterval=3600] { + Thing automower myAutomower [ mowerId="", pollingInterval=3600 ] { } } ``` @@ -151,25 +190,24 @@ Bridge automower:bridge:mybridge [ appKey="", user ### automower.items ```java -String Automower_Mode "Mode [%s]" { channel="automower:automower:mybridge:myAutomower:mode" } -String Automower_Activity "Activity [%s]" { channel="automower:automower:mybridge:myAutomower:activity" } -String Automower_State "State [%s]" { channel="automower:automower:mybridge:myAutomower:state" } -DateTime Automower_Last_Update "Last Update" { channel="automower:automower:mybridge:myAutomower:last-update" } -Number Automower_Battery "Battery [%d %%]" { channel="automower:automower:mybridge:myAutomower:battery" } -Number Automower_Error_Code "Error Code [%d]" { channel="automower:automower:mybridge:myAutomower:error-code" } -DateTime Automower_Error_Time "Error Time" { channel="automower:automower:mybridge:myAutomower:error-timestamp" } -String Automower_Override_Action "Override Action [%s]" { channel="automower:automower:mybridge:myAutomower:planner-override-action" } -DateTime Automower_Next_Start_Time "Next Start Time" { channel="automower:automower:mybridge:myAutomower:planner-next-start" } -String Automower_Calendar_Tasks "Planned Tasks [%s]" { channel="automower:automower:mybridge:myAutomower:calendar-tasks" } - -Number Automower_Command_Start "Start mowing for duration [%d min]" { channel="automower:automower:mybridge:myAutomower:start" } -Switch Automower_Command_Resume "Resume the schedule" { channel="automower:automower:mybridge:myAutomower:resume_schedule" } -Switch Automower_Command_Pause "Pause the automower" { channel="automower:automower:mybridge:myAutomower:pause" } -Number Automower_Command_Park "Park for duration [%d min]" { channel="automower:automower:mybridge:myAutomower:park" } -Switch Automower_Command_Park_Next_Schedule "Park until next schedule" { channel="automower:automower:mybridge:myAutomower:park_until_next_schedule" } -Switch Automower_Command_Park_Notice "Park until further notice" { channel="automower:automower:mybridge:myAutomower:park_until_further_notice" } - -Location Automower_Last_Position "Last Position" { channel="automower:automower:mybridge:myAutomower:last-position" } +String Automower_Mode "Mode [%s]" { channel="automower:automower:mybridge:myAutomower:status#mode" } +String Automower_Activity "Activity [%s]" { channel="automower:automower:mybridge:myAutomower:status#activity" } +String Automower_State "State [%s]" { channel="automower:automower:mybridge:myAutomower:status#state" } +DateTime Automower_Last_Update "Last Update" { channel="automower:automower:mybridge:myAutomower:status#last-update" } +Number Automower_Battery "Battery [%d %%]" { channel="automower:automower:mybridge:myAutomower:status#battery" } +Number Automower_Error_Code "Error Code [%d]" { channel="automower:automower:mybridge:myAutomower:status#error-code" } +DateTime Automower_Error_Time "Error Time" { channel="automower:automower:mybridge:myAutomower:status#error-timestamp" } +String Automower_Override_Action "Override Action [%s]" { channel="automower:automower:mybridge:myAutomower:status#planner-override-action" } +DateTime Automower_Next_Start_Time "Next Start Time" { channel="automower:automower:mybridge:myAutomower:status#planner-next-start" } + +Number Automower_Command_Start "Start mowing for duration [%d min]" { channel="automower:automower:mybridge:myAutomower:command#start" } +Switch Automower_Command_Resume "Resume the schedule" { channel="automower:automower:mybridge:myAutomower:command#resume_schedule" } +Switch Automower_Command_Pause "Pause the automower" { channel="automower:automower:mybridge:myAutomower:command#pause" } +Number Automower_Command_Park "Park for duration [%d min]" { channel="automower:automower:mybridge:myAutomower:command#park" } +Switch Automower_Command_Park_Next_Schedule "Park until next schedule" { channel="automower:automower:mybridge:myAutomower:command#park_until_next_schedule" } +Switch Automower_Command_Park_Notice "Park until further notice" { channel="automower:automower:mybridge:myAutomower:command#park_until_further_notice" } + +Location Automower_Last_Position "Last Position" { channel="automower:automower:mybridge:myAutomower:position#last-position" } ``` ### automower.sitemap @@ -194,13 +232,17 @@ sitemap demo label="Automower" ### automower.rule -Example rule that triggers an automower action +Example rule that triggers an Automower® action ```java rule "AutomowerParkUntilFurtherNotice" when Item Some_Item changed to ON then + // via command item + Automower_Command_Park_Notice.sendCommand(ON) + + // alternative via actions val mowerActions = getActions("automower", "automower:automower:mybridge:myAutomower") mowerActions.parkUntilFurtherNotice() end diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java index aa140b49e4209..f12d253d1c081 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java @@ -13,7 +13,9 @@ package org.openhab.binding.automower.internal; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.thing.ThingTypeUID; @@ -35,57 +37,345 @@ public class AutomowerBindingConstants { public static final ThingTypeUID THING_TYPE_AUTOMOWER = new ThingTypeUID(BINDING_ID, "automower"); // List of all status Channel ids - public static final String GROUP_STATUS = ""; // no channel group in use at the moment, we'll possibly introduce - // this in a future release + public static final String GROUP_STATUS = "status#"; + public static final String CHANNEL_STATUS_NAME = GROUP_STATUS + "name"; public static final String CHANNEL_STATUS_MODE = GROUP_STATUS + "mode"; public static final String CHANNEL_STATUS_ACTIVITY = GROUP_STATUS + "activity"; + public static final String CHANNEL_STATUS_INACTIVE_REASON = GROUP_STATUS + "inactive-reason"; public static final String CHANNEL_STATUS_STATE = GROUP_STATUS + "state"; public static final String CHANNEL_STATUS_LAST_UPDATE = GROUP_STATUS + "last-update"; + public static final String CHANNEL_STATUS_WORK_AREA_ID = GROUP_STATUS + "work-area-id"; + public static final String CHANNEL_STATUS_WORK_AREA = GROUP_STATUS + "work-area"; public static final String CHANNEL_STATUS_BATTERY = GROUP_STATUS + "battery"; public static final String CHANNEL_STATUS_ERROR_CODE = GROUP_STATUS + "error-code"; + public static final String CHANNEL_STATUS_ERROR_MESSAGE = GROUP_STATUS + "error-message"; public static final String CHANNEL_STATUS_ERROR_TIMESTAMP = GROUP_STATUS + "error-timestamp"; - public static final String CHANNEL_PLANNER_NEXT_START = GROUP_STATUS + "planner-next-start"; - public static final String CHANNEL_PLANNER_OVERRIDE_ACTION = GROUP_STATUS + "planner-override-action"; - public static final String CHANNEL_CALENDAR_TASKS = GROUP_STATUS + "calendar-tasks"; + public static final String CHANNEL_STATUS_ERROR_CONFIRMABLE = GROUP_STATUS + "error-confirmable"; + public static final String CHANNEL_STATUS_NEXT_START = GROUP_STATUS + "next-start"; + public static final String CHANNEL_STATUS_OVERRIDE_ACTION = GROUP_STATUS + "override-action"; + public static final String CHANNEL_STATUS_RESTRICTED_REASON = GROUP_STATUS + "restricted-reason"; + public static final String CHANNEL_STATUS_EXTERNAL_REASON = GROUP_STATUS + "external-reason"; + + // List of all setting Channel ids + public static final String GROUP_SETTING = "setting#"; + + public static final String CHANNEL_SETTING_CUTTING_HEIGHT = GROUP_SETTING + "cutting-height"; + public static final String CHANNEL_SETTING_HEADLIGHT_MODE = GROUP_SETTING + "headlight-mode"; + + // List of all setting Channel ids + public static final String GROUP_STATISTIC = "statistic#"; + + public static final String CHANNEL_STATISTIC_CUTTING_BLADE_USAGE_TIME = GROUP_STATISTIC + + "cutting-blade-usage-time"; + public static final String CHANNEL_STATISTIC_DOWN_TIME = GROUP_STATISTIC + "down-time"; + public static final String CHANNEL_STATISTIC_NUMBER_OF_CHARGING_CYCLES = GROUP_STATISTIC + + "number-of-charging-cycles"; + public static final String CHANNEL_STATISTIC_NUMBER_OF_COLLISIONS = GROUP_STATISTIC + "number-of-collisions"; + public static final String CHANNEL_STATISTIC_TOTAL_CHARGING_TIME = GROUP_STATISTIC + "total-charging-time"; + public static final String CHANNEL_STATISTIC_TOTAL_CUTTING_TIME = GROUP_STATISTIC + "total-cutting-time"; + public static final String CHANNEL_STATISTIC_TOTAL_CUTTING_PERCENT = GROUP_STATISTIC + "total-cutting-percent"; + public static final String CHANNEL_STATISTIC_TOTAL_DRIVE_DISTANCE = GROUP_STATISTIC + "total-drive-distance"; + public static final String CHANNEL_STATISTIC_TOTAL_RUNNING_TIME = GROUP_STATISTIC + "total-running-time"; + public static final String CHANNEL_STATISTIC_TOTAL_SEARCHING_TIME = GROUP_STATISTIC + "total-searching-time"; + public static final String CHANNEL_STATISTIC_TOTAL_SEARCHING_PERCENT = GROUP_STATISTIC + "total-searching-percent"; + public static final String CHANNEL_STATISTIC_UP_TIME = GROUP_STATISTIC + "up-time"; + + // Calendar Task Channels ids + public static final String GROUP_CALENDARTASK = "calendartask#"; + + public static final ArrayList CHANNEL_CALENDARTASKS = new ArrayList<>(List.of( + GROUP_CALENDARTASK + "01-start", GROUP_CALENDARTASK + "01-duration", GROUP_CALENDARTASK + "01-monday", + GROUP_CALENDARTASK + "01-tuesday", GROUP_CALENDARTASK + "01-wednesday", GROUP_CALENDARTASK + "01-thursday", + GROUP_CALENDARTASK + "01-friday", GROUP_CALENDARTASK + "01-saturday", GROUP_CALENDARTASK + "01-sunday", + GROUP_CALENDARTASK + "01-workAreaId", GROUP_CALENDARTASK + "01-workArea", GROUP_CALENDARTASK + "02-start", + GROUP_CALENDARTASK + "02-duration", GROUP_CALENDARTASK + "02-monday", GROUP_CALENDARTASK + "02-tuesday", + GROUP_CALENDARTASK + "02-wednesday", GROUP_CALENDARTASK + "02-thursday", GROUP_CALENDARTASK + "02-friday", + GROUP_CALENDARTASK + "02-saturday", GROUP_CALENDARTASK + "02-sunday", GROUP_CALENDARTASK + "02-workAreaId", + GROUP_CALENDARTASK + "02-workArea", GROUP_CALENDARTASK + "03-start", GROUP_CALENDARTASK + "03-duration", + GROUP_CALENDARTASK + "03-monday", GROUP_CALENDARTASK + "03-tuesday", GROUP_CALENDARTASK + "03-wednesday", + GROUP_CALENDARTASK + "03-thursday", GROUP_CALENDARTASK + "03-friday", GROUP_CALENDARTASK + "03-saturday", + GROUP_CALENDARTASK + "03-sunday", GROUP_CALENDARTASK + "03-workAreaId", GROUP_CALENDARTASK + "03-workArea", + GROUP_CALENDARTASK + "04-start", GROUP_CALENDARTASK + "04-duration", GROUP_CALENDARTASK + "04-monday", + GROUP_CALENDARTASK + "04-tuesday", GROUP_CALENDARTASK + "04-wednesday", GROUP_CALENDARTASK + "04-thursday", + GROUP_CALENDARTASK + "04-friday", GROUP_CALENDARTASK + "04-saturday", GROUP_CALENDARTASK + "04-sunday", + GROUP_CALENDARTASK + "04-workAreaId", GROUP_CALENDARTASK + "04-workArea", GROUP_CALENDARTASK + "05-start", + GROUP_CALENDARTASK + "05-duration", GROUP_CALENDARTASK + "05-monday", GROUP_CALENDARTASK + "05-tuesday", + GROUP_CALENDARTASK + "05-wednesday", GROUP_CALENDARTASK + "05-thursday", GROUP_CALENDARTASK + "05-friday", + GROUP_CALENDARTASK + "05-saturday", GROUP_CALENDARTASK + "05-sunday", GROUP_CALENDARTASK + "05-workAreaId", + GROUP_CALENDARTASK + "05-workArea", GROUP_CALENDARTASK + "06-start", GROUP_CALENDARTASK + "06-duration", + GROUP_CALENDARTASK + "06-monday", GROUP_CALENDARTASK + "06-tuesday", GROUP_CALENDARTASK + "06-wednesday", + GROUP_CALENDARTASK + "06-thursday", GROUP_CALENDARTASK + "06-friday", GROUP_CALENDARTASK + "06-saturday", + GROUP_CALENDARTASK + "06-sunday", GROUP_CALENDARTASK + "06-workAreaId", GROUP_CALENDARTASK + "06-workArea", + GROUP_CALENDARTASK + "07-start", GROUP_CALENDARTASK + "07-duration", GROUP_CALENDARTASK + "07-monday", + GROUP_CALENDARTASK + "07-tuesday", GROUP_CALENDARTASK + "07-wednesday", GROUP_CALENDARTASK + "07-thursday", + GROUP_CALENDARTASK + "07-friday", GROUP_CALENDARTASK + "07-saturday", GROUP_CALENDARTASK + "07-sunday", + GROUP_CALENDARTASK + "07-workAreaId", GROUP_CALENDARTASK + "07-workArea", GROUP_CALENDARTASK + "08-start", + GROUP_CALENDARTASK + "08-duration", GROUP_CALENDARTASK + "08-monday", GROUP_CALENDARTASK + "08-tuesday", + GROUP_CALENDARTASK + "08-wednesday", GROUP_CALENDARTASK + "08-thursday", GROUP_CALENDARTASK + "08-friday", + GROUP_CALENDARTASK + "08-saturday", GROUP_CALENDARTASK + "08-sunday", GROUP_CALENDARTASK + "08-workAreaId", + GROUP_CALENDARTASK + "08-workArea", GROUP_CALENDARTASK + "09-start", GROUP_CALENDARTASK + "09-duration", + GROUP_CALENDARTASK + "09-monday", GROUP_CALENDARTASK + "09-tuesday", GROUP_CALENDARTASK + "09-wednesday", + GROUP_CALENDARTASK + "09-thursday", GROUP_CALENDARTASK + "09-friday", GROUP_CALENDARTASK + "09-saturday", + GROUP_CALENDARTASK + "09-sunday", GROUP_CALENDARTASK + "09-workAreaId", GROUP_CALENDARTASK + "09-workArea", + GROUP_CALENDARTASK + "10-start", GROUP_CALENDARTASK + "10-duration", GROUP_CALENDARTASK + "10-monday", + GROUP_CALENDARTASK + "10-tuesday", GROUP_CALENDARTASK + "10-wednesday", GROUP_CALENDARTASK + "10-thursday", + GROUP_CALENDARTASK + "10-friday", GROUP_CALENDARTASK + "10-saturday", GROUP_CALENDARTASK + "10-sunday", + GROUP_CALENDARTASK + "10-workAreaId", GROUP_CALENDARTASK + "10-workArea")); // Position Channels ids - public static final String GROUP_POSITIONS = ""; // no channel group in use at the moment, we'll possibly - // introduce - // this in a future release - public static final String LAST_POSITION = GROUP_POSITIONS + "last-position"; + public static final String GROUP_POSITION = "position#"; + + public static final String LAST_POSITION = GROUP_POSITION + "last"; public static final ArrayList CHANNEL_POSITIONS = new ArrayList<>( - List.of(GROUP_POSITIONS + "position01", GROUP_POSITIONS + "position02", GROUP_POSITIONS + "position03", - GROUP_POSITIONS + "position04", GROUP_POSITIONS + "position05", GROUP_POSITIONS + "position06", - GROUP_POSITIONS + "position07", GROUP_POSITIONS + "position08", GROUP_POSITIONS + "position09", - GROUP_POSITIONS + "position10", GROUP_POSITIONS + "position11", GROUP_POSITIONS + "position12", - GROUP_POSITIONS + "position13", GROUP_POSITIONS + "position14", GROUP_POSITIONS + "position15", - GROUP_POSITIONS + "position16", GROUP_POSITIONS + "position17", GROUP_POSITIONS + "position18", - GROUP_POSITIONS + "position19", GROUP_POSITIONS + "position20", GROUP_POSITIONS + "position21", - GROUP_POSITIONS + "position22", GROUP_POSITIONS + "position23", GROUP_POSITIONS + "position24", - GROUP_POSITIONS + "position25", GROUP_POSITIONS + "position26", GROUP_POSITIONS + "position27", - GROUP_POSITIONS + "position28", GROUP_POSITIONS + "position29", GROUP_POSITIONS + "position30", - GROUP_POSITIONS + "position31", GROUP_POSITIONS + "position32", GROUP_POSITIONS + "position33", - GROUP_POSITIONS + "position34", GROUP_POSITIONS + "position35", GROUP_POSITIONS + "position36", - GROUP_POSITIONS + "position37", GROUP_POSITIONS + "position38", GROUP_POSITIONS + "position39", - GROUP_POSITIONS + "position40", GROUP_POSITIONS + "position41", GROUP_POSITIONS + "position42", - GROUP_POSITIONS + "position43", GROUP_POSITIONS + "position44", GROUP_POSITIONS + "position45", - GROUP_POSITIONS + "position46", GROUP_POSITIONS + "position47", GROUP_POSITIONS + "position48", - GROUP_POSITIONS + "position49", GROUP_POSITIONS + "position50")); + List.of(GROUP_POSITION + "01", GROUP_POSITION + "02", GROUP_POSITION + "03", GROUP_POSITION + "04", + GROUP_POSITION + "05", GROUP_POSITION + "06", GROUP_POSITION + "07", GROUP_POSITION + "08", + GROUP_POSITION + "09", GROUP_POSITION + "10", GROUP_POSITION + "11", GROUP_POSITION + "12", + GROUP_POSITION + "13", GROUP_POSITION + "14", GROUP_POSITION + "15", GROUP_POSITION + "16", + GROUP_POSITION + "17", GROUP_POSITION + "18", GROUP_POSITION + "19", GROUP_POSITION + "20", + GROUP_POSITION + "21", GROUP_POSITION + "22", GROUP_POSITION + "23", GROUP_POSITION + "24", + GROUP_POSITION + "25", GROUP_POSITION + "26", GROUP_POSITION + "27", GROUP_POSITION + "28", + GROUP_POSITION + "29", GROUP_POSITION + "30", GROUP_POSITION + "31", GROUP_POSITION + "32", + GROUP_POSITION + "33", GROUP_POSITION + "34", GROUP_POSITION + "35", GROUP_POSITION + "36", + GROUP_POSITION + "37", GROUP_POSITION + "38", GROUP_POSITION + "39", GROUP_POSITION + "40", + GROUP_POSITION + "41", GROUP_POSITION + "42", GROUP_POSITION + "43", GROUP_POSITION + "44", + GROUP_POSITION + "45", GROUP_POSITION + "46", GROUP_POSITION + "47", GROUP_POSITION + "48", + GROUP_POSITION + "49", GROUP_POSITION + "50")); + + // Stayout Zones Channels ids + public static final String GROUP_STAYOUTZONE = "stayoutzone#"; + + public static final String CHANNEL_STAYOUTZONES_DIRTY = GROUP_STAYOUTZONE + "dirty"; + public static final ArrayList CHANNEL_STAYOUTZONES = new ArrayList<>( + List.of(GROUP_STAYOUTZONE + "01-id", GROUP_STAYOUTZONE + "01-name", GROUP_STAYOUTZONE + "01-enabled", + GROUP_STAYOUTZONE + "02-id", GROUP_STAYOUTZONE + "02-name", GROUP_STAYOUTZONE + "02-enabled", + GROUP_STAYOUTZONE + "03-id", GROUP_STAYOUTZONE + "03-name", GROUP_STAYOUTZONE + "03-enabled", + GROUP_STAYOUTZONE + "04-id", GROUP_STAYOUTZONE + "04-name", GROUP_STAYOUTZONE + "04-enabled", + GROUP_STAYOUTZONE + "05-id", GROUP_STAYOUTZONE + "05-name", GROUP_STAYOUTZONE + "05-enabled", + GROUP_STAYOUTZONE + "06-id", GROUP_STAYOUTZONE + "06-name", GROUP_STAYOUTZONE + "06-enabled", + GROUP_STAYOUTZONE + "07-id", GROUP_STAYOUTZONE + "07-name", GROUP_STAYOUTZONE + "07-enabled", + GROUP_STAYOUTZONE + "08-id", GROUP_STAYOUTZONE + "08-name", GROUP_STAYOUTZONE + "08-enabled", + GROUP_STAYOUTZONE + "09-id", GROUP_STAYOUTZONE + "09-name", GROUP_STAYOUTZONE + "09-enabled", + GROUP_STAYOUTZONE + "10-id", GROUP_STAYOUTZONE + "10-name", GROUP_STAYOUTZONE + "10-enabled")); + + // Work Areas Channels ids + public static final String GROUP_WORKAREA = "workarea#"; + + public static final ArrayList CHANNEL_WORKAREAS = new ArrayList<>(List.of(GROUP_WORKAREA + "01-id", + GROUP_WORKAREA + "01-name", GROUP_WORKAREA + "01-cutting-height", GROUP_WORKAREA + "01-enabled", + GROUP_WORKAREA + "01-progress", GROUP_WORKAREA + "01-last-time-completed", + + GROUP_WORKAREA + "02-id", GROUP_WORKAREA + "02-name", GROUP_WORKAREA + "02-cutting-height", + GROUP_WORKAREA + "02-enabled", GROUP_WORKAREA + "02-progress", GROUP_WORKAREA + "02-last-time-completed", + + GROUP_WORKAREA + "03-id", GROUP_WORKAREA + "03-name", GROUP_WORKAREA + "03-cutting-height", + GROUP_WORKAREA + "03-enabled", GROUP_WORKAREA + "03-progress", GROUP_WORKAREA + "03-last-time-completed", + + GROUP_WORKAREA + "04-id", GROUP_WORKAREA + "04-name", GROUP_WORKAREA + "04-cutting-height", + GROUP_WORKAREA + "04-enabled", GROUP_WORKAREA + "04-progress", GROUP_WORKAREA + "04-last-time-completed", + + GROUP_WORKAREA + "05-id", GROUP_WORKAREA + "05-name", GROUP_WORKAREA + "05-cutting-height", + GROUP_WORKAREA + "05-enabled", GROUP_WORKAREA + "05-progress", GROUP_WORKAREA + "05-last-time-completed", + + GROUP_WORKAREA + "06-id", GROUP_WORKAREA + "06-name", GROUP_WORKAREA + "06-cutting-height", + GROUP_WORKAREA + "06-enabled", GROUP_WORKAREA + "06-progress", GROUP_WORKAREA + "06-last-time-completed", + + GROUP_WORKAREA + "07-id", GROUP_WORKAREA + "07-name", GROUP_WORKAREA + "07-cutting-height", + GROUP_WORKAREA + "07-enabled", GROUP_WORKAREA + "07-progress", GROUP_WORKAREA + "07-last-time-completed", + + GROUP_WORKAREA + "08-id", GROUP_WORKAREA + "08-name", GROUP_WORKAREA + "08-cutting-height", + GROUP_WORKAREA + "08-enabled", GROUP_WORKAREA + "08-progress", GROUP_WORKAREA + "08-last-time-completed", + + GROUP_WORKAREA + "09-id", GROUP_WORKAREA + "09-name", GROUP_WORKAREA + "09-cutting-height", + GROUP_WORKAREA + "09-enabled", GROUP_WORKAREA + "09-progress", GROUP_WORKAREA + "09-last-time-completed", + + GROUP_WORKAREA + "10-id", GROUP_WORKAREA + "10-name", GROUP_WORKAREA + "10-cutting-height", + GROUP_WORKAREA + "10-enabled", GROUP_WORKAREA + "10-progress", GROUP_WORKAREA + "10-last-time-completed")); // Command Channel ids - public static final String GROUP_COMMANDS = ""; // no channel group in use at the moment, we'll possibly introduce - // this in a future release - public static final String CHANNEL_COMMAND_START = GROUP_COMMANDS + "start"; - public static final String CHANNEL_COMMAND_RESUME_SCHEDULE = GROUP_COMMANDS + "resume_schedule"; - public static final String CHANNEL_COMMAND_PAUSE = GROUP_COMMANDS + "pause"; - public static final String CHANNEL_COMMAND_PARK = GROUP_COMMANDS + "park"; - public static final String CHANNEL_COMMAND_PARK_UNTIL_NEXT_SCHEDULE = GROUP_COMMANDS + "park_until_next_schedule"; - public static final String CHANNEL_COMMAND_PARK_UNTIL_NOTICE = GROUP_COMMANDS + "park_until_further_notice"; + public static final String GROUP_COMMAND = "command#"; + + public static final String CHANNEL_COMMAND_START = GROUP_COMMAND + "start"; + public static final String CHANNEL_COMMAND_RESUME_SCHEDULE = GROUP_COMMAND + "resume_schedule"; + public static final String CHANNEL_COMMAND_PAUSE = GROUP_COMMAND + "pause"; + public static final String CHANNEL_COMMAND_PARK = GROUP_COMMAND + "park"; + public static final String CHANNEL_COMMAND_PARK_UNTIL_NEXT_SCHEDULE = GROUP_COMMAND + "park_until_next_schedule"; + public static final String CHANNEL_COMMAND_PARK_UNTIL_NOTICE = GROUP_COMMAND + "park_until_further_notice"; // Automower properties public static final String AUTOMOWER_ID = "mowerId"; public static final String AUTOMOWER_NAME = "mowerName"; public static final String AUTOMOWER_MODEL = "mowerModel"; public static final String AUTOMOWER_SERIAL_NUMBER = "mowerSerialNumber"; + public static final String AUTOMOWER_CAN_CONFIRM_ERROR = "mowerCanConfirmError"; + public static final String AUTOMOWER_HAS_HEADLIGHTS = "mowerHasHeadlights"; + public static final String AUTOMOWER_HAS_POSITION = "mowerHasPosition"; + public static final String AUTOMOWER_HAS_STAY_OUT_ZONES = "mowerHasStayOutZones"; + public static final String AUTOMOWER_HAS_WORK_AREAS = "mowerHasWorkAreas"; + + public static final Map ERROR = new HashMap<>() { + { + put(0, "No message"); + put(1, "Outside working area"); + put(2, "No loop signal"); + put(3, "Wrong loop signal"); + put(4, "Loop sensor problem, front"); + put(5, "Loop sensor problem, rear"); + put(6, "Loop sensor problem, left"); + put(7, "Loop sensor problem, right"); + put(8, "Wrong PIN code"); + put(9, "Trapped"); + put(10, "Upside down"); + put(11, "Low battery"); + put(12, "Empty battery"); + put(13, "No drive"); + put(14, "Mower lifted"); + put(15, "Lifted"); + put(16, "Stuck in charging station"); + put(17, "Charging station blocked"); + put(18, "Collision sensor problem, rear"); + put(19, "Collision sensor problem, front"); + put(20, "Wheel motor blocked, right"); + put(21, "Wheel motor blocked, left"); + put(22, "Wheel drive problem, right"); + put(23, "Wheel drive problem, left"); + put(24, "Cutting system blocked"); + put(25, "Cutting system blocked"); + put(26, "Invalid sub-device combination"); + put(27, "Settings restored"); + put(28, "Memory circuit problem"); + put(29, "Slope too steep"); + put(30, "Charging system problem"); + put(31, "STOP button problem"); + put(32, "Tilt sensor problem"); + put(33, "Mower tilted"); + put(34, "Cutting stopped - slope too steep"); + put(35, "Wheel motor overloaded, right"); + put(36, "Wheel motor overloaded, left"); + put(37, "Charging current too high"); + put(38, "Electronic problem"); + put(39, "Cutting motor problem"); + put(40, "Limited cutting height range"); + put(41, "Unexpected cutting height adj"); + put(42, "Limited cutting height range"); + put(43, "Cutting height problem, drive"); + put(44, "Cutting height problem, curr"); + put(45, "Cutting height problem, dir"); + put(46, "Cutting height blocked"); + put(47, "Cutting height problem"); + put(48, "No response from charger"); + put(49, "Ultrasonic problem"); + put(50, "Guide 1 not found"); + put(51, "Guide 2 not found"); + put(52, "Guide 3 not found"); + put(53, "GPS navigation problem"); + put(54, "Weak GPS signal"); + put(55, "Difficult finding home"); + put(56, "Guide calibration accomplished"); + put(57, "Guide calibration failed"); + put(58, "Temporary battery problem"); + put(59, "Temporary battery problem"); + put(60, "Temporary battery problem"); + put(61, "Temporary battery problem"); + put(62, "Temporary battery problem"); + put(63, "Temporary battery problem"); + put(64, "Temporary battery problem"); + put(65, "Temporary battery problem"); + put(66, "Battery problem"); + put(67, "Battery problem"); + put(68, "Temporary battery problem"); + put(69, "Alarm! Mower switched off"); + put(70, "Alarm! Mower stopped"); + put(71, "Alarm! Mower lifted"); + put(72, "Alarm! Mower tilted"); + put(73, "Alarm! Mower in motion"); + put(74, "Alarm! Outside geofence"); + put(75, "Connection changed"); + put(76, "Connection NOT changed"); + put(77, "Com board not available"); + put(78, "Slipped - Mower has Slipped. Situation not solved with moving pattern"); + put(79, "Invalid battery combination - Invalid combination of different battery types."); + put(80, "Cutting system imbalance Warning"); + put(81, "Safety function faulty"); + put(82, "Wheel motor blocked, rear right"); + put(83, "Wheel motor blocked, rear left"); + put(84, "Wheel drive problem, rear right"); + put(85, "Wheel drive problem, rear left"); + put(86, "Wheel motor overloaded, rear right"); + put(87, "Wheel motor overloaded, rear left"); + put(88, "Angular sensor problem"); + put(89, "Invalid system configuration"); + put(90, "No power in charging station"); + put(91, "Switch cord problem"); + put(92, "Work area not valid"); + put(93, "No accurate position from satellites"); + put(94, "Reference station communication problem"); + put(95, "Folding sensor activated"); + put(96, "Right brush motor overloaded"); + put(97, "Left brush motor overloaded"); + put(98, "Ultrasonic Sensor 1 defect"); + put(99, "Ultrasonic Sensor 2 defect"); + put(100, "Ultrasonic Sensor 3 defect"); + put(101, "Ultrasonic Sensor 4 defect"); + put(102, "Cutting drive motor 1 defect"); + put(103, "Cutting drive motor 2 defect"); + put(104, "Cutting drive motor 3 defect"); + put(105, "Lift Sensor defect"); + put(106, "Collision sensor defect"); + put(107, "Docking sensor defect"); + put(108, "Folding cutting deck sensor defect"); + put(109, "Loop sensor defect"); + put(110, "Collision sensor error"); + put(111, "No confirmed position"); + put(112, "Cutting system major imbalance"); + put(113, "Complex working area"); + put(114, "Too high discharge current"); + put(115, "Too high internal current"); + put(116, "High charging power loss"); + put(117, "High internal power loss"); + put(118, "Charging system problem"); + put(119, "Zone generator problem"); + put(120, "Internal voltage error"); + put(121, "High internal temerature"); + put(122, "CAN error"); + put(123, "Destination not reachable"); + put(124, "Destination blocked"); + put(125, "Battery needs replacement"); + put(126, "Battery near end of life"); + put(127, "Battery problem"); + put(128, "Multiple reference stations detected"); + put(129, "Auxiliary cutting means blocked"); + put(130, "Imbalanced auxiliary cutting disc detected"); + put(131, "Lifted in link arm"); + put(132, "EPOS accessory missing"); + put(133, "Bluetooth com with CS failed"); + put(134, "Invalid SW configuration"); + put(135, "Radar problem"); + put(136, "Work area tampered"); + put(137, "High temperature in cutting motor, right"); + put(138, "High temperature in cutting motor, center"); + put(139, "High temperature in cutting motor, left"); + put(141, "Wheel brush motor problem"); + put(143, "Accessory power problem"); + put(144, "Boundary wire problem"); + put(701, "Connectivity problem"); + put(702, "Connectivity settings restored"); + put(703, "Connectivity problem"); + put(704, "Connectivity problem"); + put(705, "Connectivity problem"); + put(706, "Poor signal quality"); + put(707, "SIM card requires PIN"); + put(708, "SIM card locked"); + put(709, "SIM card not found"); + put(710, "SIM card locked"); + put(711, "SIM card locked"); + put(712, "SIM card locked"); + put(713, "Geofence problem"); + put(714, "Geofence problem"); + put(715, "Connectivity problem"); + put(716, "Connectivity problem"); + put(717, "SMS could not be sent"); + put(724, "Communication circuit board SW must be updated"); + } + }; } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java index b8626e878131c..116bc2a111cee 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java @@ -14,6 +14,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.HeadlightMode; import org.openhab.binding.automower.internal.things.AutomowerCommand; import org.openhab.binding.automower.internal.things.AutomowerHandler; import org.openhab.core.automation.annotation.ActionInput; @@ -131,4 +132,113 @@ public void resumeSchedule() { public static void resumeSchedule(ThingActions actions) { ((AutomowerActions) actions).resumeSchedule(); } + + @RuleAction(label = "@text/action-confirm-error-label", description = "@text/action-confirm-error-desc") + public void confirmError() { + AutomowerHandler automowerHandler = handler; + if (automowerHandler == null) { + logger.warn("Automower Action service ThingHandler is null!"); + } else { + automowerHandler.sendAutomowerConfirmError(); + } + } + + public static void confirmError(ThingActions actions) { + ((AutomowerActions) actions).confirmError(); + } + + @RuleAction(label = "@text/action-reset-cutting-blade-usage-time-label", description = "@text/action-reset-cutting-blade-usage-time-desc") + public void resetCuttingBladeUsageTime() { + AutomowerHandler automowerHandler = handler; + if (automowerHandler == null) { + logger.warn("Automower Action service ThingHandler is null!"); + } else { + automowerHandler.sendAutomowerResetCuttingBladeUsageTime(); + } + } + + public static void resetCuttingBladeUsageTime(ThingActions actions) { + ((AutomowerActions) actions).resetCuttingBladeUsageTime(); + } + + @RuleAction(label = "@text/action-set-settings-label", description = "@text/action-set-settings-desc") + public void setSettings( + @ActionInput(name = "cutting-height", label = "@text/action-input-cutting-height-label", description = "@text/action-input-cutting-height-desc") byte cuttingHeight, + @ActionInput(name = "headlight-mode", label = "@text/action-input-headlight-mode-label", description = "@text/action-input-headlight-mode-desc") String headlightMode) { + AutomowerHandler automowerHandler = handler; + if (automowerHandler == null) { + logger.warn("Automower Action service ThingHandler is null!"); + } else { + try { + automowerHandler.sendAutomowerSettings(cuttingHeight, HeadlightMode.valueOf(headlightMode)); + } catch (IllegalArgumentException e) { + logger.warn("Invalid HeadlightMode: {}, Error: {}", headlightMode, e.getMessage()); + } + } + } + + public static void setSettings(ThingActions actions, byte cuttingHeight, String headlightMode) { + ((AutomowerActions) actions).setSettings(cuttingHeight, headlightMode); + } + + @RuleAction(label = "@text/action-set-work-area-label", description = "@text/action-set-work-area-desc") + public void setWorkArea( + @ActionInput(name = "workarea-id", label = "@text/action-input-workarea-id-label", description = "@text/action-input-workarea-id-desc") long workAreaId, + @ActionInput(name = "enable", label = "@text/action-input-enable-label", description = "@text/action-input-enable-desc") boolean enable, + @ActionInput(name = "cutting-height", label = "@text/action-input-cutting-height-label", description = "@text/action-input-cutting-height-desc") byte cuttingHeight) { + AutomowerHandler automowerHandler = handler; + if (automowerHandler == null) { + logger.warn("Automower Action service ThingHandler is null!"); + } else { + automowerHandler.sendAutomowerWorkArea(workAreaId, enable, cuttingHeight); + } + } + + public static void setWorkArea(ThingActions actions, long workAreaId, boolean enable, byte cuttingHeight) { + ((AutomowerActions) actions).setWorkArea(workAreaId, enable, cuttingHeight); + } + + @RuleAction(label = "@text/action-set-stayoutzone-label", description = "@text/action-set-stayoutzone-desc") + public void setStayOutZone( + @ActionInput(name = "zone-id", label = "@text/action-input-zone-id-label", description = "@text/action-input-zone-id-desc") String zoneId, + @ActionInput(name = "enable", label = "@text/action-input-enable-label", description = "@text/action-input-enable-desc") boolean enable) { + AutomowerHandler automowerHandler = handler; + if (automowerHandler == null) { + logger.warn("Automower Action service ThingHandler is null!"); + } else { + automowerHandler.sendAutomowerStayOutZone(zoneId, enable); + } + } + + public static void setStayOutZone(ThingActions actions, String zoneId, boolean enable) { + ((AutomowerActions) actions).setStayOutZone(zoneId, enable); + } + + @RuleAction(label = "@text/action-set-calendartask-label", description = "@text/action-set-calendartask-desc") + public void setCalendarTask( + @ActionInput(name = "workarea-id", label = "@text/action-input-workarea-id-label", description = "@text/action-input-workarea-id-desc") Long workAreaId, + @ActionInput(name = "start", label = "@text/action-input-start-label", description = "@text/action-input-start-desc") short[] start, + @ActionInput(name = "duration", label = "@text/action-input-duration-label", description = "@text/action-input-duration-desc") short[] duration, + @ActionInput(name = "monday", label = "@text/action-input-monday-label", description = "@text/action-input-monday-desc") boolean[] monday, + @ActionInput(name = "tuesday", label = "@text/action-input-tuesday-label", description = "@text/action-input-tuesday-desc") boolean[] tuesday, + @ActionInput(name = "wednesday", label = "@text/action-input-wednesday-label", description = "@text/action-input-wednesday-desc") boolean[] wednesday, + @ActionInput(name = "thursday", label = "@text/action-input-thursday-label", description = "@text/action-input-thursday-desc") boolean[] thursday, + @ActionInput(name = "friday", label = "@text/action-input-friday-label", description = "@text/action-input-friday-desc") boolean[] friday, + @ActionInput(name = "saturday", label = "@text/action-input-saturday-label", description = "@text/action-input-saturday-desc") boolean[] saturday, + @ActionInput(name = "sunday", label = "@text/action-input-sunday-label", description = "@text/action-input-sunday-desc") boolean[] sunday) { + AutomowerHandler automowerHandler = handler; + if (automowerHandler == null) { + logger.warn("Automower Action service ThingHandler is null!"); + } else { + automowerHandler.sendAutomowerCalendarTask(workAreaId, start, duration, monday, tuesday, wednesday, + thursday, friday, saturday, sunday); + } + } + + public static void setCalendarTask(ThingActions actions, Long workAreaId, short[] start, short[] duration, + boolean[] monday, boolean[] tuesday, boolean[] wednesday, boolean[] thursday, boolean[] friday, + boolean[] saturday, boolean[] sunday) { + ((AutomowerActions) actions).setCalendarTask(workAreaId, start, duration, monday, tuesday, wednesday, thursday, + friday, saturday, sunday); + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java index db261562f8726..bce4c21b81cda 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java @@ -14,16 +14,30 @@ import java.io.IOException; import java.time.Instant; +import java.util.List; import java.util.concurrent.ScheduledExecutorService; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.automower.internal.rest.api.automowerconnect.AutomowerConnectApi; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Calendar; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.CalendarTask; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Mower; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerCalendar; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerCalendardRequest; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerCommand; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerCommandAttributes; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerCommandRequest; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerListResult; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerSettings; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerSettingsRequest; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZone; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZoneAttributes; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZoneRequest; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerWorkArea; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerWorkAreaAttributes; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerWorkAreaRequest; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Settings; import org.openhab.binding.automower.internal.rest.exceptions.AutomowerCommunicationException; import org.openhab.binding.automower.internal.things.AutomowerCommand; import org.openhab.core.auth.client.oauth2.AccessTokenResponse; @@ -103,4 +117,109 @@ public void sendAutomowerCommand(String id, AutomowerCommand command, long comma request.setData(mowerCommand); automowerApi.sendCommand(appKey, authenticate().getAccessToken(), id, request); } + + /** + * Sends a calendarTask to the automower + * + * @param id The id of the mower + * @param hasWorkAreas Work area capability of the mower + * @param workAreaId The Id of the work area this calendar belongs to (or null, if there is no work area support) + * @param calendarTasks The calendar that should be sent. It is using the same json structure (start, duration, ...) + * as provided when reading the channel + * @throws AutomowerCommunicationException In case the query cannot be executed successfully + */ + public void sendAutomowerCalendarTask(String id, boolean hasWorkAreas, Long workAreaId, + List calendarTasks) throws AutomowerCommunicationException { + Calendar calendar = new Calendar(); + calendar.setTasks(calendarTasks); + + MowerCalendar mowerCalendar = new MowerCalendar(); + mowerCalendar.setType("calendar"); + mowerCalendar.setAttributes(calendar); + + MowerCalendardRequest calendarRequest = new MowerCalendardRequest(); + calendarRequest.setData(mowerCalendar); + + automowerApi.sendCalendar(appKey, authenticate().getAccessToken(), id, hasWorkAreas, workAreaId, + calendarRequest); + } + + /** + * Sends Settings to the automower + * + * @param id The id of the mower + * @param settings The Settings that should be sent. It is using the same json structure + * as provided when reading the channel + * @throws AutomowerCommunicationException In case the query cannot be executed successfully + */ + public void sendAutomowerSettings(String id, Settings settings) throws AutomowerCommunicationException { + MowerSettings mowerSettings = new MowerSettings(); + mowerSettings.setType("settings"); + mowerSettings.setAttributes(settings); + + MowerSettingsRequest settingsRequest = new MowerSettingsRequest(); + settingsRequest.setData(mowerSettings); + + automowerApi.sendSettings(appKey, authenticate().getAccessToken(), id, settingsRequest); + } + + /** + * Confirm current non fatal error on the mower + * + * @param id The id of the mower + * @throws AutomowerCommunicationException In case the query cannot be executed successfully + */ + public void sendAutomowerConfirmError(String id) throws AutomowerCommunicationException { + automowerApi.sendConfirmError(appKey, authenticate().getAccessToken(), id); + } + + /** + * Reset the cutting blade usage time + * + * @param id The id of the mower + * @throws AutomowerCommunicationException In case the query cannot be executed successfully + */ + public void sendAutomowerResetCuttingBladeUsageTime(String id) throws AutomowerCommunicationException { + automowerApi.sendResetCuttingBladeUsageTime(appKey, authenticate().getAccessToken(), id); + } + + /** + * Enable or disable stay out zone + * + * @param id The id of the mower + * @param zoneId The id of the stay out zone + * @param zoneAttributes The new zone status + * @throws AutomowerCommunicationException In case the query cannot be executed successfully + */ + public void sendAutomowerStayOutZone(String id, String zoneId, MowerStayOutZoneAttributes zoneAttributes) + throws AutomowerCommunicationException { + MowerStayOutZone zoneData = new MowerStayOutZone(); + zoneData.setType("stayOutZone"); + zoneData.setId(zoneId); + zoneData.setAttributes(zoneAttributes); + MowerStayOutZoneRequest zoneRequest = new MowerStayOutZoneRequest(); + zoneRequest.setData(zoneData); + + automowerApi.sendStayOutZone(appKey, authenticate().getAccessToken(), id, zoneId, zoneRequest); + } + + /** + * Update a work area setting + * + * @param id The id of the mower + * @param workAreaId The id of the work area + * @param workAreaAttributes The new work area status + * @throws AutomowerCommunicationException In case the query cannot be executed successfully + */ + public void sendAutomowerWorkArea(String id, long workAreaId, MowerWorkAreaAttributes workAreaAttributes) + throws AutomowerCommunicationException { + MowerWorkArea workAreaData = new MowerWorkArea(); + workAreaData.setType("workArea"); + workAreaData.setId(workAreaId); + workAreaData.setAttributes(workAreaAttributes); + MowerWorkAreaRequest workAreaRequest = new MowerWorkAreaRequest(); + workAreaRequest.setData(workAreaData); + + automowerApi.sendWorkArea(appKey, authenticate().getAccessToken(), id, workAreaId, workAreaRequest); + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/discovery/AutomowerDiscoveryService.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/discovery/AutomowerDiscoveryService.java index abf43c6884b30..eae36ab8519c5 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/discovery/AutomowerDiscoveryService.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/discovery/AutomowerDiscoveryService.java @@ -63,6 +63,17 @@ protected void startScan() { properties.put(AutomowerBindingConstants.AUTOMOWER_MODEL, mower.getAttributes().getSystem().getModel()); properties.put(AutomowerBindingConstants.AUTOMOWER_NAME, mower.getAttributes().getSystem().getName()); + properties.put(AutomowerBindingConstants.AUTOMOWER_CAN_CONFIRM_ERROR, + (mower.getAttributes().getCapabilities().canConfirmError() ? "yes" : "no")); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_HEADLIGHTS, + (mower.getAttributes().getCapabilities().hasHeadlights() ? "yes" : "no")); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_POSITION, + (mower.getAttributes().getCapabilities().hasPosition() ? "yes" : "no")); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_STAY_OUT_ZONES, + (mower.getAttributes().getCapabilities().hasStayOutZones() ? "yes" : "no")); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_WORK_AREAS, + (mower.getAttributes().getCapabilities().hasWorkAreas() ? "yes" : "no")); + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(mowerThingUid) .withThingType(thingTypeUID).withProperties(properties).withBridge(bridgeUID) .withRepresentationProperty(AutomowerBindingConstants.AUTOMOWER_ID) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java index f6695a69439c7..71809f3d3780a 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java @@ -24,11 +24,17 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.openhab.binding.automower.internal.rest.api.HusqvarnaApi; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerCalendardRequest; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerCommandRequest; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerListResult; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerResult; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerSettingsRequest; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZoneRequest; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerWorkAreaRequest; import org.openhab.binding.automower.internal.rest.exceptions.AutomowerCommunicationException; import org.openhab.binding.automower.internal.rest.exceptions.UnauthorizedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.gson.JsonSyntaxException; @@ -39,6 +45,9 @@ */ @NonNullByDefault public class AutomowerConnectApi extends HusqvarnaApi { + + private final Logger logger = LoggerFactory.getLogger(AutomowerConnectApi.class); + public AutomowerConnectApi(HttpClient httpClient) { super(httpClient); } @@ -62,7 +71,6 @@ public MowerResult getMower(String appKey, String token, String mowerId) throws request.method(HttpMethod.GET); ContentResponse response = executeRequest(appKey, token, request); - return parseResponse(response, MowerResult.class); } @@ -71,6 +79,7 @@ public void sendCommand(String appKey, String token, String id, MowerCommandRequ final Request request = getHttpClient().newRequest(getBaseUrl() + "/mowers/" + id + "/actions"); request.method(HttpMethod.POST); + logger.trace("sendCommand: {}", gson.toJson(command)); request.content(new StringContentProvider(gson.toJson(command))); ContentResponse response = executeRequest(appKey, token, request); @@ -78,6 +87,93 @@ public void sendCommand(String appKey, String token, String id, MowerCommandRequ checkForError(response, response.getStatus()); } + public void sendCalendar(String appKey, String token, String id, boolean hasWorkAreas, Long workAreaId, + MowerCalendardRequest calendar) throws AutomowerCommunicationException { + String url; + if (hasWorkAreas && (workAreaId != null)) { + url = getBaseUrl() + "/mowers/" + id + "/workAreas/" + workAreaId + "/calendar"; + } else { + url = getBaseUrl() + "/mowers/" + id + "/calendar"; + } + final Request request = getHttpClient().newRequest(url); + request.method(HttpMethod.POST); + + logger.trace("sendCalendar: {}", gson.toJson(calendar)); + request.content(new StringContentProvider(gson.toJson(calendar))); + + ContentResponse response = executeRequest(appKey, token, request); + + checkForError(response, response.getStatus()); + } + + public void sendSettings(String appKey, String token, String id, MowerSettingsRequest settings) + throws AutomowerCommunicationException { + String url; + url = getBaseUrl() + "/mowers/" + id + "/settings"; + final Request request = getHttpClient().newRequest(url); + request.method(HttpMethod.POST); + + logger.trace("sendSettings: {}", gson.toJson(settings)); + request.content(new StringContentProvider(gson.toJson(settings))); + + ContentResponse response = executeRequest(appKey, token, request); + + checkForError(response, response.getStatus()); + } + + public void sendConfirmError(String appKey, String token, String id) throws AutomowerCommunicationException { + String url; + url = getBaseUrl() + "/mowers/" + id + "/errors/confirm"; + final Request request = getHttpClient().newRequest(url); + request.method(HttpMethod.POST); + + ContentResponse response = executeRequest(appKey, token, request); + + checkForError(response, response.getStatus()); + } + + public void sendResetCuttingBladeUsageTime(String appKey, String token, String id) + throws AutomowerCommunicationException { + String url; + url = getBaseUrl() + "/mowers/" + id + "/statistics/resetCuttingBladeUsageTime"; + final Request request = getHttpClient().newRequest(url); + request.method(HttpMethod.POST); + + ContentResponse response = executeRequest(appKey, token, request); + + checkForError(response, response.getStatus()); + } + + public void sendStayOutZone(String appKey, String token, String id, String zoneId, + MowerStayOutZoneRequest zoneRequest) throws AutomowerCommunicationException { + String url; + url = getBaseUrl() + "/mowers/" + id + "/stayOutZones/" + zoneId; + final Request request = getHttpClient().newRequest(url); + request.method(HttpMethod.PATCH); + + logger.trace("sendStayOutZone: {}", gson.toJson(zoneRequest)); + request.content(new StringContentProvider(gson.toJson(zoneRequest))); + + ContentResponse response = executeRequest(appKey, token, request); + + checkForError(response, response.getStatus()); + } + + public void sendWorkArea(String appKey, String token, String id, long workAreaId, + MowerWorkAreaRequest workAreaRequest) throws AutomowerCommunicationException { + String url; + url = getBaseUrl() + "/mowers/" + id + "/workAreas/" + workAreaId; + final Request request = getHttpClient().newRequest(url); + request.method(HttpMethod.PATCH); + + logger.trace("sendWorkArea: {}", gson.toJson(workAreaRequest)); + request.content(new StringContentProvider(gson.toJson(workAreaRequest))); + + ContentResponse response = executeRequest(appKey, token, request); + + checkForError(response, response.getStatus()); + } + private ContentResponse executeRequest(String appKey, String token, final Request request) throws AutomowerCommunicationException { request.timeout(10, TimeUnit.SECONDS); @@ -103,7 +199,6 @@ private T parseResponse(ContentResponse response, Class type) throws Auto int statusCode = response.getStatus(); checkForError(response, statusCode); - try { return gson.fromJson(response.getContentAsString(), type); } catch (JsonSyntaxException e) { diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Action.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Action.java new file mode 100644 index 0000000000000..9717f3fba7eae --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Action.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public enum Action { + NOT_ACTIVE, + FORCE_PARK, + FORCE_MOW +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Battery.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Battery.java index a21cfa496302e..32dd54e1510af 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Battery.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Battery.java @@ -16,13 +16,13 @@ * @author Markus Pfleger - Initial contribution */ public class Battery { - private int batteryPercent; + private byte batteryPercent; - public int getBatteryPercent() { + public byte getBatteryPercent() { return batteryPercent; } - public void setBatteryPercent(int batteryPercent) { + public void setBatteryPercent(byte batteryPercent) { this.batteryPercent = batteryPercent; } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Calendar.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Calendar.java index ef4c35d670365..5a28b217ab372 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Calendar.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Calendar.java @@ -25,4 +25,8 @@ public class Calendar { public List getTasks() { return tasks; } + + public void setTasks(List tasks) { + this.tasks = tasks; + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java index 87733f507ccb5..ad548a60ca345 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java @@ -19,53 +19,98 @@ public class CalendarTask { /** * Start time expressed in minutes after midnight. */ - private Integer start; + private short start; /** * Duration time expressed in minutes */ - private Integer duration; - private Boolean monday; - private Boolean tuesday; - private Boolean wednesday; - private Boolean thursday; - private Boolean friday; - private Boolean saturday; - private Boolean sunday; - - public Integer getStart() { + private short duration; + private boolean monday; + private boolean tuesday; + private boolean wednesday; + private boolean thursday; + private boolean friday; + private boolean saturday; + private boolean sunday; + private Long workAreaId; + + public short getStart() { return start; } - public Integer getDuration() { + public short getDuration() { return duration; } - public Boolean getMonday() { + public boolean getMonday() { return monday; } - public Boolean getTuesday() { + public boolean getTuesday() { return tuesday; } - public Boolean getWednesday() { + public boolean getWednesday() { return wednesday; } - public Boolean getThursday() { + public boolean getThursday() { return thursday; } - public Boolean getFriday() { + public boolean getFriday() { return friday; } - public Boolean getSaturday() { + public boolean getSaturday() { return saturday; } - public Boolean getSunday() { + public boolean getSunday() { return sunday; } + + public Long getWorkAreaId() { + return workAreaId; + } + + public void setStart(short start) { + this.start = start; + } + + public void setDuration(short duration) { + this.duration = duration; + } + + public void setMonday(boolean monday) { + this.monday = monday; + } + + public void setTuesday(boolean tuesday) { + this.tuesday = tuesday; + } + + public void setWednesday(boolean wednesday) { + this.wednesday = wednesday; + } + + public void setThursday(boolean thursday) { + this.thursday = thursday; + } + + public void setFriday(boolean friday) { + this.friday = friday; + } + + public void setSaturday(boolean saturday) { + this.saturday = saturday; + } + + public void setSunday(boolean sunday) { + this.sunday = sunday; + } + + public void setWorkAreaId(Long workAreaId) { + this.workAreaId = workAreaId; + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Capabilities.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Capabilities.java new file mode 100644 index 0000000000000..f077b1682cde4 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Capabilities.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class Capabilities { + private boolean canConfirmError; + private boolean headlights; + private boolean position; + private boolean stayOutZones; + private boolean workAreas; + + public boolean canConfirmError() { + return canConfirmError; + } + + public boolean hasHeadlights() { + return headlights; + } + + public boolean hasPosition() { + return position; + } + + public boolean hasStayOutZones() { + return stayOutZones; + } + + public boolean hasWorkAreas() { + return workAreas; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Headlight.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Headlight.java new file mode 100644 index 0000000000000..4a9e02a70476c --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Headlight.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class Headlight { + private HeadlightMode mode; + + public HeadlightMode getHeadlightMode() { + return mode; + } + + public void setHeadlightMode(HeadlightMode mode) { + this.mode = mode; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/HeadlightMode.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/HeadlightMode.java new file mode 100644 index 0000000000000..a8d339a603d54 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/HeadlightMode.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public enum HeadlightMode { + ALWAYS_ON, + ALWAYS_OFF, + EVENING_ONLY, + EVENING_AND_NIGHT +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/InactiveReason.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/InactiveReason.java new file mode 100644 index 0000000000000..c70e32e3ccf66 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/InactiveReason.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public enum InactiveReason { + NONE, + PLANNING, + SEARCHING_FOR_SATELLITES +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerApp.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerApp.java index 0ec992d5dc053..19ad4695025b6 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerApp.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerApp.java @@ -18,10 +18,12 @@ public class MowerApp { private Mode mode; private Activity activity; + private InactiveReason inactiveReason; private State state; - + private Long workAreaId; private int errorCode; private long errorCodeTimestamp; + private Boolean isErrorConfirmable; public Mode getMode() { return mode; @@ -39,6 +41,14 @@ public void setActivity(Activity activity) { this.activity = activity; } + public InactiveReason getInactiveReason() { + return inactiveReason; + } + + public void setInactiveReason(InactiveReason inactiveReason) { + this.inactiveReason = inactiveReason; + } + public State getState() { return state; } @@ -47,6 +57,14 @@ public void setState(State state) { this.state = state; } + public Long getWorkAreaId() { + return workAreaId; + } + + public void setWorkAreaId(Long workAreaId) { + this.workAreaId = workAreaId; + } + public int getErrorCode() { return errorCode; } @@ -62,4 +80,12 @@ public long getErrorCodeTimestamp() { public void setErrorCodeTimestamp(long errorCodeTimestamp) { this.errorCodeTimestamp = errorCodeTimestamp; } + + public Boolean getIsErrorConfirmable() { + return isErrorConfirmable; + } + + public void setIsErrorConfirmable(Boolean isErrorConfirmable) { + this.isErrorConfirmable = isErrorConfirmable; + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendar.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendar.java new file mode 100644 index 0000000000000..0dfc738c7a5ad --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendar.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class MowerCalendar { + private String type; + private Calendar attributes; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Calendar getAttributes() { + return attributes; + } + + public void setAttributes(Calendar attributes) { + this.attributes = attributes; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendardRequest.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendardRequest.java new file mode 100644 index 0000000000000..55a7c74db42db --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendardRequest.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class MowerCalendardRequest { + private MowerCalendar data; + + public MowerCalendar getData() { + return data; + } + + public void setData(MowerCalendar data) { + this.data = data; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerData.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerData.java index 3f861c33fee0d..1ed50ce502ce9 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerData.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerData.java @@ -13,6 +13,7 @@ package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; import java.util.ArrayList; +import java.util.List; /** * @author Markus Pfleger - Initial contribution @@ -20,11 +21,16 @@ public class MowerData { private System system; private Battery battery; + private Capabilities capabilities; private MowerApp mower; private Calendar calendar; private Planner planner; private Metadata metadata; - private ArrayList positions = new ArrayList<>(); + private List positions = new ArrayList<>(); + private Settings settings; + private Statistics statistics; + private StayOutZones stayOutZones; + private List workAreas = new ArrayList<>(); public System getSystem() { return system; @@ -42,6 +48,14 @@ public void setBattery(Battery battery) { this.battery = battery; } + public Capabilities getCapabilities() { + return capabilities; + } + + public void setCapabilities(Capabilities capabilities) { + this.capabilities = capabilities; + } + public MowerApp getMower() { return mower; } @@ -78,11 +92,43 @@ public void addPosition(Position position) { this.positions.add(position); } - public ArrayList getPositions() { + public List getPositions() { return this.positions; } public Position getLastPosition() { return !this.positions.isEmpty() ? this.positions.get(0) : null; } + + public Settings getSettings() { + return settings; + } + + public void setSettings(Settings settings) { + this.settings = settings; + } + + public Statistics getStatistics() { + return statistics; + } + + public void setStatistics(Statistics statistics) { + this.statistics = statistics; + } + + public StayOutZones getStayOutZones() { + return stayOutZones; + } + + public void setStayOutZones(StayOutZones stayOutZones) { + this.stayOutZones = stayOutZones; + } + + public List getWorkAreas() { + return workAreas; + } + + public void setWorkAreas(List workAreas) { + this.workAreas = workAreas; + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettings.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettings.java new file mode 100644 index 0000000000000..d4a7d5a7edd3b --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettings.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class MowerSettings { + private String type; + private Settings attributes; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Settings getAttributes() { + return attributes; + } + + public void setAttributes(Settings attributes) { + this.attributes = attributes; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettingsRequest.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettingsRequest.java new file mode 100644 index 0000000000000..39a15f00043d4 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettingsRequest.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class MowerSettingsRequest { + private MowerSettings data; + + public MowerSettings getData() { + return data; + } + + public void setData(MowerSettings data) { + this.data = data; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZone.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZone.java new file mode 100644 index 0000000000000..405be3571f27d --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZone.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class MowerStayOutZone { + private String type; + private String id; + private MowerStayOutZoneAttributes attributes; + + public String getType() { + return type; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setType(String type) { + this.type = type; + } + + public MowerStayOutZoneAttributes getAttributes() { + return attributes; + } + + public void setAttributes(MowerStayOutZoneAttributes attributes) { + this.attributes = attributes; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZoneAttributes.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZoneAttributes.java new file mode 100644 index 0000000000000..712d39e1f363c --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZoneAttributes.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class MowerStayOutZoneAttributes { + private boolean enable; + + public boolean getEnable() { + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZoneRequest.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZoneRequest.java new file mode 100644 index 0000000000000..f9b3eca292281 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZoneRequest.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class MowerStayOutZoneRequest { + private MowerStayOutZone data; + + public MowerStayOutZone getData() { + return data; + } + + public void setData(MowerStayOutZone data) { + this.data = data; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkArea.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkArea.java new file mode 100644 index 0000000000000..9ab8216f84d0e --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkArea.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class MowerWorkArea { + private String type; + private long id; + private MowerWorkAreaAttributes attributes; + + public String getType() { + return type; + } + + public void setId(long id) { + this.id = id; + } + + public long getId() { + return id; + } + + public void setType(String type) { + this.type = type; + } + + public MowerWorkAreaAttributes getAttributes() { + return attributes; + } + + public void setAttributes(MowerWorkAreaAttributes attributes) { + this.attributes = attributes; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkAreaAttributes.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkAreaAttributes.java new file mode 100644 index 0000000000000..7544086f84c0a --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkAreaAttributes.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class MowerWorkAreaAttributes { + private byte cuttingHeight; + private boolean enable; + + public byte getCuttingHeight() { + return cuttingHeight; + } + + public void setCuttingHeight(byte cuttingHeight) { + this.cuttingHeight = cuttingHeight; + } + + public boolean getEnable() { + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkAreaRequest.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkAreaRequest.java new file mode 100644 index 0000000000000..4bbe99ddb494b --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkAreaRequest.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class MowerWorkAreaRequest { + private MowerWorkArea data; + + public MowerWorkArea getData() { + return data; + } + + public void setData(MowerWorkArea data) { + this.data = data; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Planner.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Planner.java index 6c5c6b74975f4..14293270b8e7c 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Planner.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Planner.java @@ -20,31 +20,37 @@ public class Planner { private long nextStartTimestamp; private RestrictedReason restrictedReason; private PlannerOverride override; + private int externalReason; public long getNextStartTimestamp() { return nextStartTimestamp; } - public Planner setNextStartTimestamp(long nextStartTimestamp) { + public void setNextStartTimestamp(long nextStartTimestamp) { this.nextStartTimestamp = nextStartTimestamp; - return this; } public RestrictedReason getRestrictedReason() { return restrictedReason; } - public Planner setRestrictedReason(RestrictedReason restrictedReason) { + public void setRestrictedReason(RestrictedReason restrictedReason) { this.restrictedReason = restrictedReason; - return this; } public PlannerOverride getOverride() { return override; } - public Planner setOverride(PlannerOverride override) { + public void setOverride(PlannerOverride override) { this.override = override; - return this; + } + + public int getExternalReason() { + return externalReason; + } + + public void setExternalReason(int externalReason) { + this.externalReason = externalReason; } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/PlannerOverride.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/PlannerOverride.java index 915837b3e5090..77b6269258593 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/PlannerOverride.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/PlannerOverride.java @@ -16,14 +16,13 @@ * @author Marcin Czeczko - Initial contribution */ public class PlannerOverride { - private String action; + private Action action; - public String getAction() { + public Action getAction() { return action; } - public PlannerOverride setAction(String action) { + public void setAction(Action action) { this.action = action; - return this; } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/RestrictedReason.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/RestrictedReason.java index ac3002ec3d842..64c9c2e5bb60d 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/RestrictedReason.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/RestrictedReason.java @@ -21,5 +21,8 @@ public enum RestrictedReason { PARK_OVERRIDE, SENSOR, DAILY_LIMIT, - NOT_APPLICABLE + FOTA, + FROST, + ALL_WORK_AREAS_COMPLETED, + EXTERNAL } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Settings.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Settings.java new file mode 100644 index 0000000000000..837862e82999a --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Settings.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class Settings { + private byte cuttingHeight; + private Headlight headlight; + + public byte getCuttingHeight() { + return cuttingHeight; + } + + public void setCuttingHeight(byte cuttingHeight) { + this.cuttingHeight = cuttingHeight; + } + + public Headlight getHeadlight() { + return headlight; + } + + public void setHeadlight(Headlight headlight) { + this.headlight = headlight; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java new file mode 100644 index 0000000000000..dff8ebc1b29b1 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class Statistics { + private long cuttingBladeUsageTime; + private long downTime; + private int numberOfChargingCycles; + private int numberOfCollisions; + private long totalChargingTime; + private long totalCuttingTime; + private long totalDriveDistance; // docu states totalDrivenDistance which does not work + private long totalRunningTime; + private long totalSearchingTime; + private long upTime; + + public long getCuttingBladeUsageTime() { + return cuttingBladeUsageTime; + } + + public void setCuttingBladeUsageTime(long cuttingBladeUsageTime) { + this.cuttingBladeUsageTime = cuttingBladeUsageTime; + } + + public long getDownTime() { + return downTime; + } + + public void setDownTime(long downTime) { + this.downTime = downTime; + } + + public int getNumberOfChargingCycles() { + return numberOfChargingCycles; + } + + public void setNumberOfChargingCycles(int numberOfChargingCycles) { + this.numberOfChargingCycles = numberOfChargingCycles; + } + + public int getNumberOfCollisions() { + return numberOfCollisions; + } + + public void setNumberOfCollisions(int numberOfCollisions) { + this.numberOfCollisions = numberOfCollisions; + } + + public long getTotalChargingTime() { + return totalChargingTime; + } + + public void setTotalChargingTime(long totalChargingTime) { + this.totalChargingTime = totalChargingTime; + } + + public long getTotalCuttingTime() { + return totalCuttingTime; + } + + public void setTotalCuttingTime(long totalCuttingTime) { + this.totalCuttingTime = totalCuttingTime; + } + + public long getTotalDriveDistance() { + return totalDriveDistance; + } + + public void setTotalDriveDistance(long totalDriveDistance) { + this.totalDriveDistance = totalDriveDistance; + } + + public long getTotalRunningTime() { + return totalRunningTime; + } + + public void setTotalRunningTime(long totalRunningTime) { + this.totalRunningTime = totalRunningTime; + } + + public long getTotalSearchingTime() { + return totalSearchingTime; + } + + public void setTotalSearchingTime(long totalSearchingTime) { + this.totalSearchingTime = totalSearchingTime; + } + + public long getUpTime() { + return upTime; + } + + public void setUpTime(long upTime) { + this.upTime = upTime; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZone.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZone.java new file mode 100644 index 0000000000000..2be44d338e4d6 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZone.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class StayOutZone { + private String id; + private String name; + private Boolean enabled; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Boolean isEnabled() { + return enabled; + } + + public void setEnabed(Boolean enabled) { + this.enabled = enabled; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZones.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZones.java new file mode 100644 index 0000000000000..dde68d5c25604 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZones.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author MikeTheTux - Initial contribution + */ +public class StayOutZones { + private Boolean dirty; + private List zones = new ArrayList<>(); + + public Boolean isDirty() { + return dirty; + } + + public void setDirty(Boolean dirty) { + this.dirty = dirty; + } + + public List getZones() { + return zones; + } + + public void setZones(List zones) { + this.zones = zones; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java new file mode 100644 index 0000000000000..4eca587d6f2ad --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2010-2024 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.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class WorkArea { + private long workAreaId; + private String name; + private byte cuttingHeight; + private boolean enabled; + private Byte progress; // Only available for EPOS mowers and systematic mowing work areas. + private Long lastTimeCompleted; // Only available for EPOS mowers and systematic mowing work areas. + + public long getWorkAreaId() { + return workAreaId; + } + + public void setWorkAreaId(long workAreaId) { + this.workAreaId = workAreaId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public byte getCuttingHeight() { + return cuttingHeight; + } + + public void setCuttingHeight(byte cuttingHeight) { + this.cuttingHeight = cuttingHeight; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public Byte getProgress() { + return progress; + } + + public void setProgress(Byte progress) { + this.progress = progress; + } + + public Long getLastTimeCompleted() { + return lastTimeCompleted; + } + + public void setLastTimeCompleted(Long lastTimeCompleted) { + this.lastTimeCompleted = lastTimeCompleted; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerCommand.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerCommand.java index 2849c9345ce98..24a1f74365b93 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerCommand.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerCommand.java @@ -18,6 +18,7 @@ import java.util.Optional; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.automower.internal.AutomowerBindingConstants; import org.openhab.core.thing.ChannelUID; /** @@ -25,12 +26,13 @@ */ @NonNullByDefault public enum AutomowerCommand { - START("Start", "start"), - RESUME_SCHEDULE("ResumeSchedule", "resume_schedule"), - PAUSE("Pause", "pause"), - PARK("Park", "park"), - PARK_UNTIL_NEXT_SCHEDULE("ParkUntilNextSchedule", "park_until_next_schedule"), - PARK_UNTIL_FURTHER_NOTICE("ParkUntilFurtherNotice", "park_until_further_notice"); + START("Start", AutomowerBindingConstants.CHANNEL_COMMAND_START), + RESUME_SCHEDULE("ResumeSchedule", AutomowerBindingConstants.CHANNEL_COMMAND_RESUME_SCHEDULE), + PAUSE("Pause", AutomowerBindingConstants.CHANNEL_COMMAND_PAUSE), + PARK("Park", AutomowerBindingConstants.CHANNEL_COMMAND_PARK), + PARK_UNTIL_NEXT_SCHEDULE("ParkUntilNextSchedule", + AutomowerBindingConstants.CHANNEL_COMMAND_PARK_UNTIL_NEXT_SCHEDULE), + PARK_UNTIL_FURTHER_NOTICE("ParkUntilFurtherNotice", AutomowerBindingConstants.CHANNEL_COMMAND_PARK_UNTIL_NOTICE); private static final Map CHANNEL_TO_CMD_MAP = new HashMap<>(); diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerConfiguration.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerConfiguration.java index 8b18d15c72181..10528a46699c2 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerConfiguration.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerConfiguration.java @@ -24,6 +24,7 @@ public class AutomowerConfiguration { public @Nullable String mowerId; public @Nullable Integer pollingInterval; + public @Nullable String mowerZoneId; @Nullable public String getMowerId() { @@ -41,4 +42,9 @@ public void setMowerId(String mowerId) { public void setPollingInterval(Integer pollingInterval) { this.pollingInterval = pollingInterval; } + + @Nullable + public String getMowerZoneId() { + return mowerZoneId; + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index b0a87a253cd4b..4a7451bcba05a 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -14,10 +14,13 @@ import static org.openhab.binding.automower.internal.AutomowerBindingConstants.*; +import java.time.DateTimeException; import java.time.Instant; +import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -31,17 +34,28 @@ import org.openhab.binding.automower.internal.actions.AutomowerActions; import org.openhab.binding.automower.internal.bridge.AutomowerBridge; import org.openhab.binding.automower.internal.bridge.AutomowerBridgeHandler; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.CalendarTask; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Capabilities; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Headlight; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.HeadlightMode; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Mower; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZoneAttributes; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerWorkAreaAttributes; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Position; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.RestrictedReason; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Settings; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.State; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.StayOutZone; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.WorkArea; import org.openhab.binding.automower.internal.rest.exceptions.AutomowerCommunicationException; import org.openhab.core.i18n.TimeZoneProvider; 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.PointType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.StringType; +import org.openhab.core.library.unit.SIUnits; import org.openhab.core.library.unit.Units; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; @@ -59,8 +73,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.Gson; - /** * The {@link AutomowerHandler} is responsible for handling commands, which are * sent to one of the channels. @@ -77,17 +89,17 @@ public class AutomowerHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(AutomowerHandler.class); private final TimeZoneProvider timeZoneProvider; + private ZoneId mowerZoneId; private AtomicReference automowerId = new AtomicReference<>(NO_ID); private long lastQueryTimeMs = 0L; private @Nullable ScheduledFuture automowerPollingJob; - private long maxQueryFrequencyNanos = TimeUnit.MINUTES.toNanos(1); + // Max 1 request per second and appKey. + private long maxQueryFrequencyNanos = TimeUnit.SECONDS.toNanos(1); private @Nullable Mower mowerState; - private Gson gson = new Gson(); - private Runnable automowerPollingRunnable = () -> { Bridge bridge = getBridge(); if (bridge != null && bridge.getStatus() == ThingStatus.ONLINE) { @@ -100,6 +112,7 @@ public class AutomowerHandler extends BaseThingHandler { public AutomowerHandler(Thing thing, TimeZoneProvider timeZoneProvider) { super(thing); this.timeZoneProvider = timeZoneProvider; + this.mowerZoneId = timeZoneProvider.getTimeZone(); // default initializer } @Override @@ -107,11 +120,64 @@ public void handleCommand(ChannelUID channelUID, Command command) { if (RefreshType.REFRESH == command) { logger.debug("Refreshing channel '{}'", channelUID); refreshChannels(channelUID); + } else if (CHANNEL_CALENDARTASKS.contains(channelUID.getId())) { + sendAutomowerCalendarTask(command, channelUID.getId()); + } else if (CHANNEL_STAYOUTZONES.contains(channelUID.getId())) { + String[] channelIDSplit = channelUID.getId().split("-"); + int index = Integer.parseInt(channelIDSplit[0].substring(GROUP_STAYOUTZONE.length())) - 1; + if ("enabled".equals(channelIDSplit[0])) { + if (command instanceof OnOffType cmd) { + sendAutomowerStayOutZone(index, ((cmd == OnOffType.ON) ? true : false)); + } + } + } else if (CHANNEL_WORKAREAS.contains(channelUID.getId())) { + String[] channelIDSplit = channelUID.getId().split("-"); + int index = Integer.parseInt(channelIDSplit[0].substring(GROUP_WORKAREA.length())) - 1; + if ("enabled".equals(channelIDSplit[0])) { + if (command instanceof OnOffType cmd) { + sendAutomowerWorkAreaEnable(index, ((cmd == OnOffType.ON) ? true : false)); + } + } else if ("cutting-height".equals(channelIDSplit[0])) { + if (command instanceof QuantityType cmd) { + cmd = cmd.toUnit("%"); + if (cmd != null) { + sendAutomowerWorkAreaCuttingHeight(index, cmd.byteValue()); + } + } else if (command instanceof DecimalType cmd) { + sendAutomowerWorkAreaCuttingHeight(index, cmd.byteValue()); + } + } + } else if (channelUID.getId().equals(CHANNEL_SETTING_CUTTING_HEIGHT)) { + if (command instanceof DecimalType cmd) { + sendAutomowerSettingsCuttingHeight(cmd.byteValue()); + } + } else if (channelUID.getId().equals(CHANNEL_SETTING_HEADLIGHT_MODE)) { + if (command instanceof StringType cmd) { + sendAutomowerSettingsHeadlightMode(cmd.toString()); + } + } else if (channelUID.getId().equals(CHANNEL_STATUS_ERROR_CODE)) { + if (command instanceof DecimalType cmd) { + if (cmd.equals(new DecimalType(0))) { + sendAutomowerConfirmError(); + } + } + } else if (channelUID.getId().equals(CHANNEL_STATISTIC_CUTTING_BLADE_USAGE_TIME)) { + if (command instanceof DecimalType cmd) { + if (cmd.equals(new DecimalType(0))) { + sendAutomowerResetCuttingBladeUsageTime(); + } + } else if (command instanceof QuantityType cmd) { + if (cmd.intValue() == 0) { + sendAutomowerResetCuttingBladeUsageTime(); + } + } } else { AutomowerCommand.fromChannelUID(channelUID).ifPresent(commandName -> { logger.debug("Sending command '{}'", commandName); getCommandValue(command).ifPresentOrElse(duration -> sendAutomowerCommand(commandName, duration), () -> sendAutomowerCommand(commandName)); + + updateState(channelUID, OnOffType.OFF); }); } } @@ -139,6 +205,17 @@ public void initialize() { AutomowerConfiguration currentConfig = getConfigAs(AutomowerConfiguration.class); final String configMowerId = currentConfig.getMowerId(); final Integer pollingIntervalS = currentConfig.getPollingInterval(); + final String configMowerZoneId = currentConfig.getMowerZoneId(); + if ((configMowerZoneId != null) && !configMowerZoneId.isBlank()) { + try { + mowerZoneId = ZoneId.of(configMowerZoneId); + } catch (DateTimeException e) { + logger.warn("Invalid configuration mowerZoneId: {}, Error: {}", mowerZoneId, e.getMessage()); + mowerZoneId = timeZoneProvider.getTimeZone(); // wrong config, use System TimeZone + } + } else { + mowerZoneId = timeZoneProvider.getTimeZone(); // not configured, use System TimeZone + } if (configMowerId == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, @@ -191,8 +268,14 @@ private void stopAutomowerPolling() { } private boolean isValidResult(@Nullable Mower mower) { - return mower != null && mower.getAttributes() != null && mower.getAttributes().getMetadata() != null - && mower.getAttributes().getBattery() != null && mower.getAttributes().getSystem() != null; + return ((mower != null && mower.getAttributes() != null) && (mower.getAttributes().getMetadata() != null) + && (mower.getAttributes().getBattery() != null) && (mower.getAttributes().getSystem() != null) + && (mower.getAttributes().getCalendar() != null) + && (mower.getAttributes().getCalendar().getTasks() != null) + && (mower.getAttributes().getCapabilities() != null) && (mower.getAttributes().getMower() != null) + && (mower.getAttributes().getPlanner() != null) + && (mower.getAttributes().getPlanner().getOverride() != null) + && (mower.getAttributes().getSettings() != null) && (mower.getAttributes().getStatistics() != null)); } private boolean isConnected(@Nullable Mower mower) { @@ -263,9 +346,364 @@ public void sendAutomowerCommand(AutomowerCommand command, long commandDurationM updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/conf-error-no-bridge"); } } catch (AutomowerCommunicationException e) { - logger.warn("Unable to send command to automower: {}, Error: {}", id, e.getMessage()); + logger.warn("Unable to send Command to automower: {}, Error: {}", id, e.getMessage()); } + updateAutomowerState(); + } + /** + * Sends a CalendarTask to the automower + * + * @param command The command that should be sent. E.g. a duration in min for the Start channel + * @param channelID The triggering channel + */ + public void sendAutomowerCalendarTask(Long workAreaId, short[] start, short[] duration, boolean[] monday, + boolean[] tuesday, boolean[] wednesday, boolean[] thursday, boolean[] friday, boolean[] saturday, + boolean[] sunday) { + if (isValidResult(mowerState)) { + List calendarTaskArray = new ArrayList<>(); + + for (int i = 0; (i < start.length) && (i < duration.length) && (i < monday.length) && (i < tuesday.length) + && (i < wednesday.length) && (i < thursday.length) && (i < friday.length) && (i < saturday.length) + && (i < sunday.length); i++) { + CalendarTask calendarTask = new CalendarTask(); + calendarTask.setStart(start[i]); + calendarTask.setDuration(duration[i]); + calendarTask.setMonday(monday[i]); + calendarTask.setTuesday(tuesday[i]); + calendarTask.setWednesday(wednesday[i]); + calendarTask.setThursday(thursday[i]); + calendarTask.setFriday(friday[i]); + calendarTask.setSaturday(saturday[i]); + calendarTask.setSunday(sunday[i]); + if (workAreaId != null) { + calendarTask.setWorkAreaId(workAreaId); + } + calendarTaskArray.add(calendarTask); + } + + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerCalendarTask(id, + mowerState.getAttributes().getCapabilities().hasWorkAreas(), workAreaId, calendarTaskArray); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/conf-error-no-bridge"); + } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send CalendarTask to automower: {}, Error: {}", id, e.getMessage()); + } + } + } + + /** + * Sends a CalendarTask to the automower + * + * @param command The command that should be sent. E.g. a duration in min for the Start channel + * @param channelID The triggering channel + */ + public void sendAutomowerCalendarTask(Command command, String channelID) { + String[] channelIDSplit = channelID.split("-"); + int index = Integer.parseInt(channelIDSplit[0].substring("calendartasks".length())) - 1; + String param = channelIDSplit[1]; + logger.debug("Sending CalendarTask '{}', index '{}', param '{}', command '{}'", channelID, index, param, + command.toString()); + + if (isValidResult(mowerState)) { + List calendarTasksFiltered; + List calendarTasksAll = mowerState.getAttributes().getCalendar().getTasks(); + int indexFiltered = 0; + if (mowerState.getAttributes().getCapabilities().hasWorkAreas()) { + // only set the Tasks of the current WorkArea + calendarTasksFiltered = new ArrayList<>(); + int i = 0; + for (CalendarTask calendarTask : calendarTasksAll) { + if (calendarTask.getWorkAreaId().equals(calendarTasksAll.get(index).getWorkAreaId())) { + if (index == i) { + // remember index and create deep copy + indexFiltered = calendarTasksFiltered.size(); + + CalendarTask calendarTask2 = new CalendarTask(); + calendarTask2.setStart(calendarTask.getStart()); + calendarTask2.setDuration(calendarTask.getDuration()); + calendarTask2.setMonday(calendarTask.getMonday()); + calendarTask2.setTuesday(calendarTask.getTuesday()); + calendarTask2.setWednesday(calendarTask.getWednesday()); + calendarTask2.setThursday(calendarTask.getThursday()); + calendarTask2.setFriday(calendarTask.getFriday()); + calendarTask2.setSaturday(calendarTask.getSaturday()); + calendarTask2.setSunday(calendarTask.getSunday()); + calendarTask2.setWorkAreaId(calendarTask.getWorkAreaId()); + calendarTasksFiltered.add(calendarTask2); + } else { + // no deep copy required for the lines that are not updated + calendarTasksFiltered.add(calendarTask); + } + } + i++; + } + } else { + indexFiltered = index; + calendarTasksFiltered = calendarTasksAll; + } + + CalendarTask calendarTask = calendarTasksFiltered.get(indexFiltered); + + if (command instanceof DecimalType cmd) { + if ("start".equals(param)) { + calendarTask.setStart(cmd.shortValue()); + } else if ("duration".equals(param)) { + calendarTask.setDuration(cmd.shortValue()); + } + } else if (command instanceof QuantityType cmd) { + cmd = cmd.toUnit("min"); + if (cmd != null) { + if ("start".equals(param)) { + calendarTask.setStart(cmd.shortValue()); + } else if ("duration".equals(param)) { + calendarTask.setDuration(cmd.shortValue()); + } + } + } else if (command instanceof OnOffType cmd) { + boolean day = ((cmd == OnOffType.ON) ? true : false); + + if ("monday".equals(param)) { + calendarTask.setMonday(day); + } else if ("tuesday".equals(param)) { + calendarTask.setTuesday(day); + } else if ("wednesday".equals(param)) { + calendarTask.setWednesday(day); + } else if ("thursday".equals(param)) { + calendarTask.setThursday(day); + } else if ("friday".equals(param)) { + calendarTask.setFriday(day); + } else if ("saturday".equals(param)) { + calendarTask.setSaturday(day); + } else if ("sunday".equals(param)) { + calendarTask.setSunday(day); + } + + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerCalendarTask(id, + mowerState.getAttributes().getCapabilities().hasWorkAreas(), + calendarTask.getWorkAreaId(), calendarTasksFiltered); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/conf-error-no-bridge"); + } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send CalendarTask to automower: {}, Error: {}", id, e.getMessage()); + } + } + } + updateAutomowerState(); + } + + /** + * Sends StayOutZone Setting to the automower + * + * @param index Index of zone + * @param enable Zone enabled or disabled + */ + public void sendAutomowerStayOutZone(int index, boolean enable) { + if (isValidResult(mowerState)) { + sendAutomowerStayOutZone(mowerState.getAttributes().getStayOutZones().getZones().get(index).getId(), + enable); + } + } + + /** + * Sends StayOutZone Setting to the automower + * + * @param zoneId Id of zone + * @param enable Zone enabled or disabled + */ + public void sendAutomowerStayOutZone(String zoneId, boolean enable) { + logger.debug("Sending StayOutZone: zoneId {}, enable {}", zoneId, enable); + if (isValidResult(mowerState)) { + MowerStayOutZoneAttributes attributes = new MowerStayOutZoneAttributes(); + attributes.setEnable(enable); + + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerStayOutZone(id, zoneId, attributes); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/conf-error-no-bridge"); + } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send StayOutZone to automower: {}, Error: {}", id, e.getMessage()); + } + + updateAutomowerState(); + } + } + + /** + * Sends WorkArea enable Setting to the automower + * + * @param index Index of WorkArea + * @param enable WorkArea enabled or disabled + * + */ + public void sendAutomowerWorkAreaEnable(int index, boolean enable) { + if (isValidResult(mowerState)) { + sendAutomowerWorkArea(mowerState.getAttributes().getWorkAreas().get(index).getWorkAreaId(), enable, + mowerState.getAttributes().getWorkAreas().get(index).getCuttingHeight()); + } + } + + /** + * Sends WorkArea CuttingHeight Setting to the automower + * + * @param index Index of WorkArea + * @param cuttingHeight CuttingHeight of the WorkArea + * + */ + public void sendAutomowerWorkAreaCuttingHeight(int index, byte cuttingHeight) { + if (isValidResult(mowerState)) { + sendAutomowerWorkArea(mowerState.getAttributes().getWorkAreas().get(index).getWorkAreaId(), + mowerState.getAttributes().getWorkAreas().get(index).isEnabled(), cuttingHeight); + } + } + + /** + * Sends WorkArea Settings to the automower + * + * @param workAreaId Id of WorkArea + * @param enable Work area enable or disabled + * @param cuttingHeight CuttingHeight of the WorkArea + */ + public void sendAutomowerWorkArea(long workAreaId, boolean enable, byte cuttingHeight) { + logger.debug("Sending WorkArea: workAreaId {}, enable {}, cuttingHeight {}", workAreaId, enable, cuttingHeight); + if (isValidResult(mowerState)) { + MowerWorkAreaAttributes workAreaAttributes = new MowerWorkAreaAttributes(); + workAreaAttributes.setEnable(enable); + workAreaAttributes.setCuttingHeight(cuttingHeight); + + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerWorkArea(id, workAreaId, workAreaAttributes); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/conf-error-no-bridge"); + } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send WorkArea to automower: {}, Error: {}", id, e.getMessage()); + } + + updateAutomowerState(); + } + } + + /** + * Sends CuttingHeight Setting to the automower + * + * @param cuttingHeight The cuttingHeight to be sent + */ + public void sendAutomowerSettingsCuttingHeight(byte cuttingHeight) { + if (isValidResult(mowerState)) { + sendAutomowerSettings(cuttingHeight, + mowerState.getAttributes().getSettings().getHeadlight().getHeadlightMode()); + } + } + + /** + * Sends HeadlightMode Setting to the automower + * + * @param headlightMode Headlight mode as string to be sent + */ + public void sendAutomowerSettingsHeadlightMode(String headlightMode) { + if (isValidResult(mowerState)) { + try { + sendAutomowerSettings(mowerState.getAttributes().getSettings().getCuttingHeight(), + HeadlightMode.valueOf(headlightMode)); + } catch (IllegalArgumentException e) { + logger.warn("Invalid HeadlightMode: {}, Error: {}", headlightMode, e.getMessage()); + } + } + } + + /** + * Sends a set of Settings to the automower + * + * @param cuttingHeight The cuttingHeight to be sent + * @param headlightMode Headlight mode as string to be sent + */ + public void sendAutomowerSettings(byte cuttingHeight, HeadlightMode headlightMode) { + logger.debug("Sending Settings: cuttingHeight {}, headlightMode {}", cuttingHeight, headlightMode.toString()); + if (isValidResult(mowerState)) { + Settings settings = new Settings(); + settings.setCuttingHeight(cuttingHeight); + Headlight headlight = new Headlight(); + headlight.setHeadlightMode(headlightMode); + settings.setHeadlight(headlight); + + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerSettings(id, settings); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/conf-error-no-bridge"); + } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send SettingCuttingHeight to automower: {}, Error: {}", id, e.getMessage()); + } + + updateAutomowerState(); + } + } + + /** + * Confirm current non fatal error on the mower + */ + public void sendAutomowerConfirmError() { + logger.debug("Sending ConfirmError"); + if (isValidResult(mowerState) && (mowerState.getAttributes().getCapabilities().canConfirmError()) + && (mowerState.getAttributes().getMower().getIsErrorConfirmable())) { + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerConfirmError(id); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/conf-error-no-bridge"); + } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send ConfirmError to automower: {}, Error: {}", id, e.getMessage()); + } + updateAutomowerState(); + } + } + + /** + * Reset the cutting blade usage time + */ + public void sendAutomowerResetCuttingBladeUsageTime() { + logger.debug("Sending ResetCuttingBladeUsageTime"); + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerResetCuttingBladeUsageTime(id); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/conf-error-no-bridge"); + } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send ResetCuttingBladeUsageTime to automower: {}, Error: {}", id, e.getMessage()); + } updateAutomowerState(); } @@ -273,11 +711,30 @@ private String restrictedState(RestrictedReason reason) { return "RESTRICTED_" + reason.name(); } + private @Nullable String getErrorMessage(int errorCode) { + return ERROR.get(errorCode); + } + + private @Nullable WorkArea getWorkAreaById(@Nullable Mower mower, long workAreaId) { + if (mower != null) { + List workAreas = mower.getAttributes().getWorkAreas(); + for (WorkArea workArea : workAreas) { + if (workArea.getWorkAreaId() == workAreaId) { + return workArea; + } + } + } + return null; + } + private void updateChannelState(@Nullable Mower mower) { if (isValidResult(mower)) { updateState(CHANNEL_STATUS_NAME, new StringType(mower.getAttributes().getSystem().getName())); + updateState(CHANNEL_STATUS_MODE, new StringType(mower.getAttributes().getMower().getMode().name())); updateState(CHANNEL_STATUS_ACTIVITY, new StringType(mower.getAttributes().getMower().getActivity().name())); + updateState(CHANNEL_STATUS_INACTIVE_REASON, + new StringType(mower.getAttributes().getMower().getInactiveReason().name())); if (mower.getAttributes().getMower().getState() != State.RESTRICTED) { updateState(CHANNEL_STATUS_STATE, new StringType(mower.getAttributes().getMower().getState().name())); @@ -286,71 +743,257 @@ private void updateChannelState(@Nullable Mower mower) { new StringType(restrictedState(mower.getAttributes().getPlanner().getRestrictedReason()))); } - updateState(CHANNEL_STATUS_LAST_UPDATE, - new DateTimeType(toZonedDateTime(mower.getAttributes().getMetadata().getStatusTimestamp()))); + Long workAreaId = mower.getAttributes().getMower().getWorkAreaId(); + if (workAreaId != null) { + updateState(CHANNEL_STATUS_WORK_AREA_ID, new DecimalType(workAreaId)); + WorkArea workArea = getWorkAreaById(mower, workAreaId); + if ((workArea != null) && (workArea.getName() != null)) { + if ((workAreaId.equals(0L)) && workArea.getName().isBlank()) { + updateState(CHANNEL_STATUS_WORK_AREA, new StringType("main area")); + } else { + updateState(CHANNEL_STATUS_WORK_AREA, new StringType(workArea.getName())); + } + } else { + updateState(CHANNEL_STATUS_WORK_AREA, UnDefType.NULL); + } + } else { + updateState(CHANNEL_STATUS_WORK_AREA_ID, UnDefType.NULL); + } + + updateState(CHANNEL_STATUS_LAST_UPDATE, new DateTimeType( + toZonedDateTime(mower.getAttributes().getMetadata().getStatusTimestamp(), ZoneId.of("UTC")))); updateState(CHANNEL_STATUS_BATTERY, new QuantityType<>(mower.getAttributes().getBattery().getBatteryPercent(), Units.PERCENT)); - updateState(CHANNEL_STATUS_ERROR_CODE, new DecimalType(mower.getAttributes().getMower().getErrorCode())); + int errorCode = mower.getAttributes().getMower().getErrorCode(); + updateState(CHANNEL_STATUS_ERROR_CODE, new DecimalType(errorCode)); + String errorMessage = getErrorMessage(errorCode); + if (errorMessage != null) { + updateState(CHANNEL_STATUS_ERROR_MESSAGE, new StringType(errorMessage)); + } else { + updateState(CHANNEL_STATUS_ERROR_MESSAGE, UnDefType.NULL); + } long errorCodeTimestamp = mower.getAttributes().getMower().getErrorCodeTimestamp(); if (errorCodeTimestamp == 0L) { updateState(CHANNEL_STATUS_ERROR_TIMESTAMP, UnDefType.NULL); } else { - updateState(CHANNEL_STATUS_ERROR_TIMESTAMP, new DateTimeType(toZonedDateTime(errorCodeTimestamp))); + updateState(CHANNEL_STATUS_ERROR_TIMESTAMP, + new DateTimeType(toZonedDateTime(errorCodeTimestamp, mowerZoneId))); } + updateState(CHANNEL_STATUS_ERROR_CONFIRMABLE, + OnOffType.from(mower.getAttributes().getMower().getIsErrorConfirmable())); + long nextStartTimestamp = mower.getAttributes().getPlanner().getNextStartTimestamp(); // If next start timestamp is 0 it means the mower should start now, so using current timestamp if (nextStartTimestamp == 0L) { - updateState(CHANNEL_PLANNER_NEXT_START, UnDefType.NULL); + updateState(CHANNEL_STATUS_NEXT_START, UnDefType.NULL); + } else { + updateState(CHANNEL_STATUS_NEXT_START, + new DateTimeType(toZonedDateTime(nextStartTimestamp, mowerZoneId))); + } + updateState(CHANNEL_STATUS_OVERRIDE_ACTION, + new StringType(mower.getAttributes().getPlanner().getOverride().getAction().name())); + RestrictedReason restrictedReason = mower.getAttributes().getPlanner().getRestrictedReason(); + if (restrictedReason != null) { + updateState(CHANNEL_STATUS_RESTRICTED_REASON, new StringType(restrictedReason.name())); + } else { + updateState(CHANNEL_STATUS_RESTRICTED_REASON, UnDefType.NULL); + } + + updateState(CHANNEL_STATUS_EXTERNAL_REASON, + new DecimalType(mower.getAttributes().getPlanner().getExternalReason())); + + updateState(CHANNEL_SETTING_CUTTING_HEIGHT, + new DecimalType(mower.getAttributes().getSettings().getCuttingHeight())); + + Headlight headlight = mower.getAttributes().getSettings().getHeadlight(); + if (headlight != null) { + updateState(CHANNEL_SETTING_HEADLIGHT_MODE, new StringType(headlight.getHeadlightMode().name())); + } else { + updateState(CHANNEL_SETTING_HEADLIGHT_MODE, UnDefType.NULL); + } + + updateState(CHANNEL_STATISTIC_CUTTING_BLADE_USAGE_TIME, + new QuantityType<>(mower.getAttributes().getStatistics().getCuttingBladeUsageTime(), Units.SECOND)); + updateState(CHANNEL_STATISTIC_DOWN_TIME, + new QuantityType<>(mower.getAttributes().getStatistics().getDownTime(), Units.SECOND)); + updateState(CHANNEL_STATISTIC_NUMBER_OF_CHARGING_CYCLES, + new DecimalType(mower.getAttributes().getStatistics().getNumberOfChargingCycles())); + updateState(CHANNEL_STATISTIC_NUMBER_OF_COLLISIONS, + new DecimalType(mower.getAttributes().getStatistics().getNumberOfCollisions())); + updateState(CHANNEL_STATISTIC_TOTAL_CHARGING_TIME, + new QuantityType<>(mower.getAttributes().getStatistics().getTotalChargingTime(), Units.SECOND)); + updateState(CHANNEL_STATISTIC_TOTAL_CUTTING_TIME, + new QuantityType<>(mower.getAttributes().getStatistics().getTotalCuttingTime(), Units.SECOND)); + updateState(CHANNEL_STATISTIC_TOTAL_DRIVE_DISTANCE, + new QuantityType<>(mower.getAttributes().getStatistics().getTotalDriveDistance(), SIUnits.METRE)); + updateState(CHANNEL_STATISTIC_TOTAL_RUNNING_TIME, + new QuantityType<>(mower.getAttributes().getStatistics().getTotalRunningTime(), Units.SECOND)); + updateState(CHANNEL_STATISTIC_TOTAL_SEARCHING_TIME, + new QuantityType<>(mower.getAttributes().getStatistics().getTotalSearchingTime(), Units.SECOND)); + + if (mower.getAttributes().getStatistics().getTotalRunningTime() != 0) { + updateState(CHANNEL_STATISTIC_TOTAL_CUTTING_PERCENT, + new QuantityType<>( + (float) mower.getAttributes().getStatistics().getTotalCuttingTime() + / (float) mower.getAttributes().getStatistics().getTotalRunningTime() * 100.0, + Units.PERCENT)); + updateState(CHANNEL_STATISTIC_TOTAL_SEARCHING_PERCENT, + new QuantityType<>( + (float) mower.getAttributes().getStatistics().getTotalSearchingTime() + / (float) mower.getAttributes().getStatistics().getTotalRunningTime() * 100.0, + Units.PERCENT)); + } else { + updateState(CHANNEL_STATISTIC_TOTAL_CUTTING_PERCENT, new QuantityType<>(0, Units.PERCENT)); + updateState(CHANNEL_STATISTIC_TOTAL_SEARCHING_PERCENT, new QuantityType<>(0, Units.PERCENT)); + } + updateState(CHANNEL_STATISTIC_UP_TIME, + new QuantityType<>(mower.getAttributes().getStatistics().getUpTime(), Units.SECOND)); + + if (mower.getAttributes().getLastPosition() != null) { + updateState(LAST_POSITION, + new PointType(new DecimalType(mower.getAttributes().getLastPosition().getLatitude()), + new DecimalType(mower.getAttributes().getLastPosition().getLongitude()))); + List positions = mower.getAttributes().getPositions(); + for (int i = 0; i < positions.size(); i++) { + updateState(CHANNEL_POSITIONS.get(i), new PointType(new DecimalType(positions.get(i).getLatitude()), + new DecimalType(positions.get(i).getLongitude()))); + } } else { - updateState(CHANNEL_PLANNER_NEXT_START, new DateTimeType(toZonedDateTime(nextStartTimestamp))); + updateState(LAST_POSITION, UnDefType.NULL); + for (int i = 0; i < CHANNEL_POSITIONS.size(); i++) { + updateState(CHANNEL_POSITIONS.get(i), UnDefType.NULL); + } } - updateState(CHANNEL_PLANNER_OVERRIDE_ACTION, - new StringType(mower.getAttributes().getPlanner().getOverride().getAction())); - updateState(CHANNEL_CALENDAR_TASKS, - new StringType(gson.toJson(mower.getAttributes().getCalendar().getTasks()))); + updateState(CHANNEL_STAYOUTZONES_DIRTY, OnOffType.from(mower.getAttributes().getStayOutZones().isDirty())); + + List calendarTasks = mower.getAttributes().getCalendar().getTasks(); + int j = 0; + for (int i = 0; i < calendarTasks.size() && j < CHANNEL_CALENDARTASKS.size(); i++) { + updateState(CHANNEL_CALENDARTASKS.get(j++), + new QuantityType<>(calendarTasks.get(i).getStart(), Units.MINUTE)); + updateState(CHANNEL_CALENDARTASKS.get(j++), + new QuantityType<>(calendarTasks.get(i).getDuration(), Units.MINUTE)); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getMonday())); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getTuesday())); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getWednesday())); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getThursday())); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getFriday())); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getSaturday())); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getSunday())); + if (calendarTasks.get(i).getWorkAreaId() == null) { + updateState(CHANNEL_CALENDARTASKS.get(j++), UnDefType.NULL); + updateState(CHANNEL_CALENDARTASKS.get(j++), UnDefType.NULL); + } else { + updateState(CHANNEL_CALENDARTASKS.get(j++), new DecimalType(calendarTasks.get(i).getWorkAreaId())); + WorkArea workArea = getWorkAreaById(mower, calendarTasks.get(i).getWorkAreaId()); + if (workArea != null) { + if ((calendarTasks.get(i).getWorkAreaId().equals(0L)) && workArea.getName().isBlank()) { + updateState(CHANNEL_CALENDARTASKS.get(j++), new StringType("main area")); + } else { + updateState(CHANNEL_CALENDARTASKS.get(j++), new StringType(workArea.getName())); + } + } else { + updateState(CHANNEL_CALENDARTASKS.get(j++), UnDefType.NULL); + } + } + } + // clear remaining channels + for (; j < CHANNEL_CALENDARTASKS.size(); j++) { + updateState(CHANNEL_CALENDARTASKS.get(j), UnDefType.NULL); + } - updateState(LAST_POSITION, - new PointType(new DecimalType(mower.getAttributes().getLastPosition().getLatitude()), - new DecimalType(mower.getAttributes().getLastPosition().getLongitude()))); - ArrayList positions = mower.getAttributes().getPositions(); - for (int i = 0; i < positions.size(); i++) { - updateState(CHANNEL_POSITIONS.get(i), new PointType(new DecimalType(positions.get(i).getLatitude()), - new DecimalType(positions.get(i).getLongitude()))); + j = 0; + if (mower.getAttributes().getStayOutZones() != null) { + List stayOutZones = mower.getAttributes().getStayOutZones().getZones(); + if (stayOutZones != null) { + for (int i = 0; i < stayOutZones.size() && j < CHANNEL_STAYOUTZONES.size(); i++) { + updateState(CHANNEL_STAYOUTZONES.get(j++), new StringType(stayOutZones.get(i).getId())); + updateState(CHANNEL_STAYOUTZONES.get(j++), new StringType(stayOutZones.get(i).getName())); + updateState(CHANNEL_STAYOUTZONES.get(j++), OnOffType.from(stayOutZones.get(i).isEnabled())); + } + } + } + // clear remaining channels + for (; j < CHANNEL_STAYOUTZONES.size(); j++) { + updateState(CHANNEL_STAYOUTZONES.get(j), UnDefType.NULL); + } + j = 0; + List workAreas = mower.getAttributes().getWorkAreas(); + if (workAreas != null) { + for (int i = 0; i < workAreas.size() && j < CHANNEL_WORKAREAS.size(); i++) { + WorkArea workArea = workAreas.get(i); + updateState(CHANNEL_WORKAREAS.get(j++), new DecimalType(workArea.getWorkAreaId())); + if ((workArea.getWorkAreaId() == 0L) && workArea.getName().isBlank()) { + updateState(CHANNEL_WORKAREAS.get(j++), new StringType("main area")); + } else { + updateState(CHANNEL_WORKAREAS.get(j++), new StringType(workArea.getName())); + } + updateState(CHANNEL_WORKAREAS.get(j++), + new QuantityType<>(workArea.getCuttingHeight(), Units.PERCENT)); + updateState(CHANNEL_WORKAREAS.get(j++), OnOffType.from(workArea.isEnabled())); + if (workArea.getProgress() != null) { + updateState(CHANNEL_WORKAREAS.get(j++), + new QuantityType<>(workArea.getProgress(), Units.PERCENT)); + } else { + updateState(CHANNEL_WORKAREAS.get(j++), UnDefType.NULL); + } + + if ((workArea.getLastTimeCompleted() != null) && (workArea.getLastTimeCompleted() != 0)) { + updateState(CHANNEL_WORKAREAS.get(j++), + new DateTimeType(toZonedDateTime(workArea.getLastTimeCompleted(), mowerZoneId))); + } else { + updateState(CHANNEL_WORKAREAS.get(j++), UnDefType.NULL); + } + } + } + // clear remaining channels + for (; j < CHANNEL_WORKAREAS.size(); j++) { + updateState(CHANNEL_WORKAREAS.get(j), UnDefType.NULL); } } } private void initializeProperties(@Nullable Mower mower) { - Map properties = editProperties(); - - properties.put(AutomowerBindingConstants.AUTOMOWER_ID, mower.getId()); + if (isValidResult(mower)) { + Map properties = editProperties(); + properties.put(AutomowerBindingConstants.AUTOMOWER_ID, mower.getId()); - if (mower.getAttributes() != null && mower.getAttributes().getSystem() != null) { properties.put(AutomowerBindingConstants.AUTOMOWER_SERIAL_NUMBER, mower.getAttributes().getSystem().getSerialNumber()); properties.put(AutomowerBindingConstants.AUTOMOWER_MODEL, mower.getAttributes().getSystem().getModel()); properties.put(AutomowerBindingConstants.AUTOMOWER_NAME, mower.getAttributes().getSystem().getName()); - } - updateProperties(properties); + Capabilities capabilities = mower.getAttributes().getCapabilities(); + properties.put(AutomowerBindingConstants.AUTOMOWER_CAN_CONFIRM_ERROR, + (capabilities.canConfirmError() ? "yes" : "no")); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_HEADLIGHTS, + (capabilities.hasHeadlights() ? "yes" : "no")); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_POSITION, + (capabilities.hasPosition() ? "yes" : "no")); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_STAY_OUT_ZONES, + (capabilities.hasStayOutZones() ? "yes" : "no")); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_WORK_AREAS, + (capabilities.hasWorkAreas() ? "yes" : "no")); + + updateProperties(properties); + } } /** - * Converts timestamp returned by the Automower API into local time-zone. - * Timestamp returned by the API doesn't have offset and it always in the current time zone - it can be treated as - * UTC. - * Method builds a ZonedDateTime with same hour value but in the current system timezone. + * Converts timestamp returned by the Automower API into ZonedDateTime of the specified timezone. + * Timestamp returned by the API is a mix of timezone specified via zoneId and UTC timezone. + * Method builds a valid ZonedDateTime object according to the provided zoneId parameter. * * @param timestamp - Automower API timestamp - * @return ZonedDateTime in system timezone + * @param zoneId - Intended timezone of the timestamp + * @return ZonedDateTime using provided timezone */ - private ZonedDateTime toZonedDateTime(long timestamp) { - Instant timestampInstant = Instant.ofEpochMilli(timestamp); - return ZonedDateTime.ofInstant(timestampInstant, timeZoneProvider.getTimeZone()); + private ZonedDateTime toZonedDateTime(long timestamp, ZoneId zoneId) { + return ZonedDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.of("UTC")).withZoneSameLocal(zoneId); } } diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties index 982b3cf5478cb..84b1bfd088b0f 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties @@ -7,7 +7,6 @@ addon.automower.description = Binding to interact with Husquvarna Automower robo thing-type.automower.automower.label = Automower thing-type.automower.automower.description = An automatic lawn mower -thing-type.automower.automower.channel.last-position.description = Last Position thing-type.automower.bridge.label = Automower Connect Bridge thing-type.automower.bridge.description = The bridge to communicate with the Automower Connect API @@ -15,6 +14,8 @@ thing-type.automower.bridge.description = The bridge to communicate with the Aut thing-type.config.automower.automower.mowerId.label = Automower Id thing-type.config.automower.automower.mowerId.description = The Id of an automower as used by the Automower Connect Api to identify a mower +thing-type.config.automower.automower.mowerZoneId.label = TimeZone +thing-type.config.automower.automower.mowerZoneId.description = Time zone of the mower (e.g. Europe/Berlin) thing-type.config.automower.automower.pollingInterval.label = Polling Interval thing-type.config.automower.automower.pollingInterval.description = How often the current automower state should be polled in seconds thing-type.config.automower.bridge.appKey.label = Application Key @@ -24,12 +25,23 @@ thing-type.config.automower.bridge.appSecret.description = The Application Secre thing-type.config.automower.bridge.pollingInterval.label = Polling Interval thing-type.config.automower.bridge.pollingInterval.description = How often the available automowers should be queried in seconds +# channel group types + +channel-group-type.automower.calendarTasksType.label = Calendar Tasks +channel-group-type.automower.commandsType.label = Commands +channel-group-type.automower.positionsType.label = Positions +channel-group-type.automower.settingsType.label = Settings +channel-group-type.automower.statisticsType.label = Statistics +channel-group-type.automower.statusType.label = Status +channel-group-type.automower.stayoutZonesType.label = Stayout Zones +channel-group-type.automower.workAreasType.label = Work Areas + # channel types channel-type.automower.activityType.label = Activity channel-type.automower.activityType.description = The current activity channel-type.automower.activityType.state.option.UNKNOWN = Unknown -channel-type.automower.activityType.state.option.NOT_APPLICABLE = N/A +channel-type.automower.activityType.state.option.NOT_APPLICABLE = Not Applicable channel-type.automower.activityType.state.option.MOWING = Mowing channel-type.automower.activityType.state.option.GOING_HOME = Returning to charging station channel-type.automower.activityType.state.option.CHARGING = Charging @@ -38,12 +50,43 @@ channel-type.automower.activityType.state.option.PARKED_IN_CS = Parked in chargi channel-type.automower.activityType.state.option.STOPPED_IN_GARDEN = Stopped in garden channel-type.automower.batteryType.label = Battery channel-type.automower.batteryType.description = The battery level of the mower at the time of last update -channel-type.automower.calendarTasksType.label = Planner Info JSON -channel-type.automower.calendarTasksType.description = The channel providing a JSON with the information about the planner tasks. +channel-type.automower.calendarTaskDurationType.label = Duration +channel-type.automower.calendarTaskDurationType.description = Duration time +channel-type.automower.calendarTaskDurationType.state.pattern = %1$tHh:%1$tMm +channel-type.automower.calendarTaskFridayType.label = Enabled on Fridays +channel-type.automower.calendarTaskFridayType.description = Enabled on Fridays +channel-type.automower.calendarTaskMondayType.label = Enabled on Mondays +channel-type.automower.calendarTaskMondayType.description = Enabled on Mondays +channel-type.automower.calendarTaskSaturdayType.label = Enabled on Saturdays +channel-type.automower.calendarTaskSaturdayType.description = Enabled on Saturdays +channel-type.automower.calendarTaskStartType.label = Start Time +channel-type.automower.calendarTaskStartType.description = Start time relative to midnight +channel-type.automower.calendarTaskStartType.state.pattern = %1$tHh:%1$tMm +channel-type.automower.calendarTaskSundayType.label = Enabled on Sundays +channel-type.automower.calendarTaskSundayType.description = Enabled on Sundays +channel-type.automower.calendarTaskThursdayType.label = Enabled on Thursdays +channel-type.automower.calendarTaskThursdayType.description = Enabled on Thursdays +channel-type.automower.calendarTaskTuesdayType.label = Enabled on Tuesdays +channel-type.automower.calendarTaskTuesdayType.description = Enabled on Tuesdays +channel-type.automower.calendarTaskWednesdayType.label = Enabled on Wednesdays +channel-type.automower.calendarTaskWednesdayType.description = Enabled on Wednesdays +channel-type.automower.calendarTaskWorkAreaIdType.label = Work Area Id of Calendar +channel-type.automower.calendarTaskWorkAreaIdType.description = Work Area Id mapped to this calendar channel-type.automower.errorCodeType.label = Error Code channel-type.automower.errorCodeType.description = The error code at the time of last update +channel-type.automower.errorConfirmableType.label = Error Confirmable +channel-type.automower.errorConfirmableType.description = If the mower has an Error Code this attribute states if the error is confirmable +channel-type.automower.errorMessageType.label = Error Message +channel-type.automower.errorMessageType.description = The error message at the time of last update channel-type.automower.errorTimestampType.label = Error Time channel-type.automower.errorTimestampType.description = The time when the error occurred +channel-type.automower.inactiveReasonType.label = Inactive Reason +channel-type.automower.inactiveReasonType.description = The current reason for being inactive +channel-type.automower.inactiveReasonType.state.option.NONE = No inactive reason +channel-type.automower.inactiveReasonType.state.option.PLANNING = The mower is planning a path or a work area +channel-type.automower.inactiveReasonType.state.option.SEARCHING_FOR_SATELLITES = Waiting for fix when using EPOS +channel-type.automower.lastPositionType.label = Last GPS Position +channel-type.automower.lastPositionType.description = Last GPS position of the mower channel-type.automower.lastUpdateType.label = Last Update channel-type.automower.lastUpdateType.description = The time when the mower sent the last update channel-type.automower.modeType.label = Mode @@ -63,16 +106,59 @@ channel-type.automower.parkUntilNextSchedule.label = Park Until Next Schedule channel-type.automower.parkUntilNextSchedule.description = Park until next schedule channel-type.automower.pause.label = Pause channel-type.automower.pause.description = Pause the mower now until manual resume +channel-type.automower.plannerExternalReasonType.label = External Reason +channel-type.automower.plannerExternalReasonType.description = The channel providing an external reason that restrics current planner operation. channel-type.automower.plannerNextStartTimestampType.label = Next Auto Start channel-type.automower.plannerNextStartTimestampType.description = The channel providing the time for the next auto start. If the mower is charging then the value is the estimated time when it will be leaving the charging station. If the mower is about to start now, the value is NULL. channel-type.automower.plannerOverrideActionType.label = Override Action channel-type.automower.plannerOverrideActionType.description = The channel providing an action that overrides current planner operation. +channel-type.automower.plannerRestrictedReasonType.label = Restricted Reason +channel-type.automower.plannerRestrictedReasonType.description = The channel providing a reason that restrics current planner operation. +channel-type.automower.plannerRestrictedReasonType.state.option.NONE = No restricted reason +channel-type.automower.plannerRestrictedReasonType.state.option.WEEK_SCHEDULE = Restricted by week schedule +channel-type.automower.plannerRestrictedReasonType.state.option.PARK_OVERRIDE = Forced to park +channel-type.automower.plannerRestrictedReasonType.state.option.SENSOR = Restricted by sensor +channel-type.automower.plannerRestrictedReasonType.state.option.DAILY_LIMIT = Restricted by daily limit +channel-type.automower.plannerRestrictedReasonType.state.option.FOTA = Restricted by FOTA transfer +channel-type.automower.plannerRestrictedReasonType.state.option.FROST = Restricted by frost sensor +channel-type.automower.plannerRestrictedReasonType.state.option.ALL_WORK_AREAS_COMPLETED = Restricted: All work areas completed +channel-type.automower.plannerRestrictedReasonType.state.option.EXTERNAL = Restricted by external reason channel-type.automower.positionType.label = GPS Position -channel-type.automower.positionType.description = The channel providing a waypoint of the mower's activity. +channel-type.automower.positionType.description = A waypoint of the mower's activity channel-type.automower.resumeSchedule.label = Resume Schedule channel-type.automower.resumeSchedule.description = Resume schedule +channel-type.automower.settingCuttingHeightType.label = Cutting Height +channel-type.automower.settingCuttingHeightType.description = Prescaled cutting height, range: 1-9 +channel-type.automower.settingHeadlightModeType.label = Headlight Mode +channel-type.automower.settingHeadlightModeType.description = Information about headlights channel-type.automower.start.label = Start with Duration channel-type.automower.start.description = Start for a duration in minutes +channel-type.automower.statCuttingBladeUsageTimeType.label = Cutting Blade Usage Time +channel-type.automower.statCuttingBladeUsageTimeType.description = The time since the last reset of the cutting blade usage counter +channel-type.automower.statDownTimeType.label = Down Time +channel-type.automower.statDownTimeType.description = The time the mower has been disconnected from the cloud +channel-type.automower.statDownTimeType.state.pattern = %1$tHh:%1$tMm:%1$tSs +channel-type.automower.statNumberOfChargingCyclesType.label = Number of Charging Cycles +channel-type.automower.statNumberOfChargingCyclesType.description = Number of charging cycles +channel-type.automower.statNumberOfCollisionsType.label = Number of Collisions +channel-type.automower.statNumberOfCollisionsType.description = The total number of collisions +channel-type.automower.statTotalChargingTimeType.label = Total Charging Time +channel-type.automower.statTotalChargingTimeType.description = Total charging time +channel-type.automower.statTotalCuttingPercentType.label = Relative Total Cutting Time +channel-type.automower.statTotalCuttingPercentType.description = Total cutting time in percent +channel-type.automower.statTotalCuttingTimeType.label = Total Cutting Time +channel-type.automower.statTotalCuttingTimeType.description = Total cutting time +channel-type.automower.statTotalDriveDistanceType.label = Total Drive Distance +channel-type.automower.statTotalDriveDistanceType.description = Total driven distance +channel-type.automower.statTotalRunningTimeType.label = Total Running Time +channel-type.automower.statTotalRunningTimeType.description = The total running time (the wheel motors have been running) +channel-type.automower.statTotalSearchingPercentType.label = Relative Total Searching Time +channel-type.automower.statTotalSearchingPercentType.description = The total searching time in percent +channel-type.automower.statTotalSearchingTimeType.label = Total Searching Time +channel-type.automower.statTotalSearchingTimeType.description = The total searching time +channel-type.automower.statUpTimeType.label = Up Time +channel-type.automower.statUpTimeType.description = The time the mower has been connected to the cloud +channel-type.automower.statUpTimeType.state.pattern = %1$tHh:%1$tMm:%1$tSs channel-type.automower.stateType.label = State channel-type.automower.stateType.description = The current state channel-type.automower.stateType.state.option.UNKNOWN = Unknown @@ -81,20 +167,82 @@ channel-type.automower.stateType.state.option.PAUSED = Paused by user channel-type.automower.stateType.state.option.IN_OPERATION = Working channel-type.automower.stateType.state.option.WAIT_UPDATING = Downloading new firmware channel-type.automower.stateType.state.option.WAIT_POWER_UP = Booting mower -channel-type.automower.stateType.state.option.RESTRICTED_NONE = Waiting +channel-type.automower.stateType.state.option.RESTRICTED_NONE = No restricted reason channel-type.automower.stateType.state.option.RESTRICTED_WEEK_SCHEDULE = Restricted by week schedule channel-type.automower.stateType.state.option.RESTRICTED_PARK_OVERRIDE = Forced to park channel-type.automower.stateType.state.option.RESTRICTED_SENSOR = Restricted by sensor channel-type.automower.stateType.state.option.RESTRICTED_DAILY_LIMIT = Restricted by daily limit +channel-type.automower.stateType.state.option.RESTRICTED_FOTA = Restricted by FOTA transfer +channel-type.automower.stateType.state.option.RESTRICTED_FROST = Restricted by frost sensor +channel-type.automower.stateType.state.option.RESTRICTED_ALL_WORK_AREAS_COMPLETED = Restricted: All work areas completed +channel-type.automower.stateType.state.option.RESTRICTED_EXTERNAL = Restricted by external reason channel-type.automower.stateType.state.option.OFF = Off -channel-type.automower.stateType.state.option.STOPPED = Stopped- Manual intervention required +channel-type.automower.stateType.state.option.STOPPED = Stopped: Manual intervention required channel-type.automower.stateType.state.option.ERROR = Error channel-type.automower.stateType.state.option.FATAL_ERROR = Fatal error channel-type.automower.stateType.state.option.ERROR_AT_POWER_UP = Boot error +channel-type.automower.workAreaIdType.label = Work Area Id +channel-type.automower.workAreaIdType.description = Id of the active work area +channel-type.automower.workAreaType.label = Work Area Name +channel-type.automower.workAreaType.description = Name of the active work area +channel-type.automower.workareaCuttingHeightType.label = Cutting Height +channel-type.automower.workareaCuttingHeightType.description = Cutting height of the work area, range: 0-100 +channel-type.automower.workareaEnabledType.label = Work Area Enabled +channel-type.automower.workareaEnabledType.description = If the work area is enabled or disabled +channel-type.automower.workareaIdType.label = Work Area Id +channel-type.automower.workareaIdType.description = Id of the Work Area +channel-type.automower.workareaLastTimeCompletedType.label = Last Time Completed +channel-type.automower.workareaLastTimeCompletedType.description = Timestamp when the work area was last completed. EPOS mowers and systematic mowing work areas only. +channel-type.automower.workareaNameType.label = Work Area Name +channel-type.automower.workareaNameType.description = Name of the work area +channel-type.automower.workareaProgressType.label = Work Area Progress +channel-type.automower.workareaProgressType.description = The progress on a work area. EPOS mowers and systematic mowing work areas only. +channel-type.automower.zoneDirtyType.label = Stayout Zones Dirty Flag +channel-type.automower.zoneDirtyType.description = If the stay-out zones are synchronized with the Husqvarna cloud. If the map is dirty you can not enable or disable a stay-out zone. +channel-type.automower.zoneEnabledType.label = Stayout Zone Enabled +channel-type.automower.zoneEnabledType.description = If the Stayout zone is enabled, the Automower® will not access the zone +channel-type.automower.zoneIdType.label = Stayout Zone Id +channel-type.automower.zoneIdType.description = Id of the Stayout zone +channel-type.automower.zoneNameType.label = Stayout Zone Name +channel-type.automower.zoneNameType.description = The name of the Stayout zone + +# channel types + +channel-type.automower.calendarTasksDurationType.label = Duration +channel-type.automower.calendarTasksDurationType.description = Duration time +channel-type.automower.calendarTasksDurationType.state.pattern = %1$tHh:%1$tMm +channel-type.automower.calendarTasksFridayType.label = Enabled on Fridays +channel-type.automower.calendarTasksFridayType.description = Enabled on Fridays +channel-type.automower.calendarTasksMondayType.label = Enabled on Mondays +channel-type.automower.calendarTasksMondayType.description = Enabled on Mondays +channel-type.automower.calendarTasksSaturdayType.label = Enabled on Saturdays +channel-type.automower.calendarTasksSaturdayType.description = Enabled on Saturdays +channel-type.automower.calendarTasksStartType.label = Start Time +channel-type.automower.calendarTasksStartType.description = Start time relative to midnight +channel-type.automower.calendarTasksStartType.state.pattern = %1$tHh:%1$tMm +channel-type.automower.calendarTasksSundayType.label = Enabled on Sundays +channel-type.automower.calendarTasksSundayType.description = Enabled on Sundays +channel-type.automower.calendarTasksThursdayType.label = Enabled on Thursdays +channel-type.automower.calendarTasksThursdayType.description = Enabled on Thursdays +channel-type.automower.calendarTasksTuesdayType.label = Enabled on Tuesdays +channel-type.automower.calendarTasksTuesdayType.description = Enabled on Tuesdays +channel-type.automower.calendarTasksWednesdayType.label = Enabled on Wednesdays +channel-type.automower.calendarTasksWednesdayType.description = Enabled on Wednesdays +channel-type.automower.calendarTasksWorkAreaIdType.label = Work Area Id of Calendar +channel-type.automower.calendarTasksWorkAreaIdType.description = Work Area Id mapped to this calendar + +# thing types + +thing-type.automower.automower.channel.last-position.description = Last Position + +# channel types + +channel-type.automower.calendarTasksType.label = Planner Info JSON +channel-type.automower.calendarTasksType.description = The channel providing a JSON with the information about the planner tasks. # actions -action-input-duration-label = Duration +action-input-duration-label = duration action-input-duration-desc = The duration of the automower command in minutes action-park-label = park the automower action-park-desc = Parks the automower for a defined amount of time, overriding its schedule. @@ -108,6 +256,52 @@ action-resumeschedule-label = resume the schedule action-resumeschedule-desc = Resumes the schedule for the automower. action-start-label = start the automower action-start-desc = Starts the automower for a defined amount of time, overriding its schedule. +action-confirm-error-label = confirm error +action-confirm-error-desc = Confirm current non fatal error. +action-reset-cutting-blade-usage-time-label = reset cutting blade usage time +action-reset-cutting-blade-usage-time-desc = Reset the cutting blade usage time. +action-set-settings-label = update automower settings +action-set-settings-desc = Sends a set of Settings to the automower. +action-input-cutting-height-label = cutting height +action-input-cutting-height-desc = Prescaled cutting height, Range: 1-9. +action-input-headlight-mode-label = headlight mode +action-input-headlight-mode-desc = Headlight Mode (ALWAYS_ON, ALWAYS_OFF, EVENING_ONLY, EVENING_AND_NIGHT). +action-set-work-area-label = update work area configuration +action-set-work-area-desc = Update the work area configuration of the specified area. +action-input-workarea-id-label = work area id +action-input-workarea-id-desc = Id of WorkArea to be updated. +action-input-enable-label = enable +action-input-enable-desc = Enable or disable WorkArea. +action-input-cutting-height-label = cutting height +action-input-cutting-height-desc = Cutting height of the WorkArea. +action-set-stayoutzone-label = update stayout zone configuration +action-set-stayoutzone-desc = Update the stayout zone configuration of the specified zone. +action-input-zone-id-label = zone id +action-input-zone-id-desc = Id of the stayout zone to be updated. +action-input-enable-label = enable +action-input-enable-desc = Enable or disable stayout zone. +action-set-calendartask-label = update calendar task configuration +action-set-calendartask-desc = Update the calendar task configuration of the mower or specified work area. +action-input-workarea-id-label = work area id +action-input-workarea-id-desc = Id of WorkArea where the calendar task configuration shall be updated (null if mower doesn't support work areas). +action-input-start-label = start +action-input-start-desc = Start time relative to midnight (minutes). +action-input-duration-label = duration +action-input-duration-desc = Duration time (minutes). +action-input-monday-label = monday[] +action-input-monday-desc = Enabled on Mondays (array with the number of calendar tasks). +action-input-tuesday-label = tuesday[] +action-input-tuesday-desc = Enabled on Tuesdays (array with the number of calendar tasks). +action-input-wednesday-label = wednesday[] +action-input-wednesday-desc = Enabled on Wednesdays (array with the number of calendar tasks). +action-input-thursday-label = thursday[] +action-input-thursday-desc = Enabled on Thursdays (array with the number of calendar tasks). +action-input-friday-label = friday[] +action-input-friday-desc = Enabled on Fridays (array with the number of calendar tasks). +action-input-saturday-label = saturday +action-input-saturday-desc = Enabled on Saturdays (array with the number of calendar tasks). +action-input-sunday-label = sunday +action-input-sunday-desc = Enabled on Sunday (array with the number of calendar tasks). # status messages diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index b039eeea8edfb..afa3f9262542e 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -42,88 +42,28 @@ An automatic lawn mower - - - - - - - - - - - - - - - - - - - - - - - - Last Position - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + N/A N/A N/A N/A + N/A + N/A + N/A + N/A + N/A + 1 @@ -136,10 +76,367 @@ 600 How often the current automower state should be polled in seconds + + + Time zone of the mower (e.g. Europe/Berlin) + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + String @@ -170,7 +467,7 @@ - + @@ -181,6 +478,19 @@ + + String + + The current reason for being inactive + + + + + + + + + String @@ -193,13 +503,17 @@ - + + + + + - + @@ -207,6 +521,20 @@ + + Number + + Id of the active work area + + + + + String + + Name of the active work area + + + DateTime @@ -221,11 +549,16 @@ - + Number The error code at the time of last update - + + + + String + + The error message at the time of last update @@ -235,6 +568,13 @@ + + Switch + + If the mower has an Error Code this attribute states if the error is confirmable + + + DateTime @@ -250,17 +590,265 @@ - + String - - The channel providing a JSON with the information about the planner tasks. + + The channel providing a reason that restrics current planner operation. + + + + + + + + + + + + + + + + + Number + + The channel providing an external reason that restrics current planner operation. - + + Number + + Prescaled cutting height, range: 1-9 + + + + String + + Information about headlights + + + + Number:Time + + The time since the last reset of the cutting blade usage counter + + + + + Number:Time + + The time the mower has been disconnected from the cloud + + + + + Number + + Number of charging cycles + + + + + Number + + The total number of collisions + + + + + Number:Time + + Total charging time + + + + + Number:Time + + Total cutting time + + + + + Number:Dimensionless + + Total cutting time in percent + + + + + Number:Length + + Total driven distance + + + + + Number:Time + + The total running time (the wheel motors have been running) + + + + + Number:Time + + The total searching time + + + + + Number:Dimensionless + + The total searching time in percent + + + + + Number:Time + + The time the mower has been connected to the cloud + + + + + Location + + Last GPS position of the mower + + + + Location - The channel providing a waypoint of the mower's activity. + A waypoint of the mower's activity + + + + + + Number:Time + + Start time relative to midnight + + + + Number:Time + + Duration time + + + + Switch + + Enabled on Mondays + + + Switch + + Enabled on Tuesdays + + + Switch + + Enabled on Wednesdays + + + Switch + + Enabled on Thursdays + + + Switch + + Enabled on Fridays + + + Switch + + Enabled on Saturdays + + + Switch + + Enabled on Sundays + + + Number + + Work Area Id mapped to this calendar + + + + + + Switch + + If the stay-out zones are synchronized with the Husqvarna cloud. If the map is dirty you can not enable + or disable a stay-out zone. + + + + + String + + Id of the Stayout zone + + + + + String + + The name of the Stayout zone + + + + + Switch + + If the Stayout zone is enabled, the Automower® will not access the zone + + + + + Number + + Id of the Work Area + + + + + String + + Name of the work area + + + + + Number:Dimensionless + + Cutting height of the work area, range: 0-100 + + + + + Switch + + If the work area is enabled or disabled + + + + Number:Dimensionless + + The progress on a work area. EPOS mowers and systematic mowing work areas only. + + + + + DateTime + + Timestamp when the work area was last completed. EPOS mowers and systematic mowing work areas only. @@ -301,5 +889,4 @@ Park and pause the mower schedule until manual resume - diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/update/update.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/update/update.xml new file mode 100644 index 0000000000000..606801abaa6ea --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/update/update.xml @@ -0,0 +1,973 @@ + + + + + + + + automower:nameType + + + automower:modeType + + + automower:activityType + + + automower:inactiveReasonType + + + automower:stateType + + + automower:workAreaIdType + + + automower:workAreaType + + + automower:lastUpdateType + + + system:battery-level + + + automower:errorCodeType + + + automower:errorMessageType + + + automower:errorTimestampType + + + automower:errorConfirmableType + + + automower:plannerNextStartTimestampType + + + automower:plannerOverrideActionType + + + automower:plannerRestrictedReasonType + + + automower:plannerExternalReasonType + + + + + automower:settingCuttingHeightType + + + automower:settingHeadlightModeType + + + + + automower:statCuttingBladeUsageTimeType + + + automower:statDownTimeType + + + automower:statNumberOfChargingCyclesType + + + automower:statNumberOfCollisionsType + + + automower:statTotalChargingTimeType + + + automower:statTotalCuttingTimeType + + + automower:statTotalCuttingPercentType + + + automower:statTotalDriveDistanceType + + + automower:statTotalRunningTimeType + + + automower:statTotalSearchingTimeType + + + automower:statTotalSearchingPercentType + + + automower:statUpTimeType + + + + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + automower:lastPositionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + + + automower:zoneDirtyType + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + + automower:start + + + automower:resumeSchedule + + + automower:pause + + + automower:park + + + automower:parkUntilNextSchedule + + + automower:parkUntilFurtherNotice + + + +