Skip to content

Commit

Permalink
[automower] Add planner, calendar and command channels (openhab#8802)
Browse files Browse the repository at this point in the history
* [Automower] Enhanced binding:
	- Added support for the planner and calendar data
	- Added command channels
	- Updated docs

Signed-off-by: Marcin Czeczko <marcin.czeczko@gmail.com>

* [Automower] Fixed consts with channel ids after removal of channel
groups. Improved the mower state update:
- Cache the last read state from API
- Use cached mower state so the items linked will always be up to date
  without the need to wait for API refresh period.
- Use timeZoneProvider to user user set timezone.

Signed-off-by: Marcin Czeczko <marcin.czeczko@gmail.com>

* Rolledback NotNullByDefault annotation

Signed-off-by: Marcin Czeczko <marcin.czeczko@gmail.com>
  • Loading branch information
marcinczeczko authored and thinkingstone committed Nov 7, 2021
1 parent 998d323 commit 957319d
Show file tree
Hide file tree
Showing 16 changed files with 454 additions and 104 deletions.
80 changes: 47 additions & 33 deletions bundles/org.openhab.binding.automower/README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
# Automower Binding

This binding communicates to the Husqvarna Automower Connect API in order to send commands and query the state of Husqvarna Automower robots.
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.

## 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

Basically all Husqvarna Automower models with "Automower Connect" support should be supported. It was tested only with a Husqvarna Automower 450X
All Husqvarna Automower models with "Automower Connect" should be supported. It was tested only with a Husqvarna Automower 430X and 450X.


## Discovery

Once the bridge is created and configured, registered automowers will be discovered automatically

Once the bridge is created and configured, OpenHab will automatically discover all Automowers registered on your account.

## Thing Configuration

`bridge:`

- appKey (mandatory): The Application Key is required to communication with the Automower Connect Api. It can be obtained by registering an Application on the Husqvarna Website. This application also needs to be connected to the "Authentication API" and the "Automower Connect API"
- 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)
- userName (mandatory): The user name for which the application key has been issued
- password (mandatory): The password for the given user
- pollingInterval (optional): How often the bridge state should be queried in seconds. Default is 1h (3600s)
Expand All @@ -34,23 +34,31 @@ With the default value of 1h this would mean ~720 requests per month for the bri
- 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 not 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 automower
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

## Channels


| channel | type | description |
|-----------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| name | String | (readonly) The name of the Automower |
| mode | String | (readonly) The current mode (MAIN_AREA, SECONDARY_AREA, HOME, DEMO, UNKNOWN) |
| activity | String | (readonly) The current activity (UNKNOWN, NOT_APPLICABLE, MOWING, GOING_HOME, CHARGING, LEAVING, PARKED_IN_CS, STOPPED_IN_GARDEN) |
| state | String | (readonly) The current state (UNKNOWN, NOT_APPLICABLE, PAUSED, IN_OPERATION, WAIT_UPDATING, WAIT_POWER_UP, RESTRICTED, OFF, STOPPED, ERROR, FATAL_ERROR, ERROR_AT_POWER_UP) |
| last-update | DateTime | (readonly) The time when the automower updated its states |
| battery | Number | (readonly) The battery state of charge in percent |
| error-code | Number | (readonly) The current error code |
| error-timestamp | DateTime | (readonly) The timestamp when the current error occurred |
| channel | type | access mode | description |
|-------------------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| mower-status#mode | String | R | The current mode (MAIN_AREA, SECONDARY_AREA, HOME, DEMO, UNKNOWN) |
| mower-status#activity | String | R | The current activity (UNKNOWN, NOT_APPLICABLE, MOWING, GOING_HOME, CHARGING, LEAVING, PARKED_IN_CS, STOPPED_IN_GARDEN) |
| mower-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, OFF, STOPPED, ERROR, FATAL_ERROR, ERROR_AT_POWER_UP) |
| mower-status#last-update | DateTime | R | The time when the automower updated its states |
| mower-status#battery | Number | R | The battery state of charge in percent |
| mower-status#error-code | Number | R | The current error code |
| mower-status#error-timestamp | DateTime | R | The timestamp when the current error occurred |
| mower-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. |
| mower-status#planner-override-action | String | R | The action that overrides current planner operation. |
| mower-status#calendar-tasks | String | R | The JSON with the information about Automower planner. |
| mower#start | Number | W | Starts the automower for a duration |
| mower#resume_schedule | Switch | W | Resumes the Automower schedule |
| mower#pause | Switch | W | Pause the Automower |
| mower#park | Number | W | Park the Automower for a duration |
| mower#park_until_next_schedule | Switch | W | Park the Automower until next schedule |
| mower#park_until_further_notice | Switch | W | Park the Automower until further notice. |


## Actions
Expand Down Expand Up @@ -79,19 +87,23 @@ The following actions are available for `automower`things:

### automower.items

String Automower_Name "Name" { channel="automower:automower:mybridge:myAutomower:name" }
String Automower_Mode "Mode" { channel="automower:automower:mybridge:myAutomower:mode" }
String Automower_Activity "Activity" { channel="automower:automower:mybridge:myAutomower:activity" }
String Automower_State "State" { channel="automower:automower:mybridge:myAutomower:state" }
DateTime Automower_Last_Update "Last Update" { channel="automower:automower:mybridge:myAutomower:last-update" }
Number Automower_Battery "Battery" { channel="automower:automower:mybridge:myAutomower:battery" }
Number Automower_Error_Code "Error Code" { channel="automower:automower:mybridge:myAutomower:error-code" }
DateTime Automower_Error_Time "Error Time" { channel="automower:automower:mybridge:myAutomower:error-timestamp" }


String Automower_Command "Command" { channel="automower:automower:mybridge:myAutomower:command" }
Number Automower_Command_Duration "Command Duration" { channel="automower:automower:mybridge:myAutomower:command-duration" }
String Automower_Command_Response "Command Response" { channel="automower:automower:mybridge:myAutomower:command-response" }
String Automower_Mode "Mode [%s]" { channel="automower:automower:mybridge:myAutomower:mower-status#mode" }
String Automower_Activity "Activity [%s]" { channel="automower:automower:mybridge:myAutomower:mower-status#activity" }
String Automower_State "State [%s]" { channel="automower:automower:mybridge:myAutomower:mower-status#state" }
DateTime Automower_Last_Update "Last Update" { channel="automower:automower:mybridge:myAutomower:mower-status#last-update" }
Number Automower_Battery "Battery [%d %%]" { channel="automower:automower:mybridge:myAutomower:mower-status#battery" }
Number Automower_Error_Code "Error Code [%d]" { channel="automower:automower:mybridge:myAutomower:mower-status#error-code" }
DateTime Automower_Error_Time "Error Time" { channel="automower:automower:mybridge:myAutomower:mower-status#error-timestamp" }
String Automower_Override_Action "Override Action [%s]" { channel="automower:automower:mybridge:myAutomower:mower-status#planner-override-action" }
DateTime Automower_Next_Start_Time "Next Start Time" { channel="automower:automower:mybridge:myAutomower:mower-status#planner-next-start" }
String Automower_Calendar_Tasks "Planned Tasks [%s]" { channel="automower:automower:mybridge:myAutomower:mower-status#calendar-tasks" }

Number Automower_Command_Start "Start mowing for duration [%d min]" { channel="automower:automower:mybridge:myAutomower:mower#start" }
Switch Automower_Command_Resume "Resume the schedule" { channel="automower:automower:mybridge:myAutomower:mower#resume_schedule" }
Switch Automower_Command_Pause "Pause the automower" { channel="automower:automower:mybridge:myAutomower:mower#pause" }
Number Automower_Command_Park "Park for duration [%d min]" { channel="automower:automower:mybridge:myAutomower:mower#park" }
Switch Automower_Command_Park_Next_Schedule "Park until next schedule" { channel="automower:automower:mybridge:myAutomower:mower#park_until_next_schedule" }
Switch Automower_Command_Park_Notice "Park until further notice" { channel="automower:automower:mybridge:myAutomower:mower#park_until_further_notice" }

### automower.sitemap

Expand All @@ -100,14 +112,16 @@ The following actions are available for `automower`things:
sitemap demo label="Automower"
{
Frame {
Text item=Automower_Name
Text item=Automower_Mode
Text item=Automower_Activity
Text item=Automower_State
Text item=Automower_Last_Update
Text item=Automower_Battery
Text item=Automower_Error_Code
Text item=Automower_Error_Time
Text item=Automower_Override_Action
Text item=Automower_Next_Start_Time
Text item=Automower_Calendar_Tasks
}
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
* used across the whole binding.
*
* @author Markus Pfleger - Initial contribution
* @author Marcin Czeczko - Added support for planner & calendar data
*/
@NonNullByDefault
public class AutomowerBindingConstants {

private static final String BINDING_ID = "automower";

public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge");
Expand All @@ -32,14 +32,25 @@ public class AutomowerBindingConstants {
public static final ThingTypeUID THING_TYPE_AUTOMOWER = new ThingTypeUID(BINDING_ID, "automower");

// List of all Channel ids
public static final String CHANNEL_MOWER_NAME = "name";
public static final String CHANNEL_STATUS_NAME = "name";
public static final String CHANNEL_STATUS_MODE = "mode";
public static final String CHANNEL_STATUS_ACTIVITY = "activity";
public static final String CHANNEL_STATUS_STATE = "state";
public static final String CHANNEL_STATUS_LAST_UPDATE = "last-update";
public static final String CHANNEL_STATUS_BATTERY = "battery";
public static final String CHANNEL_STATUS_ERROR_CODE = "error-code";
public static final String CHANNEL_STATUS_ERROR_TIMESTAMP = "error-timestamp";
public static final String CHANNEL_PLANNER_NEXT_START = "planner-next-start";
public static final String CHANNEL_PLANNER_OVERRIDE_ACTION = "planner-override-action";
public static final String CHANNEL_CALENDAR_TASKS = "calendar-tasks";

// Command channels
public static final String CHANNEL_COMMAND_START = "start";
public static final String CHANNEL_COMMAND_RESUME_SCHEDULE = "resume_schedule";
public static final String CHANNEL_COMMAND_PAUSE = "pause";
public static final String CHANNEL_COMMAND_PARK = "park";
public static final String CHANNEL_COMMAND_PARK_UNTIL_NEXT_SCHEDULE = "park_until_next_schedule";
public static final String CHANNEL_COMMAND_PARK_UNTIL_NOTICE = "park_until_further_notice";

// Automower properties
public static final String AUTOMOWER_ID = "mowerId";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.openhab.binding.automower.internal.things.AutomowerHandler;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
Expand Down Expand Up @@ -55,12 +56,14 @@ public class AutomowerHandlerFactory extends BaseThingHandlerFactory {
private final OAuthFactory oAuthFactory;
protected final @NonNullByDefault({}) HttpClient httpClient;
private @Nullable ServiceRegistration<?> automowerDiscoveryServiceRegistration;
private final TimeZoneProvider timeZoneProvider;

@Activate
public AutomowerHandlerFactory(@Reference OAuthFactory oAuthFactory,
@Reference HttpClientFactory httpClientFactory) {
public AutomowerHandlerFactory(@Reference OAuthFactory oAuthFactory, @Reference HttpClientFactory httpClientFactory,
@Reference TimeZoneProvider timeZoneProvider) {
this.oAuthFactory = oAuthFactory;
this.httpClient = httpClientFactory.getCommonHttpClient();
this.timeZoneProvider = timeZoneProvider;
}

@Override
Expand All @@ -77,7 +80,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
}

if (AutomowerHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
return new AutomowerHandler(thing);
return new AutomowerHandler(thing, timeZoneProvider);
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
*/
@NonNullByDefault
public class AutomowerBridge {

private final OAuthClientService authService;
private final String appKey;
private final String userName;
Expand Down Expand Up @@ -96,7 +95,6 @@ public Mower getAutomowerStatus(String id) throws AutomowerCommunicationExceptio
*/
public void sendAutomowerCommand(String id, AutomowerCommand command, long commandDuration)
throws AutomowerCommunicationException {

MowerCommandAttributes attributes = new MowerCommandAttributes();
attributes.setDuration(commandDuration);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
*/
@NonNullByDefault
public abstract class HusqvarnaApi {

private final HttpClient httpClient;
protected final Gson gson;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
*/
@NonNullByDefault
public class AutomowerConnectApi extends HusqvarnaApi {

public AutomowerConnectApi(HttpClient httpClient) {
super(httpClient);
}
Expand Down Expand Up @@ -81,7 +80,6 @@ public void sendCommand(String appKey, String token, String id, MowerCommandRequ

private ContentResponse executeRequest(String appKey, String token, final Request request)
throws AutomowerCommunicationException {

request.timeout(10, TimeUnit.SECONDS);

request.header("Authorization-Provider", "husqvarna");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,17 @@
*/
package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto;

import java.util.ArrayList;
import java.util.List;

/**
* @author Markus Pfleger - Initial contribution
* @author Marcin Czeczko - Added support for planner & calendar data
*/
public class Calendar {
private List<CalendarTask> tasks = new ArrayList<>();

public List<CalendarTask> getTasks() {
return tasks;
}
}
Loading

0 comments on commit 957319d

Please sign in to comment.