[Worxlandroid] Initial contribution (#16893)
* Worxlandroid new binding Signed-off-by: Gael L'hopital <gael@lhopital.org> Co-authored-by: Leo Siepel <leosiepel@gmail.com>
@@ -454,6 +454,7 @@
|
||||
/bundles/org.openhab.binding.wlanthermo/ @CSchlipp
|
||||
/bundles/org.openhab.binding.wled/ @Skinah
|
||||
/bundles/org.openhab.binding.wolfsmartset/ @BoBiene
|
||||
/bundles/org.openhab.binding.worxlandroid/ @clinique
|
||||
/bundles/org.openhab.binding.wundergroundupdatereceiver/ @danieldemus
|
||||
/bundles/org.openhab.binding.x/ @computergeek1507
|
||||
/bundles/org.openhab.binding.xmltv/ @clinique
|
||||
|
||||
@@ -2251,6 +2251,11 @@
|
||||
<artifactId>org.openhab.binding.wolfsmartset</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.worxlandroid</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.wundergroundupdatereceiver</artifactId>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
||||
@@ -0,0 +1,539 @@
|
||||
|
||||
# WorxLandroid Binding
|
||||
|
||||
This is the binding for Worx Landroid robotic lawn mowers.
|
||||
It connects openHAB with your WorxLandroid mower using the API and MQTT.
|
||||
This binding allows you to integrate, view and control supported Worx lawn mowers with openHAB.
|
||||
|
||||
## Supported Things
|
||||
|
||||
Currently following Things are supported:
|
||||
|
||||
- `bridge`: **Bridge Worx Landroid API** Thing representing the handler for Worx API
|
||||
- `mower`: One or many Things for supported **Landroid Mower**'s
|
||||
|
||||
## Discovery
|
||||
|
||||
A Bridge is required to connect to the Worx API.
|
||||
Here you can provide your credentials for your WorxLandroid account.
|
||||
Once the Bridge has been added Worx Landroid mowers will be discovered automatically.
|
||||
|
||||
## Things Configuration
|
||||
|
||||
The following options can be set for the `bridge`:
|
||||
|
||||
| Property | Description |
|
||||
|----------|------------------------------------------|
|
||||
| username | Username to access the WorxLandroid API. |
|
||||
| password | Password to access the WorxLandroid API. |
|
||||
|
||||
The following options can be set for the `mower`:
|
||||
|
||||
| Property | Description | Default | Advanced |
|
||||
|-----------------------|----------------------------------------------------------------------------------------------|---------|----------|
|
||||
| serialNumber | Serial number of the mower | - | No |
|
||||
| refreshStatusInterval | Interval for refreshing mower status (ONLINE/OFFLINE) and channel 'common#online' in seconds | 3600 | Yes |
|
||||
| pollingInterval | Interval for polling in seconds (min="30" max="7200"). | 1200 | Yes |
|
||||
|
||||
Default values for `refreshStatusInterval` and `pollingInterval` are the recommended settings in order to prevent a 24h ban from Worx.
|
||||
Lower polling and refresh values will likely result in a 24h ban for your account.
|
||||
|
||||
## Channels
|
||||
|
||||
Currently following **Channels** are supported on the **Landroid Mower**:
|
||||
|
||||
### Common
|
||||
|
||||
| Channel | Type | ChannelName | Values |
|
||||
|------------------|----------|-------------------------|-------------------|
|
||||
| status | String | common#status | *1 (see below) |
|
||||
| error | String | common#error | *2 (see below) |
|
||||
| online | Switch | common#online | |
|
||||
| online-timestamp | DateTime | common#online-timestamp | |
|
||||
| action | String | common#action | START, STOP, HOME |
|
||||
| enable | Switch | common#enable | |
|
||||
| lock | Switch | common#lock | |
|
||||
|
||||
*1: Values for **error** Channel:
|
||||
|
||||
UNKNOWN, NO_ERR, TRAPPED, LIFTED, WIRE_MISSING, OUTSIDE_WIRE, RAINING, CLOSE_DOOR_TO_MOW, CLOSE_DOOR_TO_GO_HOME, BLADE_MOTOR_BLOCKED, WHEEL_MOTOR_BLOCKED, TRAPPED_TIMEOUT, UPSIDE_DOWN, BATTERY_LOW, REVERSE_WIRE, CHARGE_ERROR, TIMEOUT_FINDING_HOME, MOWER_LOCKED, BATTERY_OVER_TEMPERATURE, MOWER_OUTSIDE_WIRE
|
||||
|
||||
*2: Values for **status** Channel:
|
||||
|
||||
UNKNOWN, IDLE, HOME, START_SEQUENCE, LEAVING_HOME, FOLLOW_WIRE, SEARCHING_HOME, SEARCHING_WIRE, MOWING, LIFTED, TRAPPED, BLADE_BLOCKED, DEBUG, REMOTE_CONTROL, GOING_HOME, ZONE_TRAINING, BORDER_CUT, SEARCHING_ZONE, PAUSE, MANUAL_STOP
|
||||
|
||||
### Config
|
||||
|
||||
| Channel | Type | ChannelName |
|
||||
|-----------|----------|------------------|
|
||||
| timestamp | DateTime | config#timestamp |
|
||||
| command | Number | config#command |
|
||||
|
||||
### Multi-Zones
|
||||
|
||||
If Multi Zones are supported, you are able to define 4 separate zones and split working times by 10 to those.
|
||||
|
||||
To ease zone configuration, you are able to set distance in meters where a specific zone starts. Bearing in mind that you roughly shall know how many meters of cable have been used (without buffer).
|
||||
|
||||
As second step you are able to set time in percent and split in parts of 10 between allocation zones.
|
||||
|
||||
| Channel | Type | ChannelName |
|
||||
|--------------|---------------|--------------------------|
|
||||
| enable | Switch | multi-zones#enable |
|
||||
| last-zone | Number | multi-zones#last-zone |
|
||||
| zone-1 | Number:Length | multi-zones#zone-1 |
|
||||
| zone-2 | Number:Length | multi-zones#zone-2 |
|
||||
| zone-3 | Number:Length | multi-zones#zone-3 |
|
||||
| zone-4 | Number:Length | multi-zones#zone-4 |
|
||||
| allocation-0 | Number | multi-zones#allocation-0 |
|
||||
| allocation-1 | Number | multi-zones#allocation-1 |
|
||||
| allocation-2 | Number | multi-zones#allocation-2 |
|
||||
| allocation-3 | Number | multi-zones#allocation-3 |
|
||||
| allocation-4 | Number | multi-zones#allocation-4 |
|
||||
| allocation-5 | Number | multi-zones#allocation-5 |
|
||||
| allocation-6 | Number | multi-zones#allocation-6 |
|
||||
| allocation-7 | Number | multi-zones#allocation-7 |
|
||||
| allocation-8 | Number | multi-zones#allocation-8 |
|
||||
| allocation-9 | Number | multi-zones#allocation-9 |
|
||||
|
||||
### Schedule
|
||||
|
||||
| Channel | Type | ChannelName | |
|
||||
|----------------|----------|-------------------------|-------------------|
|
||||
| mode | String | schedule#mode | ONLY IF SUPPORTED |
|
||||
| time-extension | Number | schedule#time-extension | |
|
||||
| next-start | DateTime | schedule#next-start | |
|
||||
| next-stop | DateTime | schedule#next-stop | |
|
||||
|
||||
### Aws
|
||||
|
||||
| Channel | Type | ChannelName |
|
||||
|-----------|--------|---------------|
|
||||
| poll | Switch | aws#poll |
|
||||
| connected | Switch | aws#connected |
|
||||
|
||||
### Sunday (Slot 1)
|
||||
|
||||
| Channel | Type | ChannelName |
|
||||
|----------|-------------|-----------------|
|
||||
| enable | Switch | sunday#enable |
|
||||
| time | DateTime | sunday#time |
|
||||
| duration | Number:Time | sunday#duration |
|
||||
| edgecut | Switch | sunday#edgecut |
|
||||
|
||||
### Sunday2 (Slot 2, ONLY IF SUPPORTED)
|
||||
|
||||
| Channel | Type | ChannelName |
|
||||
|----------|-------------|------------------|
|
||||
| enable | Switch | sunday2#enable |
|
||||
| time | DateTime | sunday2#time |
|
||||
| duration | Number:Time | sunday2#duration |
|
||||
| edgecut | Switch | sunday2#edgecut |
|
||||
|
||||
And so on for each day of the week along with the Slot 2 when supported.
|
||||
|
||||
### One-Time
|
||||
|
||||
| Channel | Type | ChannelName |
|
||||
|----------|--------|-------------------|
|
||||
| edgecut | Switch | one-time#edgecut |
|
||||
| duration | Switch | one-time#duration |
|
||||
|
||||
### Battery
|
||||
|
||||
| Channel | Type | ChannelName |
|
||||
|---------------------|--------------------------|-----------------------------|
|
||||
| temperature | Number:Temperature | battery#temperature |
|
||||
| voltage | Number:ElectricPotential | battery#voltage |
|
||||
| level | Number | battery#level |
|
||||
| charge-cycles | Number | battery#charge-cycles |
|
||||
| charge-cycles-total | Number | battery#charge-cycles-total |
|
||||
| charging | Switch | battery#charging |
|
||||
|
||||
### Orientation
|
||||
|
||||
| Channel | Type | ChannelName |
|
||||
|---------|--------------|-------------------|
|
||||
| pitch | Number:Angle | orientation#pitch |
|
||||
| roll | Number:Angle | orientation#roll |
|
||||
| yaw | Number:Angle | orientation#yaw |
|
||||
|
||||
### Metrics
|
||||
|
||||
| Channel | Type | ChannelName |
|
||||
|------------------|---------------|--------------------------|
|
||||
| blade-time | Number:Time | metrics#blade-time |
|
||||
| blade-time-total | Number:Time | metrics#blade-time-total |
|
||||
| distance | Number:Length | metrics#distance |
|
||||
| total-time | Number:Time | metrics#total-time |
|
||||
|
||||
### Rain (if supported)
|
||||
|
||||
| Channel | Type | ChannelName |
|
||||
|---------|-------------|--------------|
|
||||
| state | Switch | rain#state |
|
||||
| counter | Number:Time | rain#counter |
|
||||
| delay | Number:Time | rain#delay |
|
||||
|
||||
### Wifi
|
||||
|
||||
| Channel | Type | ChannelName |
|
||||
|--------------|--------------|-------------------|
|
||||
| rssi | Number:Power | wifi#rssi |
|
||||
| wifi-quality | Number | wifi#wifi-quality |
|
||||
|
||||
## Provided icon set
|
||||
|
||||
This binding has its own IconProvider and makes available the following list of icons
|
||||
|
||||
| Icon Name | Dynamic | Illustration |
|
||||
|---------------------------|---------|-----------------------------------------|
|
||||
| oh:worxlandroid:charging | Yes |  |
|
||||
| oh:worxlandroid:counter | No |  |
|
||||
| oh:worxlandroid:distance | Yes |  |
|
||||
| oh:worxlandroid:lawnmower | No |  |
|
||||
| oh:worxlandroid:lock | Yes |  |
|
||||
| oh:worxlandroid:rain | Yes |  |
|
||||
| oh:worxlandroid:refresh | No |  |
|
||||
| oh:worxlandroid:zones | Yes |  |
|
||||
|
||||
## Examples
|
||||
|
||||
### $OPENHAB_CONF/items/landroid.things
|
||||
|
||||
```java
|
||||
Bridge worxlandroid:bridge:api "Worx Api" [ username="xxxxYYYxxxx", password="dldkssdjldj" ] {
|
||||
Thing mower lanmower "Worx M600" [ serialNumber="sdmldksmdskmlsd" ]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### $OPENHAB_CONF/items/landroid.items
|
||||
|
||||
```java
|
||||
String MyMower "MyMower [%s]"
|
||||
String LandroidMowerCommonStatus "Status code" {channel="worxlandroid:mower:MyWorxBridge:mymower:common#status"}
|
||||
String LandroidMowerCommonError "Error code" {channel="worxlandroid:mower:MyWorxBridge:mymower:common#error"}
|
||||
Switch LandroidMowerCommonOnline "Online" {channel="worxlandroid:mower:MyWorxBridge:mymower:common#online"}
|
||||
DateTime LandroidMowerCommonOnlineTimestamp "Online status timestamp" {channel="worxlandroid:mower:MyWorxBridge:mymower:common#online-timestamp"}
|
||||
String LandroidMowerCommonAction "Action" {channel="worxlandroid:mower:MyWorxBridge:mymower:common#action"}
|
||||
Switch LandroidMowerCommonEnable "Mowing enabled" {channel="worxlandroid:mower:MyWorxBridge:mymower:common#enable"}
|
||||
Switch LandroidMowerCommonLock "Lock mower wifi" {channel="worxlandroid:mower:MyWorxBridge:mymower:common#lock"}
|
||||
DateTime LandroidMowerConfigTimestamp "Last update" {channel="worxlandroid:mower:MyWorxBridge:mymower:config#timestamp"}
|
||||
Number LandroidMowerConfigCommand "Command" {channel="worxlandroid:mower:MyWorxBridge:mymower:config#command"}
|
||||
Switch LandroidMowerMultiZonesEnable "Multizone enabled" {channel="worxlandroid:mower:MyWorxBridge:mymower:multi-zones#enable"}
|
||||
Number LandroidMowerMultiZonesLastZone "Last zone" {channel="worxlandroid:mower:MyWorxBridge:mymower:multi-zones#last-zone"}
|
||||
Number:Length LandroidMowerMultiZonesZone1 "Meters zone 1" {channel="worxlandroid:mower:MyWorxBridge:mymower:multi-zones#zone-1"}
|
||||
Number:Length LandroidMowerMultiZonesZone2 "Meters zone 2" {channel="worxlandroid:mower:MyWorxBridge:mymower:multi-zones#zone-2"}
|
||||
Number:Length LandroidMowerMultiZonesZone3 "Meters zone 3" {channel="worxlandroid:mower:MyWorxBridge:mymower:multi-zones#zone-3"}
|
||||
Number:Length LandroidMowerMultiZonesZone4 "Meters zone 4" {channel="worxlandroid:mower:MyWorxBridge:mymower:multi-zones#zone-4"}
|
||||
Number LandroidMowerMultiZonesAllocation0 "Zone allocation 1" {channel="worxlandroid:mower:MyWorxBridge:mymower:multi-zones#allocation-0"}
|
||||
Number LandroidMowerMultiZonesAllocation1 "Zone allocation 2" {channel="worxlandroid:mower:MyWorxBridge:mymower:multi-zones#allocation-1"}
|
||||
Number LandroidMowerMultiZonesAllocation2 "Zone allocation 3" {channel="worxlandroid:mower:MyWorxBridge:mymower:multi-zones#allocation-2"}
|
||||
Number LandroidMowerMultiZonesAllocation3 "Zone allocation 4" {channel="worxlandroid:mower:MyWorxBridge:mymower:multi-zones#allocation-3"}
|
||||
Number LandroidMowerMultiZonesAllocation4 "Zone allocation 5" {channel="worxlandroid:mower:MyWorxBridge:mymower:multi-zones#allocation-4"}
|
||||
Number LandroidMowerMultiZonesAllocation5 "Zone allocation 6" {channel="worxlandroid:mower:MyWorxBridge:mymower:multi-zones#allocation-5"}
|
||||
Number LandroidMowerMultiZonesAllocation6 "Zone allocation 7" {channel="worxlandroid:mower:MyWorxBridge:mymower:multi-zones#allocation-6"}
|
||||
Number LandroidMowerMultiZonesAllocation7 "Zone allocation 8" {channel="worxlandroid:mower:MyWorxBridge:mymower:multi-zones#allocation-7"}
|
||||
Number LandroidMowerMultiZonesAllocation8 "Zone allocation 9" {channel="worxlandroid:mower:MyWorxBridge:mymower:multi-zones#allocation-8"}
|
||||
Number LandroidMowerMultiZonesAllocation9 "Zone allocation 10" {channel="worxlandroid:mower:MyWorxBridge:mymower:multi-zones#allocation-9"}
|
||||
String LandroidMowerScheduleMode "Schedule mode" {channel="worxlandroid:mower:MyWorxBridge:mymower:schedule#mode"}
|
||||
Number:Dimensionless LandroidMowerScheduleTimeExtension "Schedule time extension" {channel="worxlandroid:mower:MyWorxBridge:mymower:schedule#time-extension"}
|
||||
DateTime LandroidMowerScheduleNextStart "Next start" {channel="worxlandroid:mower:MyWorxBridge:mymower:schedule#next-start"}
|
||||
DateTime LandroidMowerScheduleNextStop "Next stop" {channel="worxlandroid:mower:MyWorxBridge:mymower:schedule#next-stop"}
|
||||
Switch LandroidMowerAwsPoll "Poll AWS" {channel="worxlandroid:mower:MyWorxBridge:mymower:aws#poll"}
|
||||
Switch LandroidMowerAwsConnected "AWS connected" {channel="worxlandroid:mower:MyWorxBridge:mymower:aws#connected"}
|
||||
Switch LandroidMowerSundayEnable "Active" {channel="worxlandroid:mower:MyWorxBridge:mymower:sunday#enable"}
|
||||
DateTime LandroidMowerSundayTime "Start time" {channel="worxlandroid:mower:MyWorxBridge:mymower:sunday#time"}
|
||||
Number:Time LandroidMowerSundayDuration "Duration" {channel="worxlandroid:mower:MyWorxBridge:mymower:sunday#duration", unit="min"}
|
||||
Switch LandroidMowerSundayEdgecut "Edgecut" {channel="worxlandroid:mower:MyWorxBridge:mymower:sunday#edgecut"}
|
||||
Switch LandroidMowerSunday2Enable "Active" {channel="worxlandroid:mower:MyWorxBridge:mymower:sunday2#enable"}
|
||||
DateTime LandroidMowerSunday2Time "Start time" {channel="worxlandroid:mower:MyWorxBridge:mymower:sunday2#time"}
|
||||
Number:Time LandroidMowerSunday2Duration "Duration" {channel="worxlandroid:mower:MyWorxBridge:mymower:sunday2#duration", unit="min"}
|
||||
Switch LandroidMowerSunday2Edgecut "Edgecut" {channel="worxlandroid:mower:MyWorxBridge:mymower:sunday2#edgecut"}
|
||||
Switch LandroidMowerMondayEnable "Active" {channel="worxlandroid:mower:MyWorxBridge:mymower:monday#enable"}
|
||||
DateTime LandroidMowerMondayTime "Start time" {channel="worxlandroid:mower:MyWorxBridge:mymower:monday#time"}
|
||||
Number:Time LandroidMowerMondayDuration "Duration" {channel="worxlandroid:mower:MyWorxBridge:mymower:monday#duration", unit="min"}
|
||||
Switch LandroidMowerMondayEdgecut "Edgecut" {channel="worxlandroid:mower:MyWorxBridge:mymower:monday#edgecut"}
|
||||
Switch LandroidMowerMonday2Enable "Active" {channel="worxlandroid:mower:MyWorxBridge:mymower:monday2#enable"}
|
||||
DateTime LandroidMowerMonday2Time "Start time" {channel="worxlandroid:mower:MyWorxBridge:mymower:monday2#time"}
|
||||
Number:Time LandroidMowerMonday2Duration "Duration" {channel="worxlandroid:mower:MyWorxBridge:mymower:monday2#duration", unit="min"}
|
||||
Switch LandroidMowerMonday2Edgecut "Edgecut" {channel="worxlandroid:mower:MyWorxBridge:mymower:monday2#edgecut"}
|
||||
Switch LandroidMowerTuesdayEnable "Active" {channel="worxlandroid:mower:MyWorxBridge:mymower:tuesday#enable"}
|
||||
DateTime LandroidMowerTuesdayTime "Start time" {channel="worxlandroid:mower:MyWorxBridge:mymower:tuesday#time"}
|
||||
Number:Time LandroidMowerTuesdayDuration "Duration" {channel="worxlandroid:mower:MyWorxBridge:mymower:tuesday#duration", unit="min"}
|
||||
Switch LandroidMowerTuesdayEdgecut "Edgecut" {channel="worxlandroid:mower:MyWorxBridge:mymower:tuesday#edgecut"}
|
||||
Switch LandroidMowerTuesday2Enable "Active" {channel="worxlandroid:mower:MyWorxBridge:mymower:tuesday2#enable"}
|
||||
DateTime LandroidMowerTuesday2Time "Start time" {channel="worxlandroid:mower:MyWorxBridge:mymower:tuesday2#time"}
|
||||
Number:Time LandroidMowerTuesday2Duration "Duration" {channel="worxlandroid:mower:MyWorxBridge:mymower:tuesday2#duration", unit="min"}
|
||||
Switch LandroidMowerTuesday2Edgecut "Edgecut" {channel="worxlandroid:mower:MyWorxBridge:mymower:tuesday2#edgecut"}
|
||||
Switch LandroidMowerWednesdayEnable "Active" {channel="worxlandroid:mower:MyWorxBridge:mymower:wednesday#enable"}
|
||||
DateTime LandroidMowerWednesdayTime "Start time" {channel="worxlandroid:mower:MyWorxBridge:mymower:wednesday#time"}
|
||||
Number:Time LandroidMowerWednesdayDuration "Duration" {channel="worxlandroid:mower:MyWorxBridge:mymower:wednesday#duration", unit="min"}
|
||||
Switch LandroidMowerWednesdayEdgecut "Edgecut" {channel="worxlandroid:mower:MyWorxBridge:mymower:wednesday#edgecut"}
|
||||
Switch LandroidMowerWednesday2Enable "Active" {channel="worxlandroid:mower:MyWorxBridge:mymower:wednesday2#enable"}
|
||||
DateTime LandroidMowerWednesday2Time "Start time" {channel="worxlandroid:mower:MyWorxBridge:mymower:wednesday2#time"}
|
||||
Number:Time LandroidMowerWednesday2Duration "Duration" {channel="worxlandroid:mower:MyWorxBridge:mymower:wednesday2#duration", unit="min"}
|
||||
Switch LandroidMowerWednesday2Edgecut "Edgecut" {channel="worxlandroid:mower:MyWorxBridge:mymower:wednesday2#edgecut"}
|
||||
Switch LandroidMowerThursdayEnable "Active" {channel="worxlandroid:mower:MyWorxBridge:mymower:thursday#enable"}
|
||||
DateTime LandroidMowerThursdayTime "Start time" {channel="worxlandroid:mower:MyWorxBridge:mymower:thursday#time"}
|
||||
Number:Time LandroidMowerThursdayDuration "Duration" {channel="worxlandroid:mower:MyWorxBridge:mymower:thursday#duration", unit="min"}
|
||||
Switch LandroidMowerThursdayEdgecut "Edgecut" {channel="worxlandroid:mower:MyWorxBridge:mymower:thursday#edgecut"}
|
||||
Switch LandroidMowerThursday2Enable "Active" {channel="worxlandroid:mower:MyWorxBridge:mymower:thursday2#enable"}
|
||||
DateTime LandroidMowerThursday2Time "Start time" {channel="worxlandroid:mower:MyWorxBridge:mymower:thursday2#time"}
|
||||
Number:Time LandroidMowerThursday2Duration "Duration" {channel="worxlandroid:mower:MyWorxBridge:mymower:thursday2#duration", unit="min"}
|
||||
Switch LandroidMowerThursday2Edgecut "Edgecut" {channel="worxlandroid:mower:MyWorxBridge:mymower:thursday2#edgecut"}
|
||||
Switch LandroidMowerFridayEnable "Active" {channel="worxlandroid:mower:MyWorxBridge:mymower:friday#enable"}
|
||||
DateTime LandroidMowerFridayTime "Start time" {channel="worxlandroid:mower:MyWorxBridge:mymower:friday#time"}
|
||||
Number:Time LandroidMowerFridayDuration "Duration" {channel="worxlandroid:mower:MyWorxBridge:mymower:friday#duration", unit="min"}
|
||||
Switch LandroidMowerFridayEdgecut "Edgecut" {channel="worxlandroid:mower:MyWorxBridge:mymower:friday#edgecut"}
|
||||
Switch LandroidMowerFriday2Enable "Active" {channel="worxlandroid:mower:MyWorxBridge:mymower:friday2#enable"}
|
||||
DateTime LandroidMowerFriday2Time "Start time" {channel="worxlandroid:mower:MyWorxBridge:mymower:friday2#time"}
|
||||
Number:Time LandroidMowerFriday2Duration "Duration" {channel="worxlandroid:mower:MyWorxBridge:mymower:friday2#duration", unit="min"}
|
||||
Switch LandroidMowerFriday2Edgecut "Edgecut" {channel="worxlandroid:mower:MyWorxBridge:mymower:friday2#edgecut"}
|
||||
Switch LandroidMowerSaturdayEnable "Active" {channel="worxlandroid:mower:MyWorxBridge:mymower:saturday#enable"}
|
||||
DateTime LandroidMowerSaturdayTime "Start time" {channel="worxlandroid:mower:MyWorxBridge:mymower:saturday#time"}
|
||||
Number:Time LandroidMowerSaturdayDuration "Duration" {channel="worxlandroid:mower:MyWorxBridge:mymower:saturday#duration", unit="min"}
|
||||
Switch LandroidMowerSaturdayEdgecut "Edgecut" {channel="worxlandroid:mower:MyWorxBridge:mymower:saturday#edgecut"}
|
||||
Switch LandroidMowerSaturday2Enable "Active" {channel="worxlandroid:mower:MyWorxBridge:mymower:saturday2#enable"}
|
||||
DateTime LandroidMowerSaturday2Time "Start time" {channel="worxlandroid:mower:MyWorxBridge:mymower:saturday2#time"}
|
||||
Number:Time LandroidMowerSaturday2Duration "Duration" {channel="worxlandroid:mower:MyWorxBridge:mymower:saturday2#duration", unit="min"}
|
||||
Switch LandroidMowerSaturday2Edgecut "Edgecut" {channel="worxlandroid:mower:MyWorxBridge:mymower:saturday2#edgecut"}
|
||||
Switch LandroidMowerOneTimeEdgecut "Schedule edgecut" {channel="worxlandroid:mower:MyWorxBridge:mymower:one-time#edgecut"}
|
||||
Number:Time LandroidMowerOneTimeDuration "Edgecut duration" {channel="worxlandroid:mower:MyWorxBridge:mymower:one-time#duration", unit="min"}
|
||||
Number:Temperature LandroidMowerBatteryTemperature "Battery temperature" {channel="worxlandroid:mower:MyWorxBridge:mymower:battery#temperature"}
|
||||
Number:ElectricPotential LandroidMowerBatteryVoltage "Battery voltage" {channel="worxlandroid:mower:MyWorxBridge:mymower:battery#voltage"}
|
||||
Number LandroidMowerBatteryLevel "Battery level" {channel="worxlandroid:mower:MyWorxBridge:mymower:battery#level"}
|
||||
Number LandroidMowerBatteryChargeCycles "Current charge cycles" {channel="worxlandroid:mower:MyWorxBridge:mymower:battery#charge-cycles"}
|
||||
Number LandroidMowerBatteryChargeCyclesTotal "Total charge cycles" {channel="worxlandroid:mower:MyWorxBridge:mymower:battery#charge-cycles-total"}
|
||||
Switch LandroidMowerBatteryCharging "Battery charging" {channel="worxlandroid:mower:MyWorxBridge:mymower:battery#charging"}
|
||||
Number:Angle LandroidMowerOrientationPitch "Pitch" {channel="worxlandroid:mower:MyWorxBridge:mymower:orientation#pitch"}
|
||||
Number:Angle LandroidMowerOrientationRoll "Roll" {channel="worxlandroid:mower:MyWorxBridge:mymower:orientation#roll"}
|
||||
Number:Angle LandroidMowerOrientationYaw "Yaw" {channel="worxlandroid:mower:MyWorxBridge:mymower:orientation#yaw"}
|
||||
Number:Time LandroidMowerMetricsBladeTime "Current blade time" {channel="worxlandroid:mower:MyWorxBridge:mymower:metrics#blade-time", unit="h"}
|
||||
Number:Time LandroidMowerMetricsBladeTimeTotal "Total blade time" {channel="worxlandroid:mower:MyWorxBridge:mymower:metrics#blade-time-total", unit="h"}
|
||||
Number:Length LandroidMowerMetricsDistance "Total distance" {channel="worxlandroid:mower:MyWorxBridge:mymower:metrics#distance", unit="km"}
|
||||
Number:Time LandroidMowerMetricsTotalTime "Total time" {channel="worxlandroid:mower:MyWorxBridge:mymower:metrics#total-time", unit="h"}
|
||||
Switch LandroidMowerRainState "State" {channel="worxlandroid:mower:MyWorxBridge:mymower:rain#state"}
|
||||
Number:Time LandroidMowerRainCounter "Counter" {channel="worxlandroid:mower:MyWorxBridge:mymower:rain#counter", unit="min"}
|
||||
Number:Time LandroidMowerRainDelay "Delay" {channel="worxlandroid:mower:MyWorxBridge:mymower:rain#delay", unit="min"}
|
||||
Number:Power LandroidMowerWifiRssi "Rssi" {channel="worxlandroid:mower:MyWorxBridge:mymower:wifi#rssi", unit="dBm"}
|
||||
Number LandroidMowerWifiWifiQuality "Wifi quality" {channel="worxlandroid:mower:MyWorxBridge:mymower:wifi#wifi-quality"}
|
||||
|
||||
```
|
||||
|
||||
### $OPENHAB_CONF/sitemaps/landroid.sitemap
|
||||
|
||||
```perl
|
||||
sitemap landroid label="Landroid"
|
||||
{
|
||||
Frame label="Worx Landroid Mower" {
|
||||
Text label="Status" item=MyMower icon=none {
|
||||
Default item=LandroidMowerCommonStatus
|
||||
Default item=LandroidMowerCommonError
|
||||
Default item=LandroidMowerCommonOnline
|
||||
Default item=LandroidMowerCommonOnlineTimestamp
|
||||
Default item=LandroidMowerConfigTimestamp
|
||||
Default item=LandroidMowerConfigCommand
|
||||
Default item=LandroidMowerAwsPoll
|
||||
Text item=LandroidMowerAwsConnected label="AWS connected [%s]"
|
||||
Default item=LandroidMowerOrientationPitch
|
||||
Default item=LandroidMowerOrientationRoll
|
||||
Default item=LandroidMowerOrientationYaw
|
||||
Default item=LandroidMowerMetricsBladeTime
|
||||
Default item=LandroidMowerMetricsBladeTimeTotal
|
||||
Default item=LandroidMowerMetricsDistance
|
||||
Default item=LandroidMowerMetricsTotalTime
|
||||
Default item=LandroidMowerWifiRssi
|
||||
Default item=LandroidMowerWifiWifiQuality
|
||||
}
|
||||
Text label="Control" icon=none {
|
||||
Default item=LandroidMowerCommonAction
|
||||
Default item=LandroidMowerCommonEnable
|
||||
Default item=LandroidMowerCommonLock
|
||||
Default item=LandroidMowerOneTimeEdgecut
|
||||
Setpoint item=LandroidMowerOneTimeDuration minValue=30 maxValue=300 step=30
|
||||
}
|
||||
Text label="Multi zones" icon=none {
|
||||
Default item=LandroidMowerMultiZonesEnable
|
||||
Default item=LandroidMowerMultiZonesLastZone
|
||||
Default item=LandroidMowerMultiZonesZone1
|
||||
Default item=LandroidMowerMultiZonesZone2
|
||||
Default item=LandroidMowerMultiZonesZone3
|
||||
Default item=LandroidMowerMultiZonesZone4
|
||||
Default item=LandroidMowerMultiZonesAllocation0
|
||||
Default item=LandroidMowerMultiZonesAllocation1
|
||||
Default item=LandroidMowerMultiZonesAllocation2
|
||||
Default item=LandroidMowerMultiZonesAllocation3
|
||||
Default item=LandroidMowerMultiZonesAllocation4
|
||||
Default item=LandroidMowerMultiZonesAllocation5
|
||||
Default item=LandroidMowerMultiZonesAllocation6
|
||||
Default item=LandroidMowerMultiZonesAllocation7
|
||||
Default item=LandroidMowerMultiZonesAllocation8
|
||||
Default item=LandroidMowerMultiZonesAllocation9
|
||||
}
|
||||
Text label="Schedule" icon=none {
|
||||
Default item=LandroidMowerScheduleMode
|
||||
Setpoint item=LandroidMowerScheduleTimeExtension minValue=-100 maxValue=100 step=10
|
||||
Default item=LandroidMowerScheduleNextStart
|
||||
Default item=LandroidMowerScheduleNextStop
|
||||
Text label="Sunday" icon=none {
|
||||
Default item=LandroidMowerSundayEnable
|
||||
Default item=LandroidMowerSundayTime
|
||||
Default item=LandroidMowerSundayDuration
|
||||
Default item=LandroidMowerSundayEdgecut
|
||||
Default item=LandroidMowerSunday2Enable
|
||||
Default item=LandroidMowerSunday2Time
|
||||
Default item=LandroidMowerSunday2Duration
|
||||
Default item=LandroidMowerSunday2Edgecut
|
||||
}
|
||||
Text label="Monday" icon=none {
|
||||
Default item=LandroidMowerMondayEnable
|
||||
Default item=LandroidMowerMondayTime
|
||||
Default item=LandroidMowerMondayDuration
|
||||
Default item=LandroidMowerMondayEdgecut
|
||||
Default item=LandroidMowerMonday2Enable
|
||||
Default item=LandroidMowerMonday2Time
|
||||
Default item=LandroidMowerMonday2Duration
|
||||
Default item=LandroidMowerMonday2Edgecut
|
||||
}
|
||||
Text label="Tuesday" icon=none {
|
||||
Default item=LandroidMowerTuesdayEnable
|
||||
Default item=LandroidMowerTuesdayTime
|
||||
Default item=LandroidMowerTuesdayDuration
|
||||
Default item=LandroidMowerTuesdayEdgecut
|
||||
Default item=LandroidMowerTuesday2Enable
|
||||
Default item=LandroidMowerTuesday2Time
|
||||
Default item=LandroidMowerTuesday2Duration
|
||||
Default item=LandroidMowerTuesday2Edgecut
|
||||
}
|
||||
Text label="Wednesday" icon=none {
|
||||
Default item=LandroidMowerWednesdayEnable
|
||||
Default item=LandroidMowerWednesdayTime
|
||||
Default item=LandroidMowerWednesdayDuration
|
||||
Default item=LandroidMowerWednesdayEdgecut
|
||||
Default item=LandroidMowerWednesday2Enable
|
||||
Default item=LandroidMowerWednesday2Time
|
||||
Default item=LandroidMowerWednesday2Duration
|
||||
Default item=LandroidMowerWednesday2Edgecut
|
||||
}
|
||||
Text label="Thursday" icon=none {
|
||||
Default item=LandroidMowerThursdayEnable
|
||||
Default item=LandroidMowerThursdayTime
|
||||
Default item=LandroidMowerThursdayDuration
|
||||
Default item=LandroidMowerThursdayEdgecut
|
||||
Default item=LandroidMowerThursday2Enable
|
||||
Default item=LandroidMowerThursday2Time
|
||||
Default item=LandroidMowerThursday2Duration
|
||||
Default item=LandroidMowerThursday2Edgecut
|
||||
}
|
||||
Text label="Friday" icon=none {
|
||||
Default item=LandroidMowerFridayEnable
|
||||
Default item=LandroidMowerFridayTime
|
||||
Default item=LandroidMowerFridayDuration
|
||||
Default item=LandroidMowerFridayEdgecut
|
||||
Default item=LandroidMowerFriday2Enable
|
||||
Default item=LandroidMowerFriday2Time
|
||||
Default item=LandroidMowerFriday2Duration
|
||||
Default item=LandroidMowerFriday2Edgecut
|
||||
}
|
||||
Text label="Saturday" icon=none {
|
||||
Default item=LandroidMowerSaturdayEnable
|
||||
Default item=LandroidMowerSaturdayTime
|
||||
Default item=LandroidMowerSaturdayDuration
|
||||
Default item=LandroidMowerSaturdayEdgecut
|
||||
Default item=LandroidMowerSaturday2Enable
|
||||
Default item=LandroidMowerSaturday2Time
|
||||
Default item=LandroidMowerSaturday2Duration
|
||||
Default item=LandroidMowerSaturday2Edgecut
|
||||
}
|
||||
}
|
||||
Text label="Battery" icon=none {
|
||||
Default item=LandroidMowerBatteryTemperature
|
||||
Default item=LandroidMowerBatteryVoltage
|
||||
Default item=LandroidMowerBatteryLevel
|
||||
Default item=LandroidMowerBatteryChargeCycles
|
||||
Default item=LandroidMowerBatteryChargeCyclesTotal
|
||||
Text item=LandroidMowerBatteryCharging label="Battery charging [%s]"
|
||||
}
|
||||
Text label="Rainsensor" icon=none {
|
||||
Text item=LandroidMowerRainState label="State [%s]"
|
||||
Default item=LandroidMowerRainCounter
|
||||
Setpoint item=LandroidMowerRainDelay minValue=30 maxValue=600 step=15
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### $OPENHAB_CONF/rules/landroid.rules
|
||||
|
||||
```java
|
||||
|
||||
rule "Landroid mower status"
|
||||
when
|
||||
Item LandroidMowerCommonError changed or
|
||||
Item LandroidMowerCommonStatus changed
|
||||
then
|
||||
if (LandroidMowerCommonError.state != "NO_ERR") {
|
||||
MyMower.postUpdate(transform("MAP", "landroid_error_de.map", LandroidMowerCommonError.state.toString))
|
||||
} else {
|
||||
MyMower.postUpdate(transform("MAP", "landroid_status_de.map", LandroidMowerCommonStatus.state.toString))
|
||||
}
|
||||
end
|
||||
|
||||
```
|
||||
|
||||
### $OPENHAB_CONF/transform/landroid_error_en.map
|
||||
|
||||
```text
|
||||
|
||||
UNKNOWN=unknown
|
||||
NO_ERR=no error
|
||||
TRAPPED=trapped
|
||||
LIFTED=lifted
|
||||
WIRE_MISSING=wire missing
|
||||
OUTSIDE_WIRE=outside wire
|
||||
RAINING=raining
|
||||
CLOSE_DOOR_TO_MOW=close door to mow
|
||||
CLOSE_DOOR_TO_GO_HOME=close door to go home
|
||||
BLADE_MOTOR_BLOCKED=blade motor blocked
|
||||
WHEEL_MOTOR_BLOKED=wheel motor blocked
|
||||
TRAPPED_TIMEOUT=trapped timeout
|
||||
UPSIDE_DOWN=upside down
|
||||
BATTERY_LOW=battery low
|
||||
REVERSE_WIRE=reverse wire
|
||||
CHARGE_ERROR=charge error
|
||||
TIMEOUT_FINDING_HOME=timeout finding home
|
||||
MOWER_LOCKED=mower locked
|
||||
BATTERY_OVER_TEMPERATURE=battery over temperature
|
||||
MOWER_OUTSIDE_WIRE=mower outside wire
|
||||
|
||||
```
|
||||
|
||||
### $OPENHAB_CONF/transform/landroid_status_en.map
|
||||
|
||||
```text
|
||||
|
||||
UNKNOWN=unknown
|
||||
IDLE=idle
|
||||
HOME=home
|
||||
START_SEQUENCE=start sequence
|
||||
LEAVING_HOME=leaving home
|
||||
FOLLOW_WIRE=follow wire
|
||||
SEARCHING_HOME=searching home
|
||||
SEARCHING_WIRE=searching wire
|
||||
MOWING=mowing
|
||||
LIFTED=lifted
|
||||
TRAPPED=trapped
|
||||
BLADE_BLOCKED=blade blocked
|
||||
DEBUG=debug
|
||||
REMOTE_CONTROL=remote control
|
||||
GOING_HOME=going home
|
||||
ZONE_TRAINING=zone training
|
||||
BORDER_CUT=border cut
|
||||
SEARCHING_ZONE=searching zone
|
||||
PAUSE=pause
|
||||
MANUEL_STOP=manuel stop
|
||||
|
||||
```
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg version="1.1" id="prefix__Layer_1" x="0" y="0" height="32" width="32" viewBox="0 0 283.46 283.46" xmlns="http://www.w3.org/2000/svg">
|
||||
<style></style>
|
||||
<path
|
||||
d="M199.7 34.99h-28.01v-8.37a7.16 7.16 0 00-7.16-7.16h-45.59a7.16 7.16 0 00-7.16 7.16v8.37H83.77c-8.45 0-15.31 6.85-15.31 15.31v198.41c0 8.45 6.85 15.31 15.31 15.31H199.7c8.45 0 15.31-6.85 15.31-15.31V50.29c-.01-8.45-6.86-15.3-15.31-15.3z"
|
||||
transform="rotate(90 141.735 141.74)" fill="#dee0e1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 469 B |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="32" width="32" viewBox="0 0 48 48">
|
||||
<path fill="#37474F" d="M45 38H3a2 2 0 01-2-2V12a2 2 0 012-2h42a2 2 0 012 2v24a2 2 0 01-2 2z" />
|
||||
<path fill="#FFF" d="M16 35H4V13h12v22zm14-22H18v22h12V13zm14 0H32v22h12V13z" />
|
||||
<path fill="#CFD8DC"
|
||||
d="M16 17H4v-4h12v4zm14-4H18v4h12v-4zm14 0H32v4h12v-4zM16 33H4v2h12v-2zm14 0H18v2h12v-2zm14 0H32v2h12v-2z" />
|
||||
<path fill="none"
|
||||
d="M37.995 17.812c.42 0 .757-.231 1.01-.694.019-.035.025-.082.043-.118h-2.093c.014.029.024.066.039.094.254.479.587.718 1.001.718z" />
|
||||
<path fill="#546E7A"
|
||||
d="M14 25.179c0 1.884-.345 3.325-1.035 4.324-.691.998-1.676 1.497-2.956 1.497-1.267 0-2.246-.485-2.936-1.455S6.025 27.185 6 25.376v-2.574c0-1.878.344-3.314 1.031-4.309S8.704 17 9.991 17s2.273.493 2.96 1.479c.687.986 1.037 2.393 1.05 4.221v2.479zm-2.686-2.767c0-1.077-.107-1.885-.322-2.424-.214-.539-.548-.808-1.002-.808-.851 0-1.292.987-1.324 2.959v3.411c0 1.102.109 1.923.326 2.461.217.539.556.808 1.017.808.435 0 .758-.252.969-.756.211-.504.323-1.276.336-2.316v-3.335zM28 25.179c0 1.884-.345 3.325-1.035 4.324-.691.998-1.676 1.497-2.956 1.497-1.267 0-2.246-.485-2.936-1.455s-1.048-2.36-1.073-4.169v-2.574c0-1.878.344-3.314 1.031-4.309S22.704 17 23.991 17c1.286 0 2.273.493 2.96 1.479.687.986 1.037 2.393 1.05 4.221v2.479zm-2.686-2.767c0-1.077-.107-1.885-.322-2.424-.214-.539-.548-.808-1.002-.808-.851 0-1.292.987-1.324 2.959v3.411c0 1.102.109 1.923.326 2.461.217.539.556.808 1.017.808.435 0 .758-.252.969-.756.211-.504.323-1.276.336-2.316v-3.335zM42 25.554V24h-8v2.24h5.303L36.892 33h2.609L42 25.554zm-2.994-8.436c-.253.462-.59.694-1.01.694-.414 0-.748-.239-1.001-.718-.015-.028-.025-.065-.039-.094h-2.589c.182.563.42 1.074.741 1.507.738.995 1.719 1.493 2.943 1.493.76 0 1.441-.199 2.044-.596s1.071-.956 1.404-1.676c.108-.232.192-.477.264-.728h-2.716c-.016.036-.022.083-.041.118z" />
|
||||
<path fill="#455A64"
|
||||
d="M36.178 35l.714-2h2.609l-.671 2h-2.652zm.436-20.078v-.963c.111-.315.28-.579.505-.793.078-.074.171-.118.262-.167H34.02c-.006.169-.02.331-.02.503v.986c0 .951.131 1.78.367 2.512h2.589c-.225-.477-.342-1.165-.342-2.078zM41.641 13h-3.144c.188.115.354.292.495.54.263.462.394 1.062.394 1.798 0 .67-.121 1.216-.338 1.662h2.716A6.032 6.032 0 0042 15.305c0-.884-.129-1.643-.359-2.305z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="32" height="32" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M92.6 21c-32 0-64.04 24-64.04 72L92.6 221l64-128c0-48-32-72-64-72zm282.3 39c-6.9.29-13.6 1.6-19.2 2.8l3.8 17.6c5.6-1.25 11.4-2.04 16.3-2.4zM92.6 61c17.7 0 32 14.33 32 32 0 17.7-14.3 32-32 32-17.67 0-32-14.3-32-32 0-17.67 14.33-32 32-32zm302.2.2l-3 17.7c4.9 1.03 9.8 2.32 14.1 4.9l8.7-15.8c-6.1-3.25-12.9-6.17-19.8-6.8zm-57.5 6.7c-6.1 2.38-12.2 4.51-17.4 6.6L327 91c5.5-2.34 11.3-4.38 16.2-6.1zM431 81.3L417.3 93c3.6 4.12 6.4 9.2 8.6 13.3l16.1-8.1c-3.4-6.55-6.4-11.51-11-16.9zm-127.8.9c-6.1 3.11-11.1 5.88-16.5 8.6l8.8 15.8c5.2-3 10.9-5.9 15.5-8.2zm-32.3 17.9c-5.3 3.1-10.5 6.2-15.6 9.6l9.8 15c4.9-3.2 10-6.2 15-9.2zM448.2 118c-5.9 1-11.9 1.7-17.8 2.4.4 5 .1 10.4-.9 14.6l17.5 4.1c1-7.2 1.9-14.6 1.2-21.1zm-208.1 1.7c-5 3.4-9.9 6.9-14.9 10.3l10.4 14.7c4.8-3.5 9.7-6.8 14.6-10.2zm-29.6 21.1c-5 3.6-10.2 7.6-14.5 10.9l10.9 14.3c5.5-4 9.3-7 14.3-10.7zm213 8c-3 4.6-6.5 9.2-10 12.7l13.1 12.5c4.3-5.1 8.9-10.3 12.1-15.5zm-241.8 14.1c-4.9 3.8-9.8 7.7-14.1 11.3l11.4 13.9c4.7-3.9 9.5-7.9 13.9-11.1zM401.1 173c-4.6 3.7-9.4 7.3-13.8 10.3l10.3 14.8c5.3-3.6 10.5-7.5 15-11.1zm-247.4 12.9c-4.7 3.8-9.2 7.8-13.8 11.7l11.7 13.7c4.5-3.9 9-7.8 13.6-11.6zm218.9 7c-5.1 3-10.4 6.1-15.2 8.7l8.6 15.9c5.4-3.3 11.5-6.2 16-9.2zm-246.4 16.6c-4.5 4-8.9 8-13.4 12.1l12.1 13.4c4.4-4 8.9-8 13.3-12zm215.5.4c-5.3 2.6-10.6 5.3-15.9 7.9l7.7 16.2c6.2-3 10.8-5.5 16.4-8.1zm-32 15.4c-5.5 2.5-10.8 4.9-16.4 7.2l7.3 16.5c5.5-2.4 11-4.9 16.5-7.4zM99.6 234c-5.1 4.5-8.65 8-13.3 12.5l12.7 13c4.7-4.5 8.5-8.4 12.9-12.2zm177.3 5.8c-5.5 2.3-11 4.7-16.5 7l7 16.7c5.6-2.3 11.1-4.7 16.6-7.1zm-33.1 14c-5.5 2.4-11 4.8-16.6 7l7 16.7c5.5-2.3 11.1-4.7 16.6-7zm184.8 7.2c-32 0-64 24-64 72l64 128 64-128c0-48-32-72-64-72zm-218 6.8c-5.7 2.6-11.7 5-16.6 7.1l7.1 16.6c5.9-2.5 11.5-4.9 16.5-7.1zM177.4 282c-5.4 2.5-11.7 5.3-16.5 7.5l7.4 16.4c5.9-2.6 11.1-5.2 16.3-7.4zm-33 15c-5.6 2.7-11.4 5.5-16.4 8l8.1 16.1c5.4-2.8 11-5.4 15.9-7.8zm284.2 4c17.7 0 32 14.3 32 32s-14.3 32-32 32-32-14.3-32-32 14.3-32 32-32zm-316.8 12.3c-5.3 2.9-10.6 5.9-16 9l9 15.6c5.1-3 10.3-5.8 15.5-8.6zM80.1 332c-5.61 3.2-11.03 7.5-15.7 10.6L75.3 357c4.97-3.6 10.32-7.3 14.6-9.9zm-29.9 22.6c-4.8 4.4-9.53 9.5-13.2 13.8l13.7 11.7c3.85-4.7 7.2-8.2 11.7-12.2zm217.8 1.3l1.6 17.9c5.2-.9 10.4-.3 15.6.5l3.1-17.7c-6.6-1-13.6-1.7-20.3-.7zm-37.2 10l6.8 16.7c5.2-2.3 10.6-4.1 16.1-5.8-1.9-5.7-3.3-11.5-4.8-17.3-6.3 1.8-12.6 4.2-18.1 6.4zm77.5-.9l-10.2 14.8c4.2 3.1 8.3 6.4 11.6 10.5l13.6-11.8c-5.1-5.2-9-10.1-15-13.5zm-94.5 9c-5.5 2.8-10.8 6-16.1 9.1l9.1 15.5c5.2-2.8 10.3-6.1 15.4-8.8zM26.01 385c-3.02 6.5-5.47 13.5-6.61 19.7l17.7 3.1c1.08-5.7 2.63-9.8 4.9-14.7-5.49-2.4-10.73-5.3-15.99-8.1zm156.09 7.8c-5.1 3.3-10.1 6.6-15.1 10l10 15c5-3.3 9.9-6.7 14.9-10zm152.7 1.2l-15.1 9.8c3.2 4.8 6.3 9.8 9.2 14.9l15.6-9c-3.5-5.6-6-10.6-9.7-15.7zm-182.7 19c-5 3.3-10 6.5-14.9 10l10 15c4.8-3.5 9.9-6.8 15-10.2zm-114.8 9.5c-5.79 1.2-11.63 2.2-17.45 3.3 1.05 7 3.86 13.8 6.4 19.2l16.25-7.8c-2.17-5-4.23-10.2-5.2-14.7zm316.1 2.8l-15.6 9c3.1 5.4 6.7 11.2 9.6 15.8l15.1-9.7c-3.4-5.3-6.3-10.3-9.1-15.1zm-231 7.5c-5 3.1-9.9 6.1-15.1 9l8.9 15.7c5.3-3.1 10.6-6.2 15.7-9.5zm-71.3 16.3l-12.3 13.2c5.56 5.3 12.42 8.8 19.9 10.4l4-17.5c-4.44-.9-8.59-3.1-11.6-6.1zm41 .3c-5.01 2.3-10.21 4.1-15.6 5.2l4.1 17.6c6.42-1.3 12.46-3.7 18.5-6.2zm280.3 4.8l-13.9 11.3c4.3 5.3 9.6 10.4 14.2 14l11.1-14.2c-4.4-3.4-8.2-7.5-11.4-11.1zm24.1 17.5l-4.5 17.5c7.9 1.6 13.8 2.1 21.2 1.3l-2.2-17.9c-4.9.8-9.7.3-14.5-.9z"
|
||||
fill="#4ba82f" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="542" height="353">
|
||||
<path d="M0 0 C178.86 0 357.72 0 542 0 C542 116.49 542 232.98 542 353 C363.14 353 184.28 353 0 353 C0 236.51 0 120.02 0 0 Z " fill="#ED7C01" transform="translate(0,0)"/>
|
||||
<path d="M0 0 C0.69233356 -0.00044186 1.38466713 -0.00088371 2.0979805 -0.00133896 C47.07451039 -0.01633819 95.18842166 1.19895125 139.0625 12.25 C140.32416992 12.56360474 140.32416992 12.56360474 141.61132812 12.88354492 C145.05421529 14.80285164 145.99044108 17.50025323 147.39453125 21.0078125 C147.6760051 21.67086395 147.95747894 22.33391541 148.2474823 23.01705933 C149.13994245 25.12984181 150.00760645 27.25182056 150.875 29.375 C151.47690697 30.81187284 152.0804051 32.24808026 152.68554688 33.68359375 C154.16308401 37.19828348 155.61847147 40.72141835 157.0625 44.25 C154.06429418 47.33153506 151.0749223 50.14350012 147.62109375 52.703125 C146.79512695 53.31857178 145.96916016 53.93401855 145.11816406 54.56811523 C144.25416992 55.20563721 143.39017578 55.84315918 142.5 56.5 C141.6140918 57.1587915 140.72818359 57.81758301 139.81542969 58.49633789 C138.04914147 59.80978535 136.28174995 61.12175059 134.51318359 62.43212891 C131.27001192 64.83785278 128.04018488 67.26106356 124.8125 69.6875 C123.22916667 70.875 121.64583333 72.0625 120.0625 73.25 C117.36498072 70.59758974 114.76938664 67.97879713 112.4375 65 C104.65085888 55.86947158 94.2548759 49.37984871 84.0625 43.25 C83.27875 42.77820313 82.495 42.30640625 81.6875 41.8203125 C56.8448362 28.24557121 25.41253569 25.19715472 -1.71899414 32.7722168 C-25.50000217 39.83019358 -48.48948078 54.52005258 -62.5546875 75.11328125 C-67.89629662 79.90321599 -77.29640623 79.8427247 -84.20703125 80.87890625 C-86.22846073 81.18940273 -88.24977768 81.50063249 -90.27099609 81.8125 C-91.32631531 81.97492188 -92.38163452 82.13734375 -93.46893311 82.3046875 C-117.63771267 86.0453026 -141.56687053 90.55568857 -165.46905518 95.71868896 C-167.83754131 96.22848477 -170.20725447 96.73201281 -172.57763672 97.23291016 C-191.43441446 101.22185873 -210.13812344 105.7300046 -228.8125 110.5 C-229.81705231 110.75657379 -230.82160461 111.01314758 -231.8565979 111.27749634 C-246.00519994 114.9010163 -260.10227477 118.61818429 -274 123.125 C-279.5661772 124.91061982 -285.14050061 126.60423221 -290.77783203 128.15039062 C-304.34534578 131.87836401 -317.63837059 136.41743473 -330.95996094 140.93188477 C-331.80011246 141.21557419 -332.64026398 141.49926361 -333.50587463 141.79154968 C-335.10523532 142.3319945 -336.70369503 142.87511476 -338.3011322 143.42121887 C-343.71384006 145.25 -343.71384006 145.25 -345.9375 145.25 C-346.78815262 141.9080221 -347.55685736 138.55212181 -348.3125 135.1875 C-348.55355469 134.24455078 -348.79460937 133.30160156 -349.04296875 132.33007812 C-350.53405926 125.42777205 -350.53405926 125.42777205 -348.89355469 122.18896484 C-347.35367337 120.35992029 -345.74398305 118.8139389 -343.9375 117.25 C-343.0403125 116.35796875 -342.143125 115.4659375 -341.21875 114.546875 C-334.28622129 108.19854815 -327.11654346 102.2883689 -319.65478516 96.57324219 C-318.02248673 95.31548594 -316.40572194 94.04048786 -314.79296875 92.7578125 C-301.3044331 82.1366511 -286.86180752 72.72616353 -271.9375 64.25 C-270.60114502 63.48864746 -270.60114502 63.48864746 -269.23779297 62.71191406 C-244.23640023 48.58445711 -218.18425294 37.32069412 -190.9375 28.25 C-189.9077002 27.90630371 -188.87790039 27.56260742 -187.81689453 27.20849609 C-171.50827612 21.83283239 -155.00069884 17.67574756 -138.24414062 13.97998047 C-135.41469441 13.35534637 -132.58971602 12.71467895 -129.765625 12.06640625 C-109.37535359 7.46836649 -88.65022605 5.09872362 -67.875 3.0625 C-67.01735046 2.97782471 -66.15970093 2.89314941 -65.27606201 2.8059082 C-43.49218628 0.67484852 -21.89014188 0.00641893 0 0 Z " fill="#FEFEFD" transform="translate(366.9375,108.75)"/>
|
||||
<path d="M0 0 C16.24400289 13.57933853 29.01345086 33.92712471 31.51806641 55.26904297 C33.5959331 82.48485606 28.82047364 106.9735342 11.15087891 128.30029297 C-3.65846973 144.99926176 -24.92460338 157.43141483 -47.48193359 159.26904297 C-75.72200393 160.59611957 -98.5087624 153.67674822 -120.11083984 134.99560547 C-136.92946402 119.15082964 -146.82169998 97.21576284 -147.67333984 74.15185547 C-148.25776179 46.69965004 -139.27379252 24.6864073 -120.41943359 4.76904297 C-110.03253136 -5.35997755 -98.20739159 -12.18580537 -84.48193359 -16.73095703 C-83.03753906 -17.23949219 -83.03753906 -17.23949219 -81.56396484 -17.75830078 C-53.71784374 -25.91296782 -22.16657448 -17.91916841 0 0 Z " fill="#FEFDFC" transform="translate(453.48193359375,181.73095703125)"/>
|
||||
<path d="M0 0 C-0.26554687 1.09957031 -0.53109375 2.19914063 -0.8046875 3.33203125 C-1.16161327 4.82548381 -1.51837519 6.31897554 -1.875 7.8125 C-2.13603516 8.89047852 -2.13603516 8.89047852 -2.40234375 9.99023438 C-2.95712204 12.32235794 -3.48425571 14.65893687 -4 17 C-4.18747803 17.83418457 -4.37495605 18.66836914 -4.56811523 19.52783203 C-6.12528218 27.11352931 -6.34737729 34.52139432 -6.3125 42.25 C-6.30758545 43.53471191 -6.3026709 44.81942383 -6.29760742 46.14306641 C-6.13028778 59.67107385 -3.48555194 71.3549463 1.01171875 84.05078125 C2 87 2 87 2 89 C-87.1 89 -176.2 89 -268 89 C-271.375 74.375 -271.375 74.375 -271 69 C-260.8599619 64.47260864 -250.43752572 61.0709151 -239.875 57.6875 C-238.0805136 57.10937612 -236.28608052 56.53108672 -234.49169922 55.95263672 C-229.33090938 54.29195446 -224.16623111 52.64366631 -219 51 C-218.31655518 50.78220886 -217.63311035 50.56441772 -216.92895508 50.34002686 C-202.60323767 45.77837816 -188.20765617 41.6155713 -173.671875 37.77734375 C-167.94411918 36.26261423 -162.23934599 34.67400294 -156.53588867 33.07055664 C-127.78576467 25.01646464 -98.78230382 18.54478896 -69.54344177 12.55247498 C-64.31468477 11.48027189 -59.09102882 10.38781305 -53.87109375 9.2734375 C-10.27667564 0 -10.27667564 0 0 0 Z " fill="#FEFDFC" transform="translate(289,209)"/>
|
||||
<path d="M0 0 C3.57520867 2.78796076 6.81579537 5.77634505 10 9 C10.63550781 9.61359375 11.27101563 10.2271875 11.92578125 10.859375 C23.02518362 22.37715839 27.5258192 38.13957295 27.27709961 53.79003906 C26.67806436 72.41648694 18.74474674 87.6650876 5.4375 100.625 C-7.23878141 111.66127544 -23.56713147 116.42122539 -40.13671875 116.28125 C-57.02925897 115.05145941 -71.60883435 107.31477368 -83 95 C-83.45761719 94.54753906 -83.91523438 94.09507812 -84.38671875 93.62890625 C-94.95860901 82.50206112 -99.60117599 66.78552036 -99.5 51.75 C-99.49427979 50.75000977 -99.48855957 49.75001953 -99.48266602 48.71972656 C-99.09487634 32.50306753 -92.32639857 19.01762626 -81.3515625 7.1953125 C-58.40741338 -14.22430844 -26.2768317 -17.35842718 0 0 Z " fill="#EC7801" transform="translate(432,199)"/>
|
||||
<path d="M0 0 C5.37104009 -0.09964824 10.74152986 -0.17183292 16.11328125 -0.21972656 C17.93661892 -0.23971364 19.75989356 -0.26691286 21.58300781 -0.30175781 C42.96231957 -0.69983919 42.96231957 -0.69983919 52.4375 6.375 C55.99449598 10.01875198 57.80677326 13.22771606 58.36328125 18.33203125 C58.17615373 31.15746384 56.496419 42.58812637 47.69921875 52.625 C38.12104385 61.68181235 28.99988575 64.18084663 16.1015625 64.09765625 C15.26660248 64.0962413 14.43164246 64.09482635 13.57138062 64.09336853 C10.92254958 64.08780174 8.2738052 64.07525547 5.625 64.0625 C3.82161581 64.0574807 2.01823029 64.05291855 0.21484375 64.04882812 C-4.19013721 64.0378432 -8.59505394 64.02060163 -13 64 C-12.39813426 57.96004758 -11.52807857 52.09253591 -10.29296875 46.15234375 C-10.12630203 45.3373793 -9.95963531 44.52241486 -9.78791809 43.68275452 C-9.43602638 41.96721458 -9.08213265 40.25208418 -8.72631836 38.53735352 C-8.19410643 35.96937668 -7.66981158 33.39987844 -7.14648438 30.83007812 C-5.02307974 20.4819822 -2.65826148 10.22373964 0 0 Z " fill="#FEFDFB" transform="translate(451,22)"/>
|
||||
<path d="M0 0 C5.22864743 -0.07476069 10.45697628 -0.12888763 15.68603516 -0.16479492 C17.46082996 -0.17978093 19.23558837 -0.20017682 21.01025391 -0.22631836 C45.16490533 -0.57293829 45.16490533 -0.57293829 54 7 C57.79126245 12.46776566 58.68590563 16.52980269 58.5625 23.125 C58.55081787 23.9595874 58.53913574 24.7941748 58.52709961 25.65405273 C58.12292222 37.09987178 54.36265621 45.71227871 46.32421875 53.91015625 C37.8592472 61.52151723 28.65512341 64.50385635 17.37109375 64.29296875 C16.49598648 64.28872391 15.62087921 64.28447906 14.71925354 64.28010559 C11.95833928 64.26347981 9.19818895 64.22585966 6.4375 64.1875 C4.55144293 64.17243228 2.66537427 64.15874756 0.77929688 64.14648438 C-3.81409083 64.11362937 -8.40691647 64.06196135 -13 64 C-9.01498272 42.59225275 -4.53553281 21.29654953 0 0 Z " fill="#FEFDFC" transform="translate(225,22)"/>
|
||||
<path d="M0 0 C5.92350015 3.75507599 9.11980136 8.06541102 11 14.875 C12.33964614 28.61416156 9.33271665 40.79671082 1 51.875 C-6.46351775 59.94241234 -15.5487549 64.32429208 -26.48828125 65.1328125 C-35.12675512 65.393269 -43.02745683 65.14927115 -49.8359375 59.296875 C-56.29698939 52.30002857 -57.57584306 45.85956536 -57.34765625 36.50390625 C-56.2572003 25.12150654 -51.07265074 14.45389401 -42.8125 6.5625 C-31.23943747 -2.83703843 -14.1090154 -6.53470187 0 0 Z " fill="#FEFDFC" transform="translate(401,23.125)"/>
|
||||
<path d="M0 0 C10.23 0 20.46 0 31 0 C32.983286 11.72725635 34.52469716 23.11742892 35 35 C35.66 35 36.32 35 37 35 C37.1134375 33.95714844 37.226875 32.91429687 37.34375 31.83984375 C38.11269588 25.69775319 39.32716719 19.63777395 40.5 13.5625 C40.72558594 12.36689453 40.95117188 11.17128906 41.18359375 9.93945312 C41.51037109 8.23885742 41.51037109 8.23885742 41.84375 6.50390625 C42.04162109 5.47080322 42.23949219 4.4377002 42.44335938 3.37329102 C43 1 43 1 44 0 C45.56172745 -0.08685603 47.12729344 -0.10702219 48.69140625 -0.09765625 C49.63564453 -0.09443359 50.57988281 -0.09121094 51.55273438 -0.08789062 C52.54595703 -0.07951172 53.53917969 -0.07113281 54.5625 -0.0625 C55.55958984 -0.05798828 56.55667969 -0.05347656 57.58398438 -0.04882812 C60.05604849 -0.03700007 62.52799518 -0.02051849 65 0 C62.21360627 15.23501746 59.10704459 30.38195799 55.75 45.5 C55.57178711 46.30642944 55.39357422 47.11285889 55.20996094 47.94372559 C54.70058244 50.2444888 54.18710719 52.54429136 53.671875 54.84375 C53.51789246 55.53845398 53.36390991 56.23315796 53.20526123 56.94891357 C52.11587444 61.76825113 52.11587444 61.76825113 51 64 C41.1 64 31.2 64 21 64 C19.74265166 58.97060663 18.66040564 54.37881241 17.9296875 49.3125 C17.77226074 48.23613281 17.61483398 47.15976563 17.45263672 46.05078125 C17.2620166 44.71402344 17.07139648 43.37726563 16.875 42 C15.946875 35.565 15.946875 35.565 15 29 C12.69 40.55 10.38 52.1 8 64 C1.07 64 -5.86 64 -13 64 C-12.44855327 56.03028178 -11.15543337 48.6228502 -9.25 40.875 C-6.77818275 30.54008496 -4.56866918 20.18057142 -2.56152344 9.74560547 C-2.27390137 8.26278076 -2.27390137 8.26278076 -1.98046875 6.75 C-1.81442139 5.87859375 -1.64837402 5.0071875 -1.47729492 4.109375 C-1 2 -1 2 0 0 Z " fill="#FEFDFB" transform="translate(156,22)"/>
|
||||
<path d="M0 0 C1.2776825 -0.01441132 1.2776825 -0.01441132 2.58117676 -0.02911377 C3.51022827 -0.03336365 4.43927979 -0.03761353 5.39648438 -0.04199219 C6.82728333 -0.0505423 6.82728333 -0.0505423 8.2869873 -0.05926514 C10.31468593 -0.0687365 12.34240009 -0.07525755 14.37011719 -0.07910156 C17.44950056 -0.0887397 20.52819728 -0.11971438 23.60742188 -0.15136719 C25.57877208 -0.15790387 27.55012707 -0.16315172 29.52148438 -0.16699219 C30.43434204 -0.17933899 31.34719971 -0.19168579 32.28771973 -0.20440674 C38.88083353 -0.18184521 45.96183161 0.39430034 51.02539062 5.02050781 C53.20534749 9.85701131 52.71812886 15.4418289 51.36914062 20.48144531 C49.01141407 25.99328606 45.23408 28.51375444 39.99023438 31.16113281 C40.8075 31.84175781 41.62476563 32.52238281 42.46679688 33.22363281 C45.59374842 36.86367548 45.55532647 38.43884504 45.36523438 43.16113281 C45.32140625 44.47210938 45.27757812 45.78308594 45.23242188 47.13378906 C45.15647358 48.47651594 45.07577155 49.81898267 44.99023438 51.16113281 C44.9103125 52.43859375 44.83039063 53.71605469 44.74804688 55.03222656 C44.52789109 58.07771488 44.26359834 61.11995873 43.99023438 64.16113281 C35.74023438 64.16113281 27.49023438 64.16113281 18.99023438 64.16113281 C18.99023438 57.23113281 18.99023438 50.30113281 18.99023438 43.16113281 C16.68023437 42.83113281 14.37023437 42.50113281 11.99023438 42.16113281 C11.8690625 42.85078125 11.74789063 43.54042969 11.62304688 44.25097656 C8.67253715 60.79652727 8.67253715 60.79652727 6.99023438 64.16113281 C-0.92976563 64.16113281 -8.84976562 64.16113281 -17.00976562 64.16113281 C-15.88455676 52.90904421 -15.88455676 52.90904421 -14.90234375 48.17651367 C-14.6854187 47.11939682 -14.46849365 46.06227997 -14.24499512 44.97312927 C-14.01268188 43.86856888 -13.78036865 42.76400848 -13.54101562 41.62597656 C-13.30064575 40.46610733 -13.06027588 39.3062381 -12.81262207 38.11122131 C-12.30742083 35.67950472 -11.79969254 33.24831189 -11.28955078 30.81762695 C-10.50585919 27.08000203 -9.73187332 23.34045798 -8.95898438 19.60058594 C-8.46606575 17.23393049 -7.97259609 14.86738973 -7.47851562 12.50097656 C-7.24620239 11.37819809 -7.01388916 10.25541962 -6.77453613 9.09861755 C-6.55761108 8.06987015 -6.34068604 7.04112274 -6.1171875 5.98120117 C-5.9270105 5.07204453 -5.7368335 4.16288788 -5.54089355 3.22618103 C-4.62969922 -0.31658169 -3.40463075 0.03090147 0 0 Z " fill="#FEFDFC" transform="translate(296.009765625,21.8388671875)"/>
|
||||
<path d="M0 0 C10.89 0 21.78 0 33 0 C34.57885705 3.15771409 34.56626168 6.61941122 34.90234375 10.09375 C34.98496964 10.91387573 35.06759552 11.73400146 35.15272522 12.57897949 C35.41801908 15.21896475 35.67797445 17.85944201 35.9375 20.5 C36.19342662 23.07285091 36.45049489 25.6455844 36.7081604 28.21826172 C36.8794061 29.92815338 37.0499577 31.6381147 37.21981812 33.34814453 C37.88251759 39.97371873 38.60995408 46.59088514 39.39378357 53.20324707 C39.81505107 56.8560619 40 60.30719722 40 64 C31.42 64 22.84 64 14 64 C14 60.7 14 57.4 14 54 C7.73 54 1.46 54 -5 54 C-5.66 55.98 -6.32 57.96 -7 60 C-7.66 61.32 -8.32 62.64 -9 64 C-16.92 64 -24.84 64 -33 64 C-31.56559271 59.69677813 -30.22066662 56.23130939 -28.1640625 52.28125 C-27.60001709 51.19440918 -27.03597168 50.10756836 -26.45483398 48.98779297 C-25.85098877 47.83682129 -25.24714355 46.68584961 -24.625 45.5 C-23.99735112 44.29710354 -23.36982985 43.0941405 -22.74243164 41.89111328 C-15.38034339 27.80727167 -7.71751155 13.89152078 0 0 Z " fill="#FEFDFC" transform="translate(101,22)"/>
|
||||
<path d="M0 0 C1.5631897 0.00785522 1.5631897 0.00785522 3.15795898 0.01586914 C4.28266602 0.0190918 5.40737305 0.02231445 6.56616211 0.02563477 C7.75016602 0.03401367 8.93416992 0.04239258 10.15405273 0.05102539 C11.34192383 0.05553711 12.52979492 0.06004883 13.75366211 0.06469727 C16.69967499 0.07652864 19.64558954 0.09301252 22.59155273 0.11352539 C22.07629653 8.90968487 20.09884091 17.31901498 18.09155273 25.86352539 C17.60747122 27.9454144 17.12517938 30.02770011 16.64465332 32.1104126 C16.21035627 33.98950042 15.77121924 35.86746772 15.33178711 37.74536133 C14.65002471 40.84747018 14.1137073 43.98059802 13.59155273 47.11352539 C20.85155273 47.11352539 28.11155273 47.11352539 35.59155273 47.11352539 C34.59155273 56.11352539 34.59155273 56.11352539 32.59155273 64.11352539 C16.75155273 64.11352539 0.91155273 64.11352539 -15.40844727 64.11352539 C-14.75614894 57.59054215 -14.01306436 51.45795657 -12.66625977 45.09790039 C-12.49722137 44.27960754 -12.32818298 43.4613147 -12.15402222 42.6182251 C-11.61860632 40.03230578 -11.0761283 37.44792862 -10.53344727 34.86352539 C-10.17751934 33.14877689 -9.82204754 31.43393364 -9.46704102 29.71899414 C-8.61522415 25.6111578 -7.75393338 21.50538805 -6.8885498 17.40039062 C-6.42652051 15.19961341 -5.97091707 12.99759692 -5.5168457 10.79516602 C-5.25162109 9.52350586 -4.98639648 8.2518457 -4.71313477 6.94165039 C-4.48352051 5.83208984 -4.25390625 4.7225293 -4.01733398 3.5793457 C-3.17038508 0.14944035 -3.17038508 0.14944035 0 0 Z " fill="#FEFDFC" transform="translate(33.408447265625,21.886474609375)"/>
|
||||
<path d="M0 0 C1.5631897 0.00785522 1.5631897 0.00785522 3.15795898 0.01586914 C4.28266602 0.0190918 5.40737305 0.02231445 6.56616211 0.02563477 C7.75016602 0.03401367 8.93416992 0.04239258 10.15405273 0.05102539 C11.34192383 0.05553711 12.52979492 0.06004883 13.75366211 0.06469727 C16.69967499 0.07652864 19.64558954 0.09301252 22.59155273 0.11352539 C21.95979699 6.58718291 20.94731306 12.86370313 19.63061523 19.23071289 C19.4393808 20.16851593 19.24814636 21.10631897 19.05111694 22.07254028 C18.64794451 24.04445059 18.24277339 26.01595321 17.83569336 27.98706055 C17.21994395 30.97208547 16.61233139 33.95868417 16.00561523 36.94555664 C15.61026941 38.87404206 15.21445139 40.80243076 14.81811523 42.73071289 C14.63987213 43.60815155 14.46162903 44.48559021 14.27798462 45.38961792 C12.95980838 51.73712964 11.30571661 57.82825784 9.59155273 64.11352539 C1.34155273 64.11352539 -6.90844727 64.11352539 -15.40844727 64.11352539 C-14.81089302 58.73553716 -14.23662825 53.75634733 -13.17407227 48.51586914 C-12.93197021 47.312771 -12.68986816 46.10967285 -12.44042969 44.87011719 C-12.18237549 43.60981689 -11.92432129 42.3495166 -11.65844727 41.05102539 C-11.39039742 39.73051493 -11.12257804 38.40995766 -10.85498047 37.08935547 C-10.17619578 33.74559944 -9.49202344 30.40296823 -8.80670166 27.06054688 C-8.17874601 23.99054148 -7.55806092 20.91906252 -6.93579102 17.84790039 C-6.48976007 15.66548936 -6.04317676 13.48319112 -5.59594727 11.30102539 C-5.389375 10.28194092 -5.18280273 9.26285645 -4.9699707 8.21289062 C-4.77822266 7.28677979 -4.58647461 6.36066895 -4.38891602 5.40649414 C-4.22286865 4.59720459 -4.05682129 3.78791504 -3.88574219 2.95410156 C-3.16778963 0.18548555 -2.92417325 0.13783483 0 0 Z " fill="#FEFDFB" transform="translate(423.408447265625,21.886474609375)"/>
|
||||
<path d="M0 0 C24.09 0 48.18 0 73 0 C70.01592986 8.95221043 64.88304369 14.79512004 56.51074219 19.14477539 C46.35496875 24.08723427 36.12981464 25.8357646 24.97265625 23.31640625 C22.50496678 22.37689099 20.29050314 21.31175978 18 20 C16.81664062 19.32904297 16.81664062 19.32904297 15.609375 18.64453125 C8.82404215 14.45425119 3.66574473 9.12783697 0 2 C0 1.34 0 0.68 0 0 Z " fill="#FEFDFD" transform="translate(68,317)"/>
|
||||
<path d="M0 0 C3.3125 0.4375 3.3125 0.4375 4.3125 1.4375 C5.29169828 9.68011847 4.40597342 16.5073649 1.5 24.1875 C1.23952881 24.87851807 0.97905762 25.56953613 0.71069336 26.28149414 C-0.70964442 29.78050304 -1.47251423 31.29417615 -4.6875 33.4375 C-7.25 33.75 -7.25 33.75 -9.6875 33.4375 C-12.5877122 29.0871817 -12.22835924 25.54927936 -11.6875 20.4375 C-7.52541109 0.89284538 -7.52541109 0.89284538 0 0 Z " fill="#F08608" transform="translate(381.6875,37.5625)"/>
|
||||
<path d="M0 0 C3.1875 0.3125 3.1875 0.3125 5.5625 1.6875 C9.11804808 7.43107767 7.35064778 14.28531318 5.9375 20.5625 C3.17407 30.52875911 3.17407 30.52875911 -1.51953125 33.33203125 C-4.38424297 34.65281413 -6.64914742 35.3125 -9.8125 35.3125 C-9.11852092 27.86799711 -7.79774584 20.63688376 -6.3125 13.3125 C-6.08691406 12.16910156 -5.86132812 11.02570312 -5.62890625 9.84765625 C-5.41105469 8.76355469 -5.19320313 7.67945313 -4.96875 6.5625 C-4.77087891 5.57765625 -4.57300781 4.5928125 -4.36914062 3.578125 C-3.5622969 0.2941295 -3.4547066 0.38385629 0 0 Z " fill="#F08708" transform="translate(475.8125,36.6875)"/>
|
||||
<path d="M0 0 C3.20449558 -0.21848833 5.42100153 -0.27649344 8.34765625 1.12109375 C11.31171954 4.49157706 10.46238432 8.29194402 10.32421875 12.5703125 C9.64136044 19.79380162 7.78344278 26.39653846 3 32 C-0.75220181 34.34512613 -2.52961855 35 -7 35 C-5.60375454 27.07181412 -4.034332 19.18950337 -2.375 11.3125 C-2.14554688 10.21744141 -1.91609375 9.12238281 -1.6796875 7.99414062 C-1.12108732 5.32915225 -0.56117263 2.66444765 0 0 Z " fill="#F08607" transform="translate(247,37)"/>
|
||||
<path d="M0 0 C0.33 0 0.66 0 1 0 C1 7.26 1 14.52 1 22 C-2.3 22 -5.6 22 -9 22 C-6.66062389 14.33204498 -3.45231194 7.22862726 0 0 Z " fill="#F08606" transform="translate(113,38)"/>
|
||||
<path d="M0 0 C6.625 -0.25 6.625 -0.25 10 2 C9.74327938 5.7652358 9.40674134 7.55839511 6.8125 10.375 C4 12 4 12 -2 12 C-1.34 8.04 -0.68 4.08 0 0 Z " fill="#F08A0F" transform="translate(313,37)"/>
|
||||
<path d="M0 0 C0.33 0 0.66 0 1 0 C1 3.63 1 7.26 1 11 C1.66 11 2.32 11 3 11 C2.34 14.96 1.68 18.92 1 23 C0.01 22.34 -0.98 21.68 -2 21 C-1.84660156 20.34644531 -1.69320313 19.69289063 -1.53515625 19.01953125 C-0.86620406 15.24507841 -0.64592703 11.51094004 -0.4375 7.6875 C-0.39431641 6.94693359 -0.35113281 6.20636719 -0.30664062 5.44335938 C-0.20129568 3.62908536 -0.10021684 1.8145645 0 0 Z " fill="#F39E38" transform="translate(283,39)"/>
|
||||
<path d="M0 0 C0.66 0.33 1.32 0.66 2 1 C1.34 2.32 0.68 3.64 0 5 C7.92 5 15.84 5 24 5 C24 5.33 24 5.66 24 6 C15.75 6 7.5 6 -1 6 C-1.33 4.68 -1.66 3.36 -2 2 C-1.34 1.34 -0.68 0.68 0 0 Z " fill="#F39F3D" transform="translate(68,81)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1,33 @@
|
||||
<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 511.843 511.843">
|
||||
<path
|
||||
d="M84.201 61.399c-1.156-4.64-5.342-8.076-10.333-8.076H10.661C4.772 53.323 0 58.095 0 63.984c0 5.889 4.772 10.661 10.661 10.661H65.62l60.771 234.624 20.642-5.342L84.201 61.399z"
|
||||
fill="#424953" />
|
||||
<path
|
||||
d="M198.268 267.031h-96.635c-11.731 0-22.767 9.481-24.516 21.087l-12.902 85.334c-1.75 11.605 6.412 21.087 18.143 21.087h115.91c11.73 0 21.329-9.591 21.329-21.306v-84.881c0-11.714-9.599-21.321-21.329-21.321z"
|
||||
fill="#fecd57" />
|
||||
<path d="M490.522 330.559C294.77 223.935 104.288 223.935 104.288 223.935l49.407 190.613 336.827 1.328v-85.317z"
|
||||
fill="#ec5564" />
|
||||
<path
|
||||
d="M170.612 447.866c-29.397 0-53.312-23.914-53.312-53.327 0-29.397 23.915-53.296 53.312-53.296 29.405 0 53.32 23.898 53.32 53.296 0 29.413-23.915 53.327-53.32 53.327z"
|
||||
fill="#646c77" />
|
||||
<path
|
||||
d="M170.612 330.559c-35.333 0-63.98 28.647-63.98 63.98 0 35.349 28.647 63.98 63.98 63.98 35.341 0 63.98-28.632 63.98-63.98 0-35.333-28.639-63.98-63.98-63.98zm0 106.655c-23.516 0-42.651-19.15-42.651-42.675 0-23.509 19.135-42.644 42.651-42.644 23.524 0 42.659 19.135 42.659 42.644 0 23.525-19.135 42.675-42.659 42.675z"
|
||||
fill="#424953" />
|
||||
<path
|
||||
d="M458.531 447.866c-23.523 0-42.658-19.135-42.658-42.643 0-23.524 19.135-42.675 42.658-42.675 23.509 0 42.644 19.15 42.644 42.675 0 23.509-19.135 42.643-42.644 42.643z"
|
||||
fill="#646c77" />
|
||||
<path
|
||||
d="M458.531 351.896c-29.443 0-53.327 23.868-53.327 53.328 0 29.428 23.884 53.296 53.327 53.296 29.445 0 53.312-23.868 53.312-53.296.001-29.46-23.866-53.328-53.312-53.328zm0 85.318c-17.65 0-31.99-14.371-31.99-31.99 0-17.651 14.34-31.99 31.99-31.99 17.636 0 31.99 14.339 31.99 31.99.001 17.619-14.354 31.99-31.99 31.99z"
|
||||
fill="#424953" />
|
||||
<g fill="#e5e8ec">
|
||||
<path
|
||||
d="M469.185 405.224c0 5.873-4.765 10.652-10.653 10.652s-10.668-4.779-10.668-10.652c0-5.904 4.779-10.685 10.668-10.685s10.653 4.78 10.653 10.685zM181.28 394.539c0 5.904-4.78 10.685-10.668 10.685s-10.661-4.78-10.661-10.685c0-5.873 4.772-10.653 10.661-10.653s10.668 4.78 10.668 10.653z" />
|
||||
</g>
|
||||
<path
|
||||
d="M203.118 233.65c20.017 44.55 64.777 75.603 116.785 75.603 27.585 0 53.14-8.748 74.039-23.603-72.399-28.389-139.481-43.721-190.824-52z"
|
||||
fill="#424953" />
|
||||
<g fill="#f5ba45">
|
||||
<path
|
||||
d="M93.846 309.691H73.86l-3.226 21.321h23.212c5.889 0 10.661-4.765 10.661-10.669 0-5.873-4.772-10.652-10.661-10.652zM65.8 363.002h23.243a10.663 10.663 0 0010.668-10.669c0-5.873-4.78-10.652-10.668-10.652H69.026L65.8 363.002z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg height="32" width="32" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M42 29.59v-5.45c0-5.52-4.48-10-10-10s-10 4.48-10 10v12.73" fill="none" stroke="#b4b4b4"
|
||||
stroke-miterlimit="10" stroke-width="4" />
|
||||
<path d="M17.5 29.5h29v25h-29z" fill="#f8cf99" />
|
||||
<path d="M46 30v24H18V30h28m1-1H17v26h30V29z" fill="#cb9d61" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 359 B |
@@ -0,0 +1,67 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="32" width="32" viewBox="0 0 55 63">
|
||||
<defs>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__e">
|
||||
<feMorphology radius="2" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
|
||||
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
|
||||
<feGaussianBlur stdDeviation="1.5" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
|
||||
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
|
||||
</filter>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__g">
|
||||
<feMorphology radius="1.08" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
|
||||
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
|
||||
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
|
||||
</filter>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__i">
|
||||
<feMorphology radius="1.44" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
|
||||
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
|
||||
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
|
||||
</filter>
|
||||
<path
|
||||
d="M22.528 23C20.887 23 11 38.241 11 44.803c0 5.174 2.987 10.572 11.528 10.572 7.82 0 11.528-5.346 11.528-10.572C34.056 38.158 24.169 23 22.528 23z"
|
||||
id="prefix__a" />
|
||||
<path
|
||||
d="M10.125 5C9.253 5 4 13.238 4 16.785c0 2.797 1.587 5.715 6.125 5.715 4.155 0 6.125-2.89 6.125-5.715C16.25 13.193 10.997 5 10.125 5z"
|
||||
id="prefix__b" />
|
||||
<path
|
||||
d="M35.313 0C34.129 0 27 11.122 27 15.91c0 3.776 2.154 7.715 8.313 7.715 5.64 0 8.312-3.901 8.312-7.715 0-4.849-7.129-15.91-8.313-15.91z"
|
||||
id="prefix__c" />
|
||||
<mask id="prefix__f" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-2" y="-2" width="27.056"
|
||||
height="36.375">
|
||||
<path fill="#fff" d="M9 21h27.056v36.375H9z" />
|
||||
<use xlink:href="#prefix__a" />
|
||||
</mask>
|
||||
<mask id="prefix__h" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.08" y="-1.08"
|
||||
width="14.41" height="19.66">
|
||||
<path fill="#fff" d="M2.92 3.92h14.41v19.66H2.92z" />
|
||||
<use xlink:href="#prefix__b" />
|
||||
</mask>
|
||||
<mask id="prefix__j" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.44" y="-1.44"
|
||||
width="19.505" height="26.505">
|
||||
<path fill="#fff" d="M25.56-1.44h19.505v26.505H25.56z" />
|
||||
<use xlink:href="#prefix__c" />
|
||||
</mask>
|
||||
<rect id="prefix__d" x="-8.105" y="28.395" width="67" height="4" rx="2" />
|
||||
</defs>
|
||||
<g transform="translate(2 2)" fill="none" fill-rule="evenodd">
|
||||
<use height="100%" width="100%" xlink:href="#prefix__a" fill="#000" filter="url(#prefix__e)" />
|
||||
<use height="100%" width="100%" xlink:href="#prefix__a" fill="#55a4ff" fill-opacity=".3" />
|
||||
<use height="100%" width="100%" xlink:href="#prefix__a" mask="url(#prefix__f)" stroke="#fff" stroke-width="4" />
|
||||
</g>
|
||||
<g transform="translate(2 2)" fill="none" fill-rule="evenodd">
|
||||
<use height="100%" width="100%" xlink:href="#prefix__b" fill="#000" filter="url(#prefix__g)" />
|
||||
<use height="100%" width="100%" xlink:href="#prefix__b" fill="#55a4ff" fill-opacity=".3" />
|
||||
<use height="100%" width="100%" xlink:href="#prefix__b" mask="url(#prefix__h)" stroke="#fff"
|
||||
stroke-width="2.16" />
|
||||
</g>
|
||||
<g transform="translate(2 2)" fill="none" fill-rule="evenodd">
|
||||
<use height="100%" width="100%" xlink:href="#prefix__c" fill="#000" filter="url(#prefix__i)" />
|
||||
<use height="100%" width="100%" xlink:href="#prefix__c" fill="#55a4ff" fill-opacity=".3" />
|
||||
<use height="100%" width="100%" xlink:href="#prefix__c" mask="url(#prefix__j)" stroke="#fff"
|
||||
stroke-width="2.88" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496.166 496.166">
|
||||
<path
|
||||
d="M.005 248.087C.005 111.063 111.073 0 248.079 0c137.014 0 248.082 111.062 248.082 248.087 0 137.002-111.068 248.079-248.082 248.079C111.073 496.166.005 385.089.005 248.087z"
|
||||
fill="#32bea6" />
|
||||
<path
|
||||
d="M400.813 169.581c-2.502-4.865-14.695-16.012-35.262-5.891-20.564 10.122-10.625 32.351-10.625 32.351 7.666 15.722 11.98 33.371 11.98 52.046 0 65.622-53.201 118.824-118.828 118.824-65.619 0-118.82-53.202-118.82-118.824 0-61.422 46.6-111.946 106.357-118.173v30.793s-.084 1.836 1.828 2.999c1.906 1.163 3.818 0 3.818 0l98.576-58.083s2.211-1.162 2.211-3.436c0-1.873-2.211-3.205-2.211-3.205l-98.248-57.754s-2.24-1.605-4.23-.826c-1.988.773-1.744 3.481-1.744 3.481v32.993c-88.998 6.392-159.23 80.563-159.23 171.21 0 94.824 76.873 171.696 171.693 171.696 94.828 0 171.707-76.872 171.707-171.696.001-28.298-6.852-54.98-18.972-78.505z"
|
||||
fill="#f7f7f7" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 967 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 12H2A10 10 0 0112 2z" fill="#767676" />
|
||||
<path d="M12 12V2A10 10 0 112 12h10z" opacity=".5" />
|
||||
<path d="M12 12V2c5.523 0 10 4.477 10 10H12zM12 12h10c0 5.523-4.477 10-10 10V12zM12 12v10C6.477 22 2 17.523 2 12h10z"
|
||||
fill="#767676" /><text style="white-space:pre" x="6.187" y="10.486" fill="#fff" font-family="Arial,sans-serif"
|
||||
font-size="9" font-weight="700">1</text><text style="white-space:pre" x="37.928" y="7.522" fill="#333"
|
||||
font-family="Arial,sans-serif" font-size=".9">Enter your text here</text><text style="white-space:pre" x="12.861"
|
||||
y="10.486" fill="#fff" font-family="Arial,sans-serif" font-size="9" font-weight="700">2</text><text
|
||||
style="white-space:pre" x="6.187" y="19.585" fill="#fff" font-family="Arial,sans-serif" font-size="9"
|
||||
font-weight="700">3</text><text style="white-space:pre" x="12.861" y="19.585" fill="#fff"
|
||||
font-family="Arial,sans-serif" font-size="9" font-weight="700">4</text>
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.worxlandroid</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Worx Landroid Binding</name>
|
||||
<properties>
|
||||
<bnd.importpackage>!org.graalvm.nativeimage.hosted</bnd.importpackage>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk.iotdevicesdk</groupId>
|
||||
<artifactId>aws-iot-device-sdk</artifactId>
|
||||
<version>1.30.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk.crt</groupId>
|
||||
<artifactId>aws-crt</artifactId>
|
||||
<version>0.44.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.worxlandroid-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-worxlandroid" description="Worx Landroid Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle dependency="true">mvn:org.openhab.osgiify/software.amazon.awssdk.crt.aws-crt/0.44.0</bundle>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.worxlandroid/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link WorxLandroidBindingConstants} class defines datCommon constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WorxLandroidBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "worxlandroid";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge");
|
||||
public static final ThingTypeUID THING_TYPE_MOWER = new ThingTypeUID(BINDING_ID, "mower");
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_MOWER);
|
||||
|
||||
// Channel group ids
|
||||
public static final String GROUP_COMMON = "common";
|
||||
public static final String GROUP_CONFIG = "config";
|
||||
public static final String GROUP_MULTI_ZONES = "multi-zones";
|
||||
public static final String GROUP_SCHEDULE = "schedule";
|
||||
public static final String GROUP_ONE_TIME = "one-time";
|
||||
public static final String GROUP_BATTERY = "battery";
|
||||
public static final String GROUP_ORIENTATION = "orientation";
|
||||
public static final String GROUP_METRICS = "metrics";
|
||||
public static final String GROUP_RAIN = "rain";
|
||||
public static final String GROUP_WIFI = "wifi";
|
||||
public static final String GROUP_AWS = "aws";
|
||||
|
||||
// Configuration constants
|
||||
public static final String SERIAL_NUMBER = "serialNumber";
|
||||
|
||||
// List channel ids
|
||||
// common
|
||||
public static final String CHANNEL_ONLINE_TIMESTAMP = "online-timestamp";
|
||||
public static final String CHANNEL_ACTION = "action";
|
||||
public static final String CHANNEL_ENABLE = "enable";
|
||||
public static final String CHANNEL_ONLINE = "online";
|
||||
public static final String CHANNEL_LOCK = "lock";
|
||||
public static final String CHANNEL_RSSI = "rssi";
|
||||
|
||||
// AWS
|
||||
public static final String CHANNEL_POLL = "poll";
|
||||
public static final String CHANNEL_CONNECTED = "connected";
|
||||
|
||||
// cfgCommon
|
||||
public static final String CHANNEL_TIMESTAMP = "timestamp";
|
||||
public static final String CHANNEL_COMMAND = "command";
|
||||
public static final String CHANNEL_DELAY = "delay";
|
||||
|
||||
// cfgSc
|
||||
public static final String CHANNEL_TIME_EXTENSION = "time-extension";
|
||||
public static final String CHANNEL_MODE = "mode";
|
||||
public static final String CHANNEL_START = "next-start";
|
||||
public static final String CHANNEL_STOP = "next-stop";
|
||||
|
||||
// cfgScXXXday
|
||||
public static final String CHANNEL_DURATION = "duration";
|
||||
public static final String CHANNEL_EDGECUT = "edgecut";
|
||||
public static final String CHANNEL_TIME = "time";
|
||||
|
||||
// datCommon
|
||||
public static final String CHANNEL_WIFI_QUALITY = "wifi-quality";
|
||||
public static final String CHANNEL_LAST_ZONE = "last-zone";
|
||||
public static final String CHANNEL_STATUS_CODE = "status";
|
||||
public static final String CHANNEL_ERROR_CODE = "error";
|
||||
|
||||
// datBattery
|
||||
public static final String CHANNEL_TEMPERATURE = "temperature";
|
||||
public static final String CHANNEL_VOLTAGE = "voltage";
|
||||
public static final String CHANNEL_LEVEL = "level";
|
||||
public static final String CHANNEL_CHARGE_CYCLES = "charge-cycles";
|
||||
public static final String CHANNEL_CHARGE_CYCLES_TOTAL = "charge-cycles-total";
|
||||
public static final String CHANNEL_CHARGING = "charging";
|
||||
|
||||
// datDmp
|
||||
public static final String CHANNEL_PITCH = "pitch";
|
||||
public static final String CHANNEL_ROLL = "roll";
|
||||
public static final String CHANNEL_YAW = "yaw";
|
||||
|
||||
// datSt
|
||||
public static final String CHANNEL_BLADE_TIME = "blade-time";
|
||||
public static final String CHANNEL_BLADE_TIME_TOTAL = "blade-time-total";
|
||||
public static final String CHANNEL_DISTANCE = "distance";
|
||||
public static final String CHANNEL_TOTAL_TIME = "total-time";
|
||||
|
||||
// datRain
|
||||
public static final String CHANNEL_RAIN_STATE = "state";
|
||||
public static final String CHANNEL_RAIN_COUNTER = "counter";
|
||||
|
||||
public static final String CHANNEL_PREFIX_ALLOCATION = "allocation-%d";
|
||||
public static final String CHANNEL_PREFIX_ZONE = "zone-%d";
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal;
|
||||
|
||||
import static org.openhab.binding.worxlandroid.internal.WorxLandroidBindingConstants.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.worxlandroid.internal.api.WorxApiHandler;
|
||||
import org.openhab.binding.worxlandroid.internal.discovery.MowerDiscoveryService;
|
||||
import org.openhab.binding.worxlandroid.internal.handler.WorxLandroidBridgeHandler;
|
||||
import org.openhab.binding.worxlandroid.internal.handler.WorxLandroidMowerHandler;
|
||||
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.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link WorxLandroidHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
* @author Gaël L'hopital - Added oAuthFactory
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.worxlandroid", service = ThingHandlerFactory.class)
|
||||
public class WorxLandroidHandlerFactory extends BaseThingHandlerFactory {
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_MOWER, THING_TYPE_BRIDGE);
|
||||
|
||||
private final Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
|
||||
private final OAuthFactory oAuthFactory;
|
||||
private final WorxApiHandler worxApiHandler;
|
||||
private final TimeZoneProvider timeZoneProvider;
|
||||
|
||||
@Activate
|
||||
public WorxLandroidHandlerFactory(final @Reference OAuthFactory oAuthFactory,
|
||||
final @Reference WorxApiHandler worxApiHandler, final @Reference TimeZoneProvider timeZoneProvider) {
|
||||
this.oAuthFactory = oAuthFactory;
|
||||
this.worxApiHandler = worxApiHandler;
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (THING_TYPE_BRIDGE.equals(thingTypeUID)) {
|
||||
WorxLandroidBridgeHandler bridgeHandler = new WorxLandroidBridgeHandler((Bridge) thing, worxApiHandler,
|
||||
oAuthFactory);
|
||||
MowerDiscoveryService discoveryService = new MowerDiscoveryService(bridgeHandler);
|
||||
discoveryServiceRegs.put(thing.getUID(), bundleContext.registerService(DiscoveryService.class.getName(),
|
||||
discoveryService, new Hashtable<>()));
|
||||
|
||||
return bridgeHandler;
|
||||
} else if (THING_TYPE_MOWER.equals(thingTypeUID)) {
|
||||
return new WorxLandroidMowerHandler(thing, worxApiHandler.getDeserializer(), timeZoneProvider);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeHandler(ThingHandler handler) {
|
||||
if (handler instanceof WorxLandroidBridgeHandler) {
|
||||
ServiceRegistration<?> serviceReg = discoveryServiceRegs.remove(handler.getThing().getUID());
|
||||
if (serviceReg != null) {
|
||||
serviceReg.unregister();
|
||||
}
|
||||
}
|
||||
super.removeHandler(handler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal;
|
||||
|
||||
import static org.openhab.binding.worxlandroid.internal.WorxLandroidBindingConstants.BINDING_ID;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.ui.icon.IconProvider;
|
||||
import org.openhab.core.ui.icon.IconSet;
|
||||
import org.openhab.core.ui.icon.IconSet.Format;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link WorxLandroidIconProvider} is the class providing binding related icons.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@Component(service = { IconProvider.class, WorxLandroidIconProvider.class })
|
||||
@NonNullByDefault
|
||||
public class WorxLandroidIconProvider implements IconProvider {
|
||||
private static final String DEFAULT_LABEL = "Worx Landroid Icons";
|
||||
private static final String DEFAULT_DESCRIPTION = "Icons illustrating channels provided by Worx Landroid binding.";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WorxLandroidIconProvider.class);
|
||||
private final BundleContext context;
|
||||
private final TranslationProvider i18nProvider;
|
||||
|
||||
@Activate
|
||||
public WorxLandroidIconProvider(final BundleContext context, final @Reference TranslationProvider i18nProvider) {
|
||||
this.context = context;
|
||||
this.i18nProvider = i18nProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<IconSet> getIconSets() {
|
||||
return getIconSets(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<IconSet> getIconSets(@Nullable Locale locale) {
|
||||
String label = getText("label", DEFAULT_LABEL, locale);
|
||||
String description = getText("decription", DEFAULT_DESCRIPTION, locale);
|
||||
|
||||
return Set.of(new IconSet(BINDING_ID, label, description, Set.of(Format.SVG)));
|
||||
}
|
||||
|
||||
private String getText(String entry, String defaultValue, @Nullable Locale locale) {
|
||||
String text = defaultValue;
|
||||
if (locale != null) {
|
||||
text = i18nProvider.getText(context.getBundle(), "iconset." + entry, defaultValue, locale);
|
||||
text = text == null ? defaultValue : text;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Integer hasIcon(String category, String iconSetId, Format format) {
|
||||
return iconSetId.equals(BINDING_ID) && format == Format.SVG ? 0 : null;
|
||||
}
|
||||
|
||||
public @Nullable InputStream getIcon(String category, String state) {
|
||||
return getIcon(category, BINDING_ID, state, Format.SVG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable InputStream getIcon(String category, String iconSetId, @Nullable String state, Format format) {
|
||||
String icon = getResource(category, true);
|
||||
if (icon.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (state != null) {
|
||||
String withState = "%s-%s".formatted(category, state.toString().toLowerCase(Locale.ROOT));
|
||||
String iconWithState = getResource(withState, false);
|
||||
if (!iconWithState.isEmpty()) {
|
||||
icon = iconWithState;
|
||||
}
|
||||
}
|
||||
|
||||
return new ByteArrayInputStream(icon.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
private String getResource(String iconName, boolean shouldExist) {
|
||||
String result = "";
|
||||
|
||||
URL iconResource = context.getBundle().getEntry("icon/%s.svg".formatted(iconName));
|
||||
if (iconResource != null) {
|
||||
try (InputStream stream = iconResource.openStream()) {
|
||||
result = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
logger.warn("Unable to load resource '{}': {}", iconResource.getPath(), e.getMessage());
|
||||
}
|
||||
} else if (shouldExist) {
|
||||
logger.warn("Unable to find icon '{}'", iconName);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.api;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link WebApiException} is a class for handling the Worx Landroid API exceptions
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WebApiException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final int UNKNOWN = 0;
|
||||
|
||||
private final int errorCode;
|
||||
|
||||
public WebApiException(int errorCode, String errorMsg) {
|
||||
super(errorMsg);
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public WebApiException(String errorMsg, Throwable cause) {
|
||||
super(errorMsg);
|
||||
this.errorCode = UNKNOWN;
|
||||
}
|
||||
|
||||
public WebApiException(String errorMsg) {
|
||||
super(errorMsg);
|
||||
this.errorCode = UNKNOWN;
|
||||
}
|
||||
|
||||
public WebApiException(Throwable cause) {
|
||||
super(cause.getMessage(), cause);
|
||||
this.errorCode = UNKNOWN;
|
||||
}
|
||||
|
||||
public int getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.api;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* The {@link WorxApiDeserializer} is responsible to instantiate suitable Gson (de)serializer
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = WorxApiDeserializer.class)
|
||||
public class WorxApiDeserializer {
|
||||
private static final DateTimeFormatter WORX_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ssX");
|
||||
|
||||
private final Gson gson;
|
||||
|
||||
@Activate
|
||||
public WorxApiDeserializer() {
|
||||
gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||
.registerTypeAdapter(ZoneId.class,
|
||||
(JsonDeserializer<ZoneId>) (json, type, context) -> ZoneId
|
||||
.of(json.getAsJsonPrimitive().getAsString()))
|
||||
.registerTypeAdapter(Instant.class, (JsonDeserializer<Instant>) (json, type, context) -> {
|
||||
String value = json.getAsString();
|
||||
OffsetDateTime odt;
|
||||
try {
|
||||
odt = OffsetDateTime.parse(value, WORX_FORMATTER);
|
||||
} catch (DateTimeParseException exc) {
|
||||
odt = OffsetDateTime.parse(value + 'Z', WORX_FORMATTER);
|
||||
}
|
||||
return odt.toInstant();
|
||||
}).registerTypeAdapter(Boolean.class, (JsonDeserializer<Boolean>) (json, type, context) -> {
|
||||
String value = json.getAsJsonPrimitive().getAsString().toUpperCase(Locale.ROOT);
|
||||
return "1".equals(value);
|
||||
}).create();
|
||||
}
|
||||
|
||||
public String toJson(Object object) {
|
||||
return gson.toJson(object);
|
||||
}
|
||||
|
||||
public Map<String, String> toMap(Object object) {
|
||||
Map<String, String> fromObject = gson.fromJson(toJson(object), new TypeToken<HashMap<String, String>>() {
|
||||
}.getType());
|
||||
return fromObject != null ? Map.copyOf(fromObject) : Map.of();
|
||||
}
|
||||
|
||||
public <T> T deserialize(Type typeToken, String json) throws WebApiException {
|
||||
try {
|
||||
T result = gson.fromJson(json, typeToken);
|
||||
if (result == null) {
|
||||
throw new WebApiException("Deserialization of '%s' resulted in null value".formatted(json));
|
||||
}
|
||||
return result;
|
||||
} catch (JsonSyntaxException e) {
|
||||
throw new WebApiException("Unexpected error deserializing '%s' : %s".formatted(json, e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.api;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.ProductItemStatus;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.UsersMeResponse;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* {@link WorxApiHandler} is a API request
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = WorxApiHandler.class)
|
||||
public class WorxApiHandler {
|
||||
private static final String URL_BASE = "https://api.worxlandroid.com/api/v2/";
|
||||
private static final String URL_PRODUCT_ITEMS = URL_BASE + "product-items";
|
||||
private static final String URL_USERS_ME = URL_BASE + "users/me";
|
||||
private static final int TIMEOUT_S = 15;
|
||||
|
||||
private static final Type PRODUCT_ITEM_STATUS_LIST = new TypeToken<List<ProductItemStatus>>() {
|
||||
}.getType();
|
||||
private static final Type PRODUCT_ITEM_STATUS = new TypeToken<ProductItemStatus>() {
|
||||
}.getType();
|
||||
private static final Type USERS_ME = new TypeToken<UsersMeResponse>() {
|
||||
}.getType();
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WorxApiHandler.class);
|
||||
private final HttpClient httpClient;
|
||||
private final WorxApiDeserializer deserializer;
|
||||
|
||||
@Activate
|
||||
public WorxApiHandler(final @Reference HttpClientFactory httpClientFactory,
|
||||
final @Reference WorxApiDeserializer deserializer) {
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
this.deserializer = deserializer;
|
||||
}
|
||||
|
||||
private Request buildRequest(String url, String accessToken, HttpMethod method) {
|
||||
Request request = httpClient.newRequest(url).method(method);
|
||||
request.header(HttpHeader.AUTHORIZATION, "Bearer %s".formatted(accessToken));
|
||||
request.header(HttpHeader.CONTENT_TYPE, "application/json; utf-8");
|
||||
request.timeout(TIMEOUT_S, TimeUnit.SECONDS);
|
||||
return request;
|
||||
}
|
||||
|
||||
private <T> T apiGet(String url, String accessToken, Type type) throws WebApiException {
|
||||
Request request = buildRequest(url, accessToken, HttpMethod.GET);
|
||||
|
||||
logger.debug("URI: {}", request.getURI().toString());
|
||||
try {
|
||||
ContentResponse response = request.send();
|
||||
if (response.getStatus() == HttpStatus.OK_200) {
|
||||
String result = response.getContentAsString();
|
||||
logger.trace("Worx Landroid Api Response: {}", result);
|
||||
return deserializer.deserialize(type, result);
|
||||
}
|
||||
throw new WebApiException(
|
||||
"Error calling Worx Landroid Api! HTTP Status = %d".formatted(response.getStatus()));
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new WebApiException(e);
|
||||
} catch (TimeoutException | ExecutionException e) {
|
||||
throw new WebApiException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean apiPost(String url, String accessToken) {
|
||||
Request request = buildRequest(url, accessToken, HttpMethod.POST);
|
||||
|
||||
logger.debug("URI: {}", request.getURI().toString());
|
||||
try {
|
||||
return request.send().getStatus() == HttpStatus.OK_200;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
logger.error("Interrupted while posting at {}: {}", request.getURI().toString(), e.getMessage());
|
||||
} catch (TimeoutException | ExecutionException e) {
|
||||
logger.error("Error posting at {}: {}", request.getURI().toString(), e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public WorxApiDeserializer getDeserializer() {
|
||||
return deserializer;
|
||||
}
|
||||
|
||||
public List<ProductItemStatus> retrieveDeviceStatus(String token) throws WebApiException {
|
||||
return apiGet("%s?status=1".formatted(URL_PRODUCT_ITEMS), token, PRODUCT_ITEM_STATUS_LIST);
|
||||
}
|
||||
|
||||
public ProductItemStatus retrieveDeviceStatus(String token, String serialNumber) throws WebApiException {
|
||||
return apiGet("%s/%s?status=1".formatted(URL_PRODUCT_ITEMS, serialNumber), token, PRODUCT_ITEM_STATUS);
|
||||
}
|
||||
|
||||
public UsersMeResponse retrieveMe(String token) throws WebApiException {
|
||||
return apiGet(URL_USERS_ME, token, USERS_ME);
|
||||
}
|
||||
|
||||
public boolean resetBladeTime(String token, String serialNumber) {
|
||||
return apiPost("%s/%s/counters/blade/reset".formatted(URL_PRODUCT_ITEMS, serialNumber), token);
|
||||
}
|
||||
|
||||
public boolean resetBatteryCycles(String token, String serialNumber) {
|
||||
return apiPost("%s/%s/counters/battery/reset".formatted(URL_PRODUCT_ITEMS, serialNumber), token);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.api.dto;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.worxlandroid.internal.codes.WorxLandroidActionCodes;
|
||||
|
||||
/**
|
||||
* The {@link Commands} class hold record definition of Commands send to API
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Commands {
|
||||
private record OTS(OTSCommand ots) {
|
||||
}
|
||||
|
||||
private record OTSCommand( //
|
||||
int bc, // bordercut
|
||||
int wtm // work time minutes
|
||||
) {
|
||||
}
|
||||
|
||||
public record OneTimeCommand(OTS sc) {
|
||||
public OneTimeCommand(int bc, int wtm) {
|
||||
this(new OTS(new OTSCommand(bc, wtm)));
|
||||
}
|
||||
}
|
||||
|
||||
private record ScheduleDaysP(int p, Object d, @Nullable Object dd) {
|
||||
}
|
||||
|
||||
public record ScheduleDaysCommand(ScheduleDaysP sc) {
|
||||
public ScheduleDaysCommand(int p, Object[] d, Object[] dd) {
|
||||
this(new ScheduleDaysP(p, d, dd));
|
||||
}
|
||||
|
||||
public ScheduleDaysCommand(int p, Object[] d) {
|
||||
this(new ScheduleDaysP(p, d, null));
|
||||
}
|
||||
}
|
||||
|
||||
private record ScheduleCommandMode(int m) {
|
||||
}
|
||||
|
||||
public record ScheduleCommand(ScheduleCommandMode sc) {
|
||||
public ScheduleCommand(int m) {
|
||||
this(new ScheduleCommandMode(m));
|
||||
}
|
||||
}
|
||||
|
||||
public record MowerCommand(int cmd) {
|
||||
public MowerCommand(WorxLandroidActionCodes actionCode) {
|
||||
this(actionCode.code);
|
||||
}
|
||||
}
|
||||
|
||||
public record ZoneMeterCommand(int[] mz) {
|
||||
}
|
||||
|
||||
public record ZoneMeterAlloc(int[] mzv) {
|
||||
}
|
||||
|
||||
public record SetRainDelay(int rd) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.api.dto;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
public class LastStatus {
|
||||
public Instant timestamp;
|
||||
public Payload payload;
|
||||
|
||||
public LastStatus(Payload payload) {
|
||||
this.payload = payload;
|
||||
this.timestamp = Instant.now();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.api.dto;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
import org.openhab.binding.worxlandroid.internal.codes.WorxLandroidErrorCodes;
|
||||
import org.openhab.binding.worxlandroid.internal.codes.WorxLandroidStatusCodes;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
public class Payload {
|
||||
public class US {
|
||||
public int enabled;
|
||||
public String stat;
|
||||
}
|
||||
|
||||
public class Ots {
|
||||
@SerializedName("wtm")
|
||||
public int duration = -1;
|
||||
private int bc = -1;
|
||||
|
||||
public boolean getEdgeCut() {
|
||||
return bc == 1;
|
||||
}
|
||||
}
|
||||
|
||||
public class Al {
|
||||
public int lvl;
|
||||
public int t;
|
||||
}
|
||||
|
||||
public class Modules {
|
||||
@SerializedName("US")
|
||||
public US uS;
|
||||
}
|
||||
|
||||
public class Rain {
|
||||
@SerializedName("s")
|
||||
public Boolean raining;
|
||||
@SerializedName("cnt")
|
||||
public int counter = -1;
|
||||
}
|
||||
|
||||
public class Schedule {
|
||||
public static enum Mode {
|
||||
@SerializedName("1")
|
||||
NORMAL,
|
||||
@SerializedName("2")
|
||||
PARTY,
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
@SerializedName("m")
|
||||
public Mode scheduleMode = Mode.UNKNOWN;
|
||||
@SerializedName("p")
|
||||
public int timeExtension = -1;
|
||||
public int distm;
|
||||
public Ots ots;
|
||||
public List<List<String>> d;
|
||||
public List<List<String>> dd;
|
||||
}
|
||||
|
||||
public class Battery {
|
||||
@SerializedName("t")
|
||||
public double temp = -1;
|
||||
@SerializedName("v")
|
||||
public double voltage = -1;
|
||||
@SerializedName("p")
|
||||
public int level = -1;
|
||||
@SerializedName("nr")
|
||||
public int chargeCycle = -1;
|
||||
@SerializedName("c")
|
||||
public Boolean charging;
|
||||
public int m;
|
||||
}
|
||||
|
||||
public class Stat {
|
||||
@SerializedName("b")
|
||||
public int bladeWorkTime = -1;
|
||||
@SerializedName("d")
|
||||
public int distanceCovered = -1;
|
||||
@SerializedName("wt")
|
||||
public int mowerWorkTime = -1;
|
||||
@SerializedName("bl")
|
||||
public int lawnPerimeter;
|
||||
}
|
||||
|
||||
public class Cfg {
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
|
||||
|
||||
private String dt = ""; // "dt": "13/03/2020",
|
||||
private String tm = ""; // "tm": "17:09:34"
|
||||
|
||||
public int id = -1;
|
||||
public String lg = ""; // en, fr...
|
||||
public int cmd = -1;
|
||||
public Schedule sc;
|
||||
@SerializedName("mz")
|
||||
public List<Integer> multiZones = List.of();
|
||||
@SerializedName("mzv")
|
||||
public List<Integer> multizoneAllocations = List.of();
|
||||
@SerializedName("rd")
|
||||
public int rainDelay = -1;
|
||||
@SerializedName("sn")
|
||||
public String serialNumber = "";
|
||||
public int mzk;
|
||||
public Al al;
|
||||
public int tq;
|
||||
public Modules modules;
|
||||
|
||||
public Instant getDateTime() {
|
||||
if (dt.isEmpty() || tm.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LocalDateTime localDateTime = LocalDateTime.parse("%s %s".formatted(dt, tm), FORMATTER);
|
||||
return localDateTime.atZone(ZoneOffset.UTC).toInstant();
|
||||
}
|
||||
}
|
||||
|
||||
public class Dat {
|
||||
public static enum Axis {
|
||||
// Don't change order - ordinal is used
|
||||
PITCH,
|
||||
ROLL,
|
||||
YAW;
|
||||
}
|
||||
|
||||
private int lk = -1;
|
||||
@SerializedName("dmp")
|
||||
private double[] dataMotionProcessor = { -1, -1, -1 }; // pitch, roll, yaw
|
||||
|
||||
public String mac = "";
|
||||
public String fw = "";
|
||||
@SerializedName("bt")
|
||||
public Battery battery;
|
||||
public Stat st;
|
||||
@SerializedName("ls")
|
||||
public WorxLandroidStatusCodes statusCode = WorxLandroidStatusCodes.UNKNOWN;
|
||||
@SerializedName("le")
|
||||
public WorxLandroidErrorCodes errorCode = WorxLandroidErrorCodes.UNKNOWN;
|
||||
@SerializedName("lz")
|
||||
public int lastZone = -1;
|
||||
@SerializedName("rsi")
|
||||
public int wifiQuality;
|
||||
public int fwb;
|
||||
public String conn;
|
||||
public int act;
|
||||
public int tr;
|
||||
public Rain rain;
|
||||
public Modules modules;
|
||||
|
||||
public boolean isLocked() {
|
||||
return lk == 1;
|
||||
}
|
||||
|
||||
public double getAngle(Axis axis) {
|
||||
return dataMotionProcessor[axis.ordinal()];
|
||||
}
|
||||
}
|
||||
|
||||
public Cfg cfg;
|
||||
public Dat dat;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.api.dto;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The {@link ProductItemStatus} class
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class ProductItemStatus {
|
||||
|
||||
public class Accessories {
|
||||
public boolean ultrasonic;
|
||||
}
|
||||
|
||||
public class MqttTopics {
|
||||
public String commandIn;
|
||||
public String commandOut;
|
||||
}
|
||||
|
||||
public class SetupLocation {
|
||||
public double latitude;
|
||||
public double longitude;
|
||||
}
|
||||
|
||||
public class AppSettings {
|
||||
boolean cellularSetupCompleted;
|
||||
}
|
||||
|
||||
public class City {
|
||||
public int id;
|
||||
public int countryId;
|
||||
public String name;
|
||||
public double latitude;
|
||||
public double longitude;
|
||||
public String createdAt;
|
||||
public String updatedAt;
|
||||
}
|
||||
|
||||
public class Sim {
|
||||
public int id;
|
||||
public String iccid;
|
||||
public String simStatus;
|
||||
public boolean pendingActivation;
|
||||
public Instant contractStartsAt;
|
||||
public Instant contractEndsAt;
|
||||
public Instant createdAt;
|
||||
public Instant updatedAt;
|
||||
}
|
||||
|
||||
public class AutoSchedule {
|
||||
public int boost;
|
||||
public String grassType;
|
||||
public boolean irrigation;
|
||||
public Map<String, String> nutrition;
|
||||
public String soilType;
|
||||
}
|
||||
|
||||
public String id;
|
||||
public String uuid;
|
||||
public int productId;
|
||||
public String userId;
|
||||
public String serialNumber;
|
||||
public String macAddress;
|
||||
public String name;
|
||||
public boolean locked;
|
||||
public String firmwareVersion;
|
||||
public boolean firmwareAutoUpgrade;
|
||||
public boolean pushNotifications;
|
||||
public Sim sim;
|
||||
public String pushNotificationsLevel;
|
||||
public boolean test;
|
||||
public boolean iotRegistered;
|
||||
public boolean mqttRegistered;
|
||||
public String pinCode;
|
||||
public String registeredAt;
|
||||
public boolean online;
|
||||
public String mqttEndpoint;
|
||||
public AppSettings appSettings;
|
||||
public int protocol;
|
||||
public String pendingRadioLinkValidation;
|
||||
public List<String> capabilities;
|
||||
public List<String> capabilitiesAvailable;
|
||||
public Accessories accessories;
|
||||
public MqttTopics mqttTopics;
|
||||
public boolean warrantyRegistered;
|
||||
public String purchasedAt;
|
||||
public String warrantyExpiresAt;
|
||||
public SetupLocation setupLocation;
|
||||
public City city;
|
||||
public ZoneId timeZone;
|
||||
public double lawnSize;
|
||||
public double lawnPerimeter;
|
||||
public AutoSchedule autoScheduleSettings;
|
||||
public boolean autoSchedule;
|
||||
public boolean improvement;
|
||||
public boolean diagnostic;
|
||||
public long distanceCovered;
|
||||
public long mowerWorkTime;
|
||||
|
||||
public long bladeWorkTime;
|
||||
public long bladeWorkTimeReset;
|
||||
public Instant bladeWorkTimeResetAt;
|
||||
|
||||
public int batteryChargeCycles;
|
||||
public int batteryChargeCyclesReset;
|
||||
public Instant batteryChargeCyclesResetAt;
|
||||
|
||||
public Instant createdAt;
|
||||
public Instant updatedAt;
|
||||
public LastStatus lastStatus;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.api.dto;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link UsersMeResponse} class
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class UsersMeResponse {
|
||||
public String id = "";
|
||||
public String userType = "";
|
||||
public boolean pushNotifications;
|
||||
public String location = "";
|
||||
public String actionsOnGooglePinCode = "";
|
||||
public String createdAt = "";
|
||||
public String updatedAt = "";
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.codes;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link WorxLandroidActionCodes} hosts action codes
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum WorxLandroidActionCodes {
|
||||
START(1, "start"),
|
||||
STOP(2, "stop"),
|
||||
HOME(3, "home"),
|
||||
ZONETRAINING(4, "zonetraining"),
|
||||
LOCK(5, "lock"),
|
||||
UNLOCK(6, "unlock");
|
||||
|
||||
public final int code;
|
||||
public final String description;
|
||||
|
||||
WorxLandroidActionCodes(int code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.codes;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.format.TextStyle;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link WorxLandroidDayCodes} hosts Landroid days of week
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum WorxLandroidDayCodes {
|
||||
SUNDAY(0, DayOfWeek.SUNDAY),
|
||||
MONDAY(1, DayOfWeek.MONDAY),
|
||||
TUESDAY(2, DayOfWeek.TUESDAY),
|
||||
WEDNESDAY(3, DayOfWeek.WEDNESDAY),
|
||||
THURSDAY(4, DayOfWeek.THURSDAY),
|
||||
FRIDAY(5, DayOfWeek.FRIDAY),
|
||||
SATURDAY(6, DayOfWeek.SATURDAY);
|
||||
|
||||
public final int code;
|
||||
public final DayOfWeek dayOfWeek;
|
||||
|
||||
WorxLandroidDayCodes(int code, DayOfWeek dayOfWeek) {
|
||||
this.code = code;
|
||||
this.dayOfWeek = dayOfWeek;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return dayOfWeek.getDisplayName(TextStyle.FULL, Locale.US);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.codes;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* The {@link WorxLandroidErrorCodes} hosts error codes
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum WorxLandroidErrorCodes {
|
||||
@SerializedName("-1")
|
||||
UNKNOWN,
|
||||
@SerializedName("0")
|
||||
NO_ERR,
|
||||
@SerializedName("1")
|
||||
TRAPPED,
|
||||
@SerializedName("2")
|
||||
LIFTED,
|
||||
@SerializedName("3")
|
||||
WIRE_MISSING,
|
||||
@SerializedName("4")
|
||||
OUTSIDE_WIRE,
|
||||
@SerializedName("5")
|
||||
RAINING,
|
||||
@SerializedName("6")
|
||||
CLOSE_DOOR_TO_MOW,
|
||||
@SerializedName("7")
|
||||
CLOSE_DOOR_TO_GO_HOME,
|
||||
@SerializedName("8")
|
||||
BLADE_MOTOR_BLOCKED,
|
||||
@SerializedName("9")
|
||||
WHEEL_MOTOR_BLOCKED,
|
||||
@SerializedName("10")
|
||||
TRAPPED_TIMEOUT,
|
||||
@SerializedName("11")
|
||||
UPSIDE_DOWN,
|
||||
@SerializedName("12")
|
||||
BATTERY_LOW,
|
||||
@SerializedName("13")
|
||||
REVERSE_WIRE,
|
||||
@SerializedName("14")
|
||||
CHARGE_ERROR,
|
||||
@SerializedName("15")
|
||||
TIMEOUT_FINDING_HOME,
|
||||
@SerializedName("16")
|
||||
MOWER_LOCKED,
|
||||
@SerializedName("17")
|
||||
BATTERY_OVER_TEMPERATURE,
|
||||
@SerializedName("20")
|
||||
MOWER_OUTSIDE_WIRE;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.codes;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* The {@link WorxLandroidStatusCodes} hosts status codes
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum WorxLandroidStatusCodes {
|
||||
@SerializedName("-1")
|
||||
UNKNOWN,
|
||||
@SerializedName("0")
|
||||
IDLE,
|
||||
@SerializedName("1")
|
||||
HOME,
|
||||
@SerializedName("2")
|
||||
START_SEQUENCE,
|
||||
@SerializedName("3")
|
||||
LEAVING_HOME,
|
||||
@SerializedName("4")
|
||||
FOLLOW_WIRE,
|
||||
@SerializedName("5")
|
||||
SEARCHING_HOME,
|
||||
@SerializedName("6")
|
||||
SEARCHING_WIRE,
|
||||
@SerializedName("7")
|
||||
MOWING,
|
||||
@SerializedName("8")
|
||||
LIFTED,
|
||||
@SerializedName("9")
|
||||
TRAPPED,
|
||||
@SerializedName("10")
|
||||
BLADE_BLOCKED,
|
||||
@SerializedName("11")
|
||||
DEBUG,
|
||||
@SerializedName("12")
|
||||
REMOTE_CONTROL,
|
||||
@SerializedName("13")
|
||||
ESCAPE_FROM_OLM,
|
||||
@SerializedName("30")
|
||||
GOING_HOME,
|
||||
@SerializedName("31")
|
||||
ZONE_TRAINING,
|
||||
@SerializedName("32")
|
||||
BORDER_CUT,
|
||||
@SerializedName("33")
|
||||
SEARCHING_ZONE,
|
||||
@SerializedName("34")
|
||||
PAUSE,
|
||||
@SerializedName("99")
|
||||
MANUAL_STOP;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link MowerConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
* @author Gaël L'hopital - Added serialNumber configuration element
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class MowerConfiguration {
|
||||
public String serialNumber = "";
|
||||
public int refreshStatusInterval = 600;
|
||||
public int pollingInterval = 3600;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MowerConfiguration [serialNumber='%s', pollingInterval='%d', refreshStatusInterval='%d']"
|
||||
.formatted(serialNumber, pollingInterval, refreshStatusInterval);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link WebApiConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
* @author Gaël L'hopital - Added NonNullByDefault, removed setters, removed reconnectInterval
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WebApiConfiguration {
|
||||
public String username = "";
|
||||
public String password = "";
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WebApiConfiguration [username='%s', password='*****']".formatted(username);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.worxlandroid.internal.WorxLandroidBindingConstants.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.worxlandroid.internal.WorxLandroidBindingConstants;
|
||||
import org.openhab.binding.worxlandroid.internal.api.WebApiException;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.ProductItemStatus;
|
||||
import org.openhab.binding.worxlandroid.internal.handler.WorxLandroidBridgeHandler;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link MowerDiscoveryService} is a service for discovering your mowers through Worx Landroid API
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
* @author Gaël L'hopital - Added representation property and serialNumber configuration element
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class MowerDiscoveryService extends AbstractDiscoveryService {
|
||||
/**
|
||||
* Maximum time to search for devices in seconds.
|
||||
*/
|
||||
private static final int SEARCH_TIME_SEC = 20;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MowerDiscoveryService.class);
|
||||
private final WorxLandroidBridgeHandler bridgeHandler;
|
||||
|
||||
public MowerDiscoveryService(WorxLandroidBridgeHandler bridgeHandler) {
|
||||
super(WorxLandroidBindingConstants.SUPPORTED_THING_TYPES, SEARCH_TIME_SEC);
|
||||
this.bridgeHandler = bridgeHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ThingTypeUID> getSupportedThingTypes() {
|
||||
return WorxLandroidBindingConstants.SUPPORTED_THING_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
try {
|
||||
List<ProductItemStatus> productItemsStatusResponse = bridgeHandler.retrieveAllDevices();
|
||||
productItemsStatusResponse.forEach(mower -> {
|
||||
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder
|
||||
.create(new ThingUID(THING_TYPE_MOWER, bridgeHandler.getThing().getUID(), mower.id))
|
||||
.withRepresentationProperty(SERIAL_NUMBER).withLabel(mower.name)
|
||||
.withProperty(SERIAL_NUMBER, mower.serialNumber).withBridge(bridgeHandler.getThing().getUID())
|
||||
.build();
|
||||
|
||||
thingDiscovered(discoveryResult);
|
||||
logger.debug("Discovered a mower thing with ID '{}'", mower.serialNumber);
|
||||
});
|
||||
} catch (WebApiException exception) {
|
||||
logger.warn("Error in WebApiException : {}", exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startBackgroundDiscovery() {
|
||||
startScan();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.handler;
|
||||
|
||||
import static org.openhab.binding.worxlandroid.internal.WorxLandroidBindingConstants.*;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.worxlandroid.internal.api.WebApiException;
|
||||
import org.openhab.binding.worxlandroid.internal.api.WorxApiDeserializer;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Payload;
|
||||
import org.openhab.binding.worxlandroid.internal.mqtt.AWSClient;
|
||||
import org.openhab.binding.worxlandroid.internal.mqtt.AWSClientCallbackI;
|
||||
import org.openhab.core.auth.client.oauth2.AccessTokenRefreshListener;
|
||||
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import software.amazon.awssdk.crt.mqtt.MqttMessage;
|
||||
|
||||
/**
|
||||
* The{@link AWSClientThingHandler} is handles outside communications (AWS,API) parts
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class AWSClientThingHandler extends BaseThingHandler
|
||||
implements AWSClientCallbackI, ThingHandlerHelper, AccessTokenRefreshListener {
|
||||
private static final Duration MIN_PUBLISH_DELAY_S = Duration.ofSeconds(2);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AWSClientThingHandler.class);
|
||||
private final AWSClient awsClient;
|
||||
protected final WorxApiDeserializer deserializer;
|
||||
|
||||
protected String endpoint = "";
|
||||
protected String uuid = "";
|
||||
protected String userId = "";
|
||||
protected String topic = "";
|
||||
protected String token = "";
|
||||
|
||||
private Instant lastPublishTS = Instant.MIN;
|
||||
private int lastReqHash = 0;
|
||||
|
||||
public AWSClientThingHandler(Thing thing, WorxApiDeserializer deserializer) {
|
||||
super(thing);
|
||||
this.deserializer = deserializer;
|
||||
this.awsClient = new AWSClient(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
checkInitBridgeOAuth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
checkInitBridgeOAuth();
|
||||
}
|
||||
|
||||
private void checkInitBridgeOAuth() {
|
||||
WorxLandroidBridgeHandler bridgeHandler = getBridgeHandler(getBridge(), WorxLandroidBridgeHandler.class);
|
||||
if (bridgeHandler != null) {
|
||||
setAccessToken(bridgeHandler.getAccessToken());
|
||||
bridgeHandler.oAuthClientService.addAccessTokenRefreshListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (!topic.isEmpty()) {
|
||||
awsClient.unsubscribe(topic);
|
||||
}
|
||||
awsClient.dispose();
|
||||
|
||||
WorxLandroidBridgeHandler bridgeHandler = getBridgeHandler(getBridge(), WorxLandroidBridgeHandler.class);
|
||||
if (bridgeHandler != null) {
|
||||
bridgeHandler.oAuthClientService.removeAccessTokenRefreshListener(this);
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAWSConnectionSuccess() {
|
||||
logger.debug("AWS connection is available");
|
||||
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
updateChannelOnOff(GROUP_AWS, CHANNEL_CONNECTED, awsClient.isConnected());
|
||||
|
||||
if (!topic.isEmpty()) {
|
||||
awsClient.subscribe(topic, this::onMqttMessage);
|
||||
logger.debug("subscribed to topic: {}", topic);
|
||||
} else {
|
||||
logger.warn("Connected but no topic to subscribe to");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAWSConnectionClosed() {
|
||||
// Don't try to reconnect if the connection is closed by the thing being disable
|
||||
if (thing.getStatus() == ThingStatus.ONLINE) {
|
||||
updateChannelOnOff(GROUP_AWS, CHANNEL_CONNECTED, awsClient.isConnected());
|
||||
|
||||
WorxLandroidBridgeHandler bridgeHandler = getBridgeHandler(getBridge(), WorxLandroidBridgeHandler.class);
|
||||
if (bridgeHandler != null) {
|
||||
bridgeHandler.requestTokenRefresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAWSConnectionFailed(@Nullable String message) {
|
||||
updateChannelOnOff(GROUP_AWS, CHANNEL_CONNECTED, awsClient.isConnected());
|
||||
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, "No AWS Connection");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLinked(ChannelUID channelUID) {
|
||||
return super.isLinked(channelUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(ChannelUID channelUID, State state) {
|
||||
super.updateState(channelUID, state);
|
||||
}
|
||||
|
||||
public void publishMessage(String topic, String cmd) {
|
||||
Instant now = Instant.now();
|
||||
int requestHash = topic.hashCode() + cmd.hashCode();
|
||||
if (requestHash == lastReqHash) {
|
||||
if (now.isBefore(lastPublishTS.plus(MIN_PUBLISH_DELAY_S))) {
|
||||
logger.debug("Won't post again too soon");
|
||||
return;
|
||||
}
|
||||
}
|
||||
lastPublishTS = now;
|
||||
lastReqHash = requestHash;
|
||||
logger.debug("publish on topic: '{}' - message: '{}'", topic, cmd);
|
||||
awsClient.publish(topic, cmd);
|
||||
}
|
||||
|
||||
public void onMqttMessage(MqttMessage mqttMessage) {
|
||||
String messagePayload = new String(mqttMessage.getPayload(), StandardCharsets.UTF_8);
|
||||
logger.debug("onMessage: {}", messagePayload);
|
||||
try {
|
||||
Payload payload = deserializer.deserialize(Payload.class, messagePayload);
|
||||
internalHandlePayload(payload);
|
||||
} catch (WebApiException e) {
|
||||
logger.warn("Error processing incoming AWS message: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void internalHandlePayload(Payload payload);
|
||||
|
||||
@Override
|
||||
public void onAccessTokenResponse(AccessTokenResponse tokenResponse) {
|
||||
String token = tokenResponse.getAccessToken();
|
||||
if (token != null) {
|
||||
setAccessToken(token);
|
||||
}
|
||||
}
|
||||
|
||||
private void setAccessToken(String token) {
|
||||
this.token = token;
|
||||
connectAWS();
|
||||
}
|
||||
|
||||
public void connectAws(String mqttEndpoint, String uuid, String userId, String commandOut) {
|
||||
this.endpoint = mqttEndpoint;
|
||||
this.uuid = uuid;
|
||||
this.userId = userId;
|
||||
this.topic = commandOut;
|
||||
connectAWS();
|
||||
}
|
||||
|
||||
private void connectAWS() {
|
||||
awsClient.disconnect();
|
||||
if (endpoint.isEmpty() || userId.isEmpty() || uuid.isEmpty() || token.isEmpty()) {
|
||||
logger.debug("Some data missing to initiate AWS connection");
|
||||
return;
|
||||
}
|
||||
awsClient.connect(endpoint, userId, uuid, token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
return;
|
||||
}
|
||||
WorxLandroidBridgeHandler bridgeHandler = getBridgeHandler(getBridge(), WorxLandroidBridgeHandler.class);
|
||||
if (bridgeHandler == null) {
|
||||
logger.error("no bridgeHandler");
|
||||
return;
|
||||
}
|
||||
if (!isOnline()) {
|
||||
logger.warn("handleCommand mower: {} is offline!", getThing().getUID());
|
||||
return;
|
||||
}
|
||||
String groupId = channelUID.getGroupId();
|
||||
String channelId = channelUID.getIdWithoutGroup();
|
||||
internalHandleCommand(groupId, channelId, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
|
||||
super.updateStatus(status, statusDetail, description);
|
||||
}
|
||||
|
||||
protected abstract void internalHandleCommand(@Nullable String groupId, String channelId, Command command);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.handler;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelGroupUID;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BridgeHandler;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
/**
|
||||
* {@link ThingHandlerHelper} provides utility function for thing handlers
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface ThingHandlerHelper {
|
||||
public boolean isLinked(ChannelUID channelUID);
|
||||
|
||||
public Thing getThing();
|
||||
|
||||
public void updateState(ChannelUID channelUID, State state);
|
||||
|
||||
void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description);
|
||||
|
||||
default @Nullable <T extends BridgeHandler> T getBridgeHandler(@Nullable Bridge bridge, Class<T> clazz) {
|
||||
if (bridge != null && bridge.getStatus() == ThingStatus.ONLINE) {
|
||||
if (bridge.getHandler() instanceof BridgeHandler bridgeHandler) {
|
||||
if (bridgeHandler.getClass() == clazz) {
|
||||
return clazz.cast(bridgeHandler);
|
||||
}
|
||||
}
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/incorrect-bridge");
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public default boolean isOnline() {
|
||||
return getThing().getStatus() == ThingStatus.ONLINE;
|
||||
}
|
||||
|
||||
public default ChannelGroupUID getGroupUID(String group) {
|
||||
return new ChannelGroupUID(getThing().getUID(), group);
|
||||
}
|
||||
|
||||
public default ChannelUID getChannelUID(String group, String channelId) {
|
||||
return new ChannelUID(getThing().getUID(), group, channelId);
|
||||
}
|
||||
|
||||
public default Set<ChannelUID> getChannelUIDs(String groupName, Set<String> channelIds) {
|
||||
Set<ChannelUID> result = new HashSet<>();
|
||||
ChannelGroupUID groupUID = getGroupUID(groupName);
|
||||
channelIds.forEach(id -> result.add(new ChannelUID(groupUID, id)));
|
||||
return result;
|
||||
}
|
||||
|
||||
public default void updateIfActive(String group, String channelId, State state) {
|
||||
ChannelUID id = getChannelUID(group, channelId);
|
||||
if (isLinked(id)) {
|
||||
updateState(id, state);
|
||||
}
|
||||
}
|
||||
|
||||
public default void updateChannelOnOff(String group, String channelId, boolean value) {
|
||||
updateIfActive(group, channelId, OnOffType.from(value));
|
||||
}
|
||||
|
||||
public default void updateChannelDateTime(String group, String channelId, Instant timestamp) {
|
||||
updateIfActive(group, channelId, new DateTimeType(timestamp));
|
||||
}
|
||||
|
||||
public default void updateChannelDateTime(String group, String channelId, ZonedDateTime timestamp) {
|
||||
updateIfActive(group, channelId, new DateTimeType(timestamp));
|
||||
}
|
||||
|
||||
public default void updateChannelString(String group, String channelId, @Nullable String value) {
|
||||
updateIfActive(group, channelId, value == null || value.isEmpty() ? UnDefType.NULL : new StringType(value));
|
||||
}
|
||||
|
||||
public default void updateChannelEnum(String group, String channelId, Enum<?> value) {
|
||||
String name = value.name();
|
||||
updateChannelString(group, channelId, "UNKNOWN".equals(name) ? null : name);
|
||||
}
|
||||
|
||||
public default void updateChannelDecimal(String group, String channelId, @Nullable Number value) {
|
||||
updateIfActive(group, channelId, value == null || value.equals(-1) ? UnDefType.NULL : new DecimalType(value));
|
||||
}
|
||||
|
||||
public default void updateChannelQuantity(String group, String channelId, @Nullable QuantityType<?> quantity) {
|
||||
updateIfActive(group, channelId, quantity != null ? quantity : UnDefType.NULL);
|
||||
}
|
||||
|
||||
public default void updateChannelQuantity(String group, String channelId, @Nullable Number d, Unit<?> unit) {
|
||||
if (d == null) {
|
||||
updateIfActive(group, channelId, UnDefType.NULL);
|
||||
} else {
|
||||
updateChannelQuantity(group, channelId, new QuantityType<>(d, unit));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.worxlandroid.internal.api.WebApiException;
|
||||
import org.openhab.binding.worxlandroid.internal.api.WorxApiHandler;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.ProductItemStatus;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.UsersMeResponse;
|
||||
import org.openhab.binding.worxlandroid.internal.config.WebApiConfiguration;
|
||||
import org.openhab.core.auth.client.oauth2.AccessTokenRefreshListener;
|
||||
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
|
||||
import org.openhab.core.auth.client.oauth2.OAuthClientService;
|
||||
import org.openhab.core.auth.client.oauth2.OAuthException;
|
||||
import org.openhab.core.auth.client.oauth2.OAuthFactory;
|
||||
import org.openhab.core.auth.client.oauth2.OAuthResponseException;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link WorxLandroidBridgeHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
* @author Gaël L'hopital - Refactored with oAuthFactory, removed AWSClient
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WorxLandroidBridgeHandler extends BaseBridgeHandler
|
||||
implements AccessTokenRefreshListener, ThingHandlerHelper {
|
||||
private static final String URL_OAUTH_TOKEN = "https://id.worx.com/oauth/token";
|
||||
private static final String CLIENT_ID = "013132A8-DB34-4101-B993-3C8348EA0EBC";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WorxLandroidBridgeHandler.class);
|
||||
private final WorxApiHandler apiHandler;
|
||||
private final OAuthFactory oAuthFactory;
|
||||
|
||||
public final OAuthClientService oAuthClientService;
|
||||
|
||||
private String accessToken = "";
|
||||
private int retryCount = 3;
|
||||
private int retryDelayS = 1;
|
||||
private @Nullable ScheduledFuture<?> tokenRefreshJob;
|
||||
private @Nullable ScheduledFuture<?> connectionJob;
|
||||
|
||||
public WorxLandroidBridgeHandler(Bridge bridge, WorxApiHandler apiHandler, OAuthFactory oAuthFactory) {
|
||||
super(bridge);
|
||||
this.apiHandler = apiHandler;
|
||||
this.oAuthFactory = oAuthFactory;
|
||||
this.oAuthClientService = oAuthFactory.createOAuthClientService(getThing().getUID().getAsString(),
|
||||
URL_OAUTH_TOKEN, null, CLIENT_ID, null, "*", true);
|
||||
oAuthClientService.addAccessTokenRefreshListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing Landroid API bridge handler.");
|
||||
WebApiConfiguration config = getConfigAs(WebApiConfiguration.class);
|
||||
|
||||
if (config.username.isBlank()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/conf-error-no-username");
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.password.isBlank()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/conf-error-no-password");
|
||||
return;
|
||||
}
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
scheduler.execute(() -> initiateConnection(config.username, config.password));
|
||||
}
|
||||
|
||||
private void initiateConnection(String username, String password) {
|
||||
stopConnectionJob();
|
||||
try {
|
||||
String token = oAuthClientService.getAccessTokenByResourceOwnerPasswordCredentials(username, password, "*")
|
||||
.getAccessToken();
|
||||
if (token != null) {
|
||||
accessToken = token;
|
||||
UsersMeResponse user = apiHandler.retrieveMe(accessToken);
|
||||
updateProperties(apiHandler.getDeserializer().toMap(user));
|
||||
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
} catch (IOException | WebApiException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
} catch (OAuthResponseException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/oauth-connection-error");
|
||||
} catch (OAuthException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause != null) {
|
||||
String message = cause.getMessage();
|
||||
if (message != null && message.contains("http code 403")) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"@text/oauth-connection-error");
|
||||
return;
|
||||
}
|
||||
}
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/oauth-connection-error");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.debug("Landroid Bridge is read-only and does not handle commands");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
stopConnectionJob();
|
||||
stopTokenRefreshJob();
|
||||
|
||||
oAuthClientService.removeAccessTokenRefreshListener(this);
|
||||
oAuthFactory.ungetOAuthService(getThing().getUID().getAsString());
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private void stopTokenRefreshJob() {
|
||||
ScheduledFuture<?> job = tokenRefreshJob;
|
||||
if (job != null) {
|
||||
job.cancel(true);
|
||||
tokenRefreshJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void stopConnectionJob() {
|
||||
ScheduledFuture<?> job = connectionJob;
|
||||
if (job != null) {
|
||||
job.cancel(true);
|
||||
connectionJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccessTokenResponse(AccessTokenResponse tokenResponse) {
|
||||
String token = tokenResponse.getAccessToken();
|
||||
if (token != null) {
|
||||
accessToken = token;
|
||||
}
|
||||
}
|
||||
|
||||
public void requestTokenRefresh() {
|
||||
if (tokenRefreshJob != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
oAuthClientService.refreshToken();
|
||||
retryCount = 3;
|
||||
stopTokenRefreshJob();
|
||||
} catch (IOException | OAuthResponseException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
} catch (OAuthException e) {
|
||||
if (retryCount > 0) {
|
||||
tokenRefreshJob = scheduler.schedule(() -> {
|
||||
retryCount--;
|
||||
requestTokenRefresh();
|
||||
}, retryDelayS, TimeUnit.MINUTES);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/oauth-refresh-error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public @Nullable ProductItemStatus retrieveDeviceStatus(String serialNumber) throws WebApiException {
|
||||
return apiHandler.retrieveDeviceStatus(accessToken, serialNumber);
|
||||
}
|
||||
|
||||
public List<ProductItemStatus> retrieveAllDevices() throws WebApiException {
|
||||
return apiHandler.retrieveDeviceStatus(accessToken);
|
||||
}
|
||||
|
||||
public boolean resetBladeTime(String serialNumber) {
|
||||
return apiHandler.resetBladeTime(accessToken, serialNumber);
|
||||
}
|
||||
|
||||
public boolean resetBatteryCycles(String serialNumber) {
|
||||
return apiHandler.resetBatteryCycles(accessToken, serialNumber);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLinked(ChannelUID channelUID) {
|
||||
return super.isLinked(channelUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(ChannelUID channelUID, State state) {
|
||||
super.updateState(channelUID, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
|
||||
super.updateStatus(status, statusDetail, description);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,591 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.handler;
|
||||
|
||||
import static org.openhab.binding.worxlandroid.internal.WorxLandroidBindingConstants.*;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.worxlandroid.internal.api.WebApiException;
|
||||
import org.openhab.binding.worxlandroid.internal.api.WorxApiDeserializer;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Commands.MowerCommand;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Commands.OneTimeCommand;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Commands.ScheduleCommand;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Commands.ScheduleDaysCommand;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Commands.SetRainDelay;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Commands.ZoneMeterAlloc;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Commands.ZoneMeterCommand;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Payload;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Payload.Dat.Axis;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.ProductItemStatus;
|
||||
import org.openhab.binding.worxlandroid.internal.codes.WorxLandroidActionCodes;
|
||||
import org.openhab.binding.worxlandroid.internal.codes.WorxLandroidDayCodes;
|
||||
import org.openhab.binding.worxlandroid.internal.codes.WorxLandroidStatusCodes;
|
||||
import org.openhab.binding.worxlandroid.internal.config.MowerConfiguration;
|
||||
import org.openhab.binding.worxlandroid.internal.vo.Mower;
|
||||
import org.openhab.binding.worxlandroid.internal.vo.ScheduledDay;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
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.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The{@link WorxLandroidMowerHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WorxLandroidMowerHandler extends AWSClientThingHandler {
|
||||
private static final String EMPTY_PAYLOAD = "{}";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WorxLandroidMowerHandler.class);
|
||||
private final TimeZoneProvider timeZoneProvider;
|
||||
private @Nullable ScheduledFuture<?> refreshJob;
|
||||
private @Nullable ScheduledFuture<?> pollingJob;
|
||||
private @Nullable Mower mower;
|
||||
|
||||
public WorxLandroidMowerHandler(Thing thing, WorxApiDeserializer deserializer, TimeZoneProvider timeZoneProvider) {
|
||||
super(thing, deserializer);
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
super.initialize();
|
||||
MowerConfiguration config = getConfigAs(MowerConfiguration.class);
|
||||
|
||||
if (config.serialNumber.isBlank()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/conf-error-no-serial");
|
||||
return;
|
||||
}
|
||||
|
||||
WorxLandroidBridgeHandler bridgeHandler = getBridgeHandler(getBridge(), WorxLandroidBridgeHandler.class);
|
||||
if (bridgeHandler != null) {
|
||||
initializeData(bridgeHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
ScheduledFuture<?> job = refreshJob;
|
||||
if (job != null) {
|
||||
job.cancel(true);
|
||||
refreshJob = null;
|
||||
}
|
||||
|
||||
job = pollingJob;
|
||||
if (job != null) {
|
||||
job.cancel(true);
|
||||
pollingJob = null;
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private void initializeData(WorxLandroidBridgeHandler bridgeHandler) {
|
||||
MowerConfiguration config = getConfigAs(MowerConfiguration.class);
|
||||
logger.debug("Initializing WorxLandroidMowerHandler for serial number '{}'", config.serialNumber);
|
||||
try {
|
||||
ProductItemStatus product = bridgeHandler.retrieveDeviceStatus(config.serialNumber);
|
||||
if (product != null) {
|
||||
connectAws(product.mqttEndpoint, product.uuid, product.userId, product.mqttTopics.commandOut);
|
||||
Mower newMower = new Mower(this, product);
|
||||
mower = newMower;
|
||||
setChannelsAndProperties(newMower);
|
||||
processStatusMessage(newMower);
|
||||
|
||||
updateStatus(product.online ? ThingStatus.ONLINE : ThingStatus.OFFLINE);
|
||||
startScheduledJobs(bridgeHandler, newMower, config);
|
||||
}
|
||||
} catch (WebApiException e) {
|
||||
logger.warn("initialize mower: id {} - {}::{}", config.serialNumber, getThing().getLabel(),
|
||||
getThing().getUID());
|
||||
}
|
||||
}
|
||||
|
||||
private void setChannelsAndProperties(Mower mower) {
|
||||
ThingBuilder thingBuilder = editThing();
|
||||
Set<ChannelUID> toRemove = new HashSet<>();
|
||||
|
||||
if (!mower.lockSupported()) { // lock channel only when supported
|
||||
toRemove.add(getChannelUID(GROUP_COMMON, CHANNEL_LOCK));
|
||||
}
|
||||
|
||||
if (!mower.rainDelaySupported()) { // rainDelay channel only when supported
|
||||
toRemove.add(getChannelUID(GROUP_RAIN, CHANNEL_DELAY));
|
||||
}
|
||||
|
||||
if (!mower.rainDelayStartSupported()) { // // rainDelayStart channel only when supported
|
||||
toRemove.addAll(getChannelUIDs(GROUP_RAIN, Set.of(CHANNEL_RAIN_STATE, CHANNEL_RAIN_COUNTER)));
|
||||
}
|
||||
|
||||
if (!mower.multiZoneSupported()) { // multizone channels only when supported
|
||||
toRemove.add(getChannelUID(GROUP_MULTI_ZONES, CHANNEL_LAST_ZONE));
|
||||
|
||||
// remove zone meter channels
|
||||
IntStream.range(0, mower.getMultiZoneCount())
|
||||
.forEach(index -> toRemove.add(getChannelUID(GROUP_MULTI_ZONES, "zone-%d".formatted(index + 1))));
|
||||
// remove allocation channels
|
||||
IntStream.range(0, 10).forEach(index -> toRemove
|
||||
.add(getChannelUID(GROUP_MULTI_ZONES, "%s-%d".formatted(CHANNEL_PREFIX_ALLOCATION, index))));
|
||||
}
|
||||
|
||||
if (!mower.oneTimeSchedulerSupported()) { // oneTimeScheduler channel only when supported
|
||||
toRemove.addAll(getChannelUIDs(GROUP_ONE_TIME, Set.of(CHANNEL_DURATION, CHANNEL_EDGECUT, CHANNEL_MODE)));
|
||||
}
|
||||
|
||||
if (!mower.scheduler2Supported()) { // Scheduler 2 channels only when supported version
|
||||
EnumSet.allOf(WorxLandroidDayCodes.class).stream()
|
||||
.map(dayCode -> "%s2".formatted(dayCode.getDescription().toLowerCase(Locale.ROOT)))
|
||||
.forEach(groupName -> toRemove.addAll(getChannelUIDs(groupName,
|
||||
Set.of(CHANNEL_ENABLE, CHANNEL_DURATION, CHANNEL_EDGECUT, CHANNEL_TIME))));
|
||||
}
|
||||
|
||||
toRemove.stream().forEach(thingBuilder::withoutChannel);
|
||||
updateThing(thingBuilder.build());
|
||||
|
||||
updateProperties(Map.of(Thing.PROPERTY_MAC_ADDRESS, mower.getMacAddress(), Thing.PROPERTY_VENDOR, "Worx",
|
||||
"productId", mower.getId(), "language", mower.getLanguage(), "mqtt_endpoint", endpoint));
|
||||
}
|
||||
|
||||
private void processStatusMessage(Mower mower) {
|
||||
updateStateCfg(mower);
|
||||
updateStateDat(mower);
|
||||
thing.setProperty(Thing.PROPERTY_FIRMWARE_VERSION, mower.getFirmwareVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Start scheduled jobs.
|
||||
* Jobs are only started if interval > 0
|
||||
*/
|
||||
private void startScheduledJobs(WorxLandroidBridgeHandler bridgeHandler, Mower theMower,
|
||||
MowerConfiguration config) {
|
||||
if (config.refreshStatusInterval > 0) {
|
||||
refreshJob = scheduler.scheduleWithFixedDelay(() -> {
|
||||
try {
|
||||
ProductItemStatus product = bridgeHandler.retrieveDeviceStatus(config.serialNumber);
|
||||
updateChannelDateTime(GROUP_COMMON, CHANNEL_ONLINE_TIMESTAMP, Instant.now());
|
||||
updateChannelOnOff(GROUP_COMMON, CHANNEL_ONLINE, product != null && product.online);
|
||||
updateStatus(product != null ? ThingStatus.ONLINE : ThingStatus.OFFLINE);
|
||||
} catch (WebApiException e) {
|
||||
logger.debug("Refreshing Thing {} failed, handler might be OFFLINE", config.serialNumber);
|
||||
}
|
||||
}, 3, config.refreshStatusInterval, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
if (config.pollingInterval > 0) {
|
||||
pollingJob = scheduler.scheduleWithFixedDelay(() -> sendCommand(theMower, EMPTY_PAYLOAD), 5,
|
||||
config.pollingInterval, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
super.bridgeStatusChanged(bridgeStatusInfo);
|
||||
WorxLandroidBridgeHandler bridgeHandler = getBridgeHandler(getBridge(), WorxLandroidBridgeHandler.class);
|
||||
if (bridgeHandler != null) {
|
||||
initializeData(bridgeHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalHandleCommand(@Nullable String groupId, String channelId, Command command) {
|
||||
Mower theMower = mower;
|
||||
if (theMower == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GROUP_MULTI_ZONES.equals(groupId)) {
|
||||
handleMultiZonesCommand(theMower, channelId, command);
|
||||
} else if (GROUP_AWS.equals(groupId)) {
|
||||
handleAWSCommand(theMower, channelId);
|
||||
} else if (GROUP_SCHEDULE.equals(groupId)) {
|
||||
handleScheduleCommand(theMower, channelId, Integer.parseInt(command.toString()));
|
||||
} else if (GROUP_ONE_TIME.equals(groupId)) {
|
||||
handleOneTimeSchedule(theMower, channelId, command);
|
||||
} else if (GROUP_COMMON.equals(groupId)) {
|
||||
handleCommonGroup(theMower, channelId, command);
|
||||
} else if (groupId != null && groupId.contains("day")) {
|
||||
setScheduledDays(theMower, groupId, channelId, command);
|
||||
sendCommand(theMower,
|
||||
theMower.scheduler2Supported()
|
||||
? new ScheduleDaysCommand(theMower.getTimeExtension(), theMower.getScheduleArray1(),
|
||||
theMower.getScheduleArray2())
|
||||
: new ScheduleDaysCommand(theMower.getTimeExtension(), theMower.getScheduleArray1()));
|
||||
} else if (CHANNEL_DELAY.equals(channelId)) {
|
||||
int delaySec = commandToInt(command, Units.SECOND);
|
||||
sendCommand(theMower, new SetRainDelay(delaySec));
|
||||
} else if (CHANNEL_BLADE_TIME.equals(channelId) || CHANNEL_CHARGE_CYCLES.equals(channelId)) {
|
||||
resetStat(channelId, theMower.getSerialNumber());
|
||||
} else {
|
||||
logger.debug("command for channel {} not supported: {}", channelId, command);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAWSCommand(Mower theMower, String channel) {
|
||||
if (CHANNEL_POLL.equals(channel)) {
|
||||
sendCommand(theMower, EMPTY_PAYLOAD);
|
||||
updateState(CHANNEL_POLL, OnOffType.OFF);
|
||||
} else {
|
||||
logger.warn("No action identified on channel {}", channel);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCommonGroup(Mower theMower, String channel, Command command) {
|
||||
if (CHANNEL_ACTION.equals(channel)) {
|
||||
WorxLandroidActionCodes actionCode = WorxLandroidActionCodes.valueOf(command.toString());
|
||||
sendCommand(theMower, new MowerCommand(actionCode));
|
||||
} else if (CHANNEL_LOCK.equals(channel)) {
|
||||
WorxLandroidActionCodes lockCode = OnOffType.ON.equals(command) ? WorxLandroidActionCodes.LOCK
|
||||
: WorxLandroidActionCodes.UNLOCK;
|
||||
sendCommand(theMower, new MowerCommand(lockCode));
|
||||
} else if (CHANNEL_ENABLE.equals(channel)) {
|
||||
theMower.setEnable(OnOffType.ON.equals(command));
|
||||
sendCommand(theMower,
|
||||
theMower.scheduler2Supported()
|
||||
? new ScheduleDaysCommand(theMower.getTimeExtension(), theMower.getScheduleArray1(),
|
||||
theMower.getScheduleArray2())
|
||||
: new ScheduleDaysCommand(theMower.getTimeExtension(), theMower.getScheduleArray1()));
|
||||
} else {
|
||||
logger.warn("No action identified for command {} on channel {}", command, channel);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleOneTimeSchedule(Mower theMower, String channel, Command command) {
|
||||
if (CHANNEL_DURATION.equals(channel)) {
|
||||
sendCommand(theMower, new OneTimeCommand(0, Integer.parseInt(command.toString())));
|
||||
} else if (CHANNEL_EDGECUT.equals(channel)) {
|
||||
sendCommand(theMower, new OneTimeCommand(OnOffType.ON.equals(command) ? 1 : 0, 0));
|
||||
} else {
|
||||
logger.warn("No action identified for command {} on channel {}", command, channel);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleScheduleCommand(Mower theMower, String channel, int command) {
|
||||
if (CHANNEL_MODE.equals(channel)) {
|
||||
sendCommand(theMower, new ScheduleCommand(command));
|
||||
} else if (CHANNEL_TIME_EXTENSION.equals(channel)) {
|
||||
theMower.setTimeExtension(command);
|
||||
sendCommand(theMower,
|
||||
theMower.scheduler2Supported()
|
||||
? new ScheduleDaysCommand(theMower.getTimeExtension(), theMower.getScheduleArray1(),
|
||||
theMower.getScheduleArray2())
|
||||
: new ScheduleDaysCommand(theMower.getTimeExtension(), theMower.getScheduleArray1()));
|
||||
} else {
|
||||
logger.warn("No action identified for command {} on channel {}", command, channel);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMultiZonesCommand(Mower theMower, String channel, Command command) {
|
||||
if (CHANNEL_ENABLE.equals(channel)) {
|
||||
theMower.setMultiZoneEnable(OnOffType.ON.equals(command));
|
||||
sendCommand(theMower, new ZoneMeterCommand(theMower.getZoneMeters()));
|
||||
} else if (CHANNEL_LAST_ZONE.equals(channel)) {
|
||||
if (!WorxLandroidStatusCodes.HOME.equals(theMower.getStatusCode())) {
|
||||
logger.warn("Cannot start zone because mower must be at HOME!");
|
||||
return;
|
||||
}
|
||||
|
||||
theMower.setZoneTo(Integer.parseInt(command.toString()));
|
||||
sendCommand(theMower, new ZoneMeterCommand(theMower.getZoneMeters()));
|
||||
scheduler.schedule(() -> sendCommand(theMower, new MowerCommand(WorxLandroidActionCodes.START)), 2000,
|
||||
TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
String[] names = channel.split("-");
|
||||
int index = Integer.valueOf(names[1]);
|
||||
|
||||
if (CHANNEL_PREFIX_ZONE.startsWith(names[0])) {
|
||||
int meterValue = commandToInt(command, SIUnits.METRE);
|
||||
theMower.setZoneMeter(index - 1, meterValue);
|
||||
sendCommand(theMower, new ZoneMeterCommand(theMower.getZoneMeters()));
|
||||
} else if (CHANNEL_PREFIX_ALLOCATION.startsWith(names[0])) {
|
||||
theMower.setAllocation(index, Integer.parseInt(command.toString()));
|
||||
sendCommand(theMower, new ZoneMeterAlloc(theMower.getAllocations()));
|
||||
} else {
|
||||
logger.warn("No action identified for command {} on channel {}", command, channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int commandToInt(Command command, @Nullable Unit<?> targetUnit) {
|
||||
if (command instanceof QuantityType<?> qtty && targetUnit != null) {
|
||||
QuantityType<?> inTarget = qtty.toUnit(targetUnit);
|
||||
if (inTarget != null) {
|
||||
return inTarget.intValue();
|
||||
}
|
||||
}
|
||||
return Integer.parseInt(command.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set scheduled days
|
||||
*
|
||||
* @param theMower
|
||||
*
|
||||
* @param scDaysIndex 1 or 2
|
||||
* @param channelUID
|
||||
* @param command
|
||||
*/
|
||||
private void setScheduledDays(Mower theMower, String groupId, String channelId, Command command) {
|
||||
int scDaysSlot = groupId.endsWith("2") ? 2 : 1;
|
||||
WorxLandroidDayCodes dayCodeUpdated = WorxLandroidDayCodes
|
||||
.valueOf(groupId.replace("2", "").toUpperCase(Locale.ROOT));
|
||||
|
||||
ScheduledDay scheduledDayUpdated = theMower.getScheduledDay(scDaysSlot, dayCodeUpdated);
|
||||
if (scheduledDayUpdated == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (CHANNEL_ENABLE.equals(channelId)) {
|
||||
scheduledDayUpdated.setEnable(OnOffType.ON.equals(command));
|
||||
} else if (CHANNEL_TIME.equals(channelId)) {
|
||||
if (command instanceof DateTimeType dateTime) {
|
||||
scheduledDayUpdated.setStartTime(dateTime.getInstant());
|
||||
} else if (command instanceof StringType stringType) {
|
||||
scheduledDayUpdated.setStartTime(stringType.toString());
|
||||
} else {
|
||||
logger.warn("Incorrect command {} on channel {}:{} ", command, groupId, channelId);
|
||||
}
|
||||
} else if (CHANNEL_DURATION.equals(channelId)) {
|
||||
scheduledDayUpdated.setDuration(Integer.parseInt(command.toString()));
|
||||
} else if (CHANNEL_EDGECUT.equals(channelId)) {
|
||||
scheduledDayUpdated.setEdgecut(OnOffType.ON.equals(command));
|
||||
}
|
||||
}
|
||||
|
||||
private void sendCommand(Mower theMower, Object command) {
|
||||
logger.debug("send command: {}", command);
|
||||
publishMessage(theMower.getMqttCommandIn(), command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update states for data values
|
||||
*
|
||||
* @param theMower
|
||||
*
|
||||
* @param dat
|
||||
*/
|
||||
private void updateStateDat(Mower theMower) {
|
||||
updateChannelQuantity(GROUP_ORIENTATION, CHANNEL_PITCH, theMower.getAngle(Axis.PITCH), Units.DEGREE_ANGLE);
|
||||
updateChannelQuantity(GROUP_ORIENTATION, CHANNEL_ROLL, theMower.getAngle(Axis.ROLL), Units.DEGREE_ANGLE);
|
||||
updateChannelQuantity(GROUP_ORIENTATION, CHANNEL_YAW, theMower.getAngle(Axis.YAW), Units.DEGREE_ANGLE);
|
||||
updateChannelEnum(GROUP_COMMON, CHANNEL_STATUS_CODE, theMower.getPayloadDat().statusCode);
|
||||
updateChannelEnum(GROUP_COMMON, CHANNEL_ERROR_CODE, theMower.getPayloadDat().errorCode);
|
||||
updateChannelDecimal(GROUP_MULTI_ZONES, CHANNEL_LAST_ZONE, theMower.getLastZone());
|
||||
|
||||
updateChannelDecimal(GROUP_BATTERY, CHANNEL_CHARGE_CYCLES, theMower.getCurrentChargeCycles());
|
||||
updateChannelDecimal(GROUP_BATTERY, CHANNEL_CHARGE_CYCLES_TOTAL, theMower.getTotalChargeCycles());
|
||||
updateChannelQuantity(GROUP_METRICS, CHANNEL_BLADE_TIME, theMower.getCurrentBladeTime(), Units.MINUTE);
|
||||
updateChannelQuantity(GROUP_METRICS, CHANNEL_BLADE_TIME_TOTAL, theMower.getTotalBladeTime(), Units.MINUTE);
|
||||
|
||||
theMower.getBattery().ifPresent(battery -> {
|
||||
updateChannelQuantity(GROUP_BATTERY, CHANNEL_TEMPERATURE, battery.temp != -1 ? battery.temp : null,
|
||||
SIUnits.CELSIUS);
|
||||
updateChannelQuantity(GROUP_BATTERY, CHANNEL_VOLTAGE, battery.voltage != -1 ? battery.voltage : null,
|
||||
Units.VOLT);
|
||||
updateChannelDecimal(GROUP_BATTERY, CHANNEL_LEVEL, battery.level);
|
||||
updateChannelOnOff(GROUP_BATTERY, CHANNEL_CHARGING, battery.charging);
|
||||
});
|
||||
|
||||
theMower.getStats().ifPresent(stats -> {
|
||||
updateChannelQuantity(GROUP_METRICS, CHANNEL_DISTANCE,
|
||||
stats.distanceCovered != -1 ? stats.distanceCovered : null, SIUnits.METRE);
|
||||
updateChannelQuantity(GROUP_METRICS, CHANNEL_TOTAL_TIME,
|
||||
stats.mowerWorkTime != -1 ? stats.mowerWorkTime : null, Units.MINUTE);
|
||||
});
|
||||
|
||||
int rssi = theMower.getPayloadDat().wifiQuality;
|
||||
updateChannelDecimal(GROUP_WIFI, CHANNEL_WIFI_QUALITY, rssi <= 0 ? toQoS(rssi) : null);
|
||||
updateChannelQuantity(GROUP_WIFI, CHANNEL_RSSI,
|
||||
rssi <= 0 ? new QuantityType<>(rssi, Units.DECIBEL_MILLIWATTS) : null);
|
||||
|
||||
if (theMower.lockSupported()) {
|
||||
updateChannelOnOff(GROUP_COMMON, CHANNEL_LOCK, theMower.getPayloadDat().isLocked());
|
||||
}
|
||||
|
||||
theMower.getRain().ifPresent(rain -> {
|
||||
if (theMower.rainDelayStartSupported()) {
|
||||
updateChannelOnOff(GROUP_RAIN, CHANNEL_RAIN_STATE, rain.raining);
|
||||
updateChannelQuantity(GROUP_RAIN, CHANNEL_RAIN_COUNTER, rain.counter, Units.MINUTE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update states for cfg values
|
||||
*
|
||||
* @param theMower
|
||||
*
|
||||
* @param cfg
|
||||
* @param zoneId
|
||||
*/
|
||||
private void updateStateCfg(Mower theMower) {
|
||||
updateChannelDateTime(GROUP_CONFIG, CHANNEL_TIMESTAMP, theMower.getLastUpdate());
|
||||
|
||||
theMower.getOneTimeSchedule().ifPresent(ots -> {
|
||||
updateChannelOnOff(GROUP_ONE_TIME, CHANNEL_EDGECUT, ots.getEdgeCut());
|
||||
updateChannelQuantity(GROUP_ONE_TIME, CHANNEL_DURATION, ots.duration != -1 ? ots.duration : null,
|
||||
Units.MINUTE);
|
||||
});
|
||||
|
||||
theMower.getSchedule().ifPresent(schedule -> {
|
||||
if (theMower.oneTimeSchedulerSupported()) {
|
||||
updateChannelEnum(GROUP_SCHEDULE, CHANNEL_MODE, schedule.scheduleMode);
|
||||
}
|
||||
|
||||
if (schedule.timeExtension != -1) {
|
||||
updateChannelQuantity(GROUP_SCHEDULE, CHANNEL_TIME_EXTENSION, schedule.timeExtension, Units.PERCENT);
|
||||
updateChannelOnOff(GROUP_COMMON, CHANNEL_ENABLE, theMower.isEnable());
|
||||
}
|
||||
|
||||
if (schedule.dd != null) {
|
||||
updateStateCfgScDays(theMower, 2, schedule.dd);
|
||||
}
|
||||
|
||||
if (schedule.d != null) {
|
||||
updateStateCfgScDays(theMower, 1, schedule.d);
|
||||
}
|
||||
});
|
||||
|
||||
int command = theMower.getPayloadCfg().cmd;
|
||||
updateChannelDecimal(GROUP_CONFIG, CHANNEL_COMMAND, command != -1 ? command : null);
|
||||
|
||||
if (theMower.multiZoneSupported()) {
|
||||
for (int zoneIndex = 0; zoneIndex < theMower.getZonesSize(); zoneIndex++) {
|
||||
updateChannelQuantity(GROUP_MULTI_ZONES, CHANNEL_PREFIX_ZONE.formatted(zoneIndex + 1),
|
||||
theMower.getZoneMeter(zoneIndex), SIUnits.METRE);
|
||||
}
|
||||
|
||||
for (int allocationIndex = 0; allocationIndex < theMower.getAllocationsSize(); allocationIndex++) {
|
||||
updateChannelDecimal(GROUP_MULTI_ZONES, CHANNEL_PREFIX_ALLOCATION.formatted(allocationIndex),
|
||||
theMower.getAllocation(allocationIndex));
|
||||
}
|
||||
updateChannelOnOff(GROUP_MULTI_ZONES, CHANNEL_ENABLE, theMower.isMultiZoneEnable());
|
||||
}
|
||||
|
||||
int rainDelay = theMower.getPayloadCfg().rainDelay;
|
||||
updateChannelQuantity(GROUP_RAIN, CHANNEL_DELAY,
|
||||
theMower.rainDelaySupported() && rainDelay != -1 ? rainDelay : null, Units.MINUTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param theMower
|
||||
* @param scDSlot scheduled day slot
|
||||
* @param d scheduled day
|
||||
*/
|
||||
private void updateStateCfgScDays(Mower theMower, int scDSlot, List<List<String>> d) {
|
||||
List<ZonedDateTime> nextStarts = new ArrayList<>();
|
||||
List<ZonedDateTime> nextEnds = new ArrayList<>();
|
||||
|
||||
for (WorxLandroidDayCodes dayCode : WorxLandroidDayCodes.values()) {
|
||||
ScheduledDay scheduledDay = theMower.getScheduledDay(scDSlot, dayCode);
|
||||
if (scheduledDay == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String groupName = "%s%s".formatted(dayCode.getDescription().toLowerCase(Locale.ROOT),
|
||||
scDSlot == 1 ? "" : String.valueOf(scDSlot));
|
||||
|
||||
updateChannelOnOff(groupName, CHANNEL_ENABLE, scheduledDay.isEnabled());
|
||||
updateChannelOnOff(groupName, CHANNEL_EDGECUT, scheduledDay.isEdgecut());
|
||||
updateChannelQuantity(groupName, CHANNEL_DURATION, scheduledDay.getDuration(), Units.MINUTE);
|
||||
|
||||
ZonedDateTime now = ZonedDateTime.now(timeZoneProvider.getTimeZone());
|
||||
if (scheduledDay.isEnabled()) {
|
||||
ZonedDateTime scheduleStart = now.truncatedTo(ChronoUnit.MINUTES).with(scheduledDay.getStartTime());
|
||||
scheduleStart = ZonedDateTime.from(dayCode.dayOfWeek.adjustInto(scheduleStart));
|
||||
updateChannelDateTime(groupName, CHANNEL_TIME, scheduleStart);
|
||||
ZonedDateTime scheduleEnd = scheduleStart.plusMinutes(scheduledDay.getDuration());
|
||||
if (scheduleStart.isBefore(now)) {
|
||||
scheduleStart = scheduleStart.plusDays(7);
|
||||
}
|
||||
if (scheduleEnd.isBefore(now)) {
|
||||
scheduleEnd = scheduleEnd.plusDays(7);
|
||||
}
|
||||
|
||||
nextStarts.add(scheduleStart);
|
||||
nextEnds.add(scheduleEnd);
|
||||
}
|
||||
}
|
||||
if (!nextStarts.isEmpty()) {
|
||||
Collections.sort(nextStarts);
|
||||
Collections.sort(nextEnds);
|
||||
updateChannelDateTime(GROUP_SCHEDULE, CHANNEL_START, nextStarts.get(0));
|
||||
updateChannelDateTime(GROUP_SCHEDULE, CHANNEL_STOP, nextEnds.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
private int toQoS(int rssi) {
|
||||
return rssi > -50 ? 4 : rssi > -60 ? 3 : rssi > -70 ? 2 : rssi > -85 ? 1 : 0;
|
||||
}
|
||||
|
||||
public void publishMessage(String topic, Object command) {
|
||||
publishMessage(topic, deserializer.toJson(command));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalHandlePayload(Payload payload) {
|
||||
Mower theMower = mower;
|
||||
if (theMower == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
theMower.setStatus(payload);
|
||||
updateStateCfg(theMower);
|
||||
updateStateDat(theMower);
|
||||
}
|
||||
|
||||
private boolean resetStat(String channelId, String serialNumber) {
|
||||
WorxLandroidBridgeHandler bridgeHandler = getBridgeHandler(getBridge(), WorxLandroidBridgeHandler.class);
|
||||
if (bridgeHandler != null) {
|
||||
logger.debug("Resetting {}", channelId);
|
||||
if (CHANNEL_BLADE_TIME.equals(channelId)) {
|
||||
bridgeHandler.resetBladeTime(serialNumber);
|
||||
} else {
|
||||
bridgeHandler.resetBatteryCycles(serialNumber);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.mqtt;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.common.ThreadPoolManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import software.amazon.awssdk.crt.CRT;
|
||||
import software.amazon.awssdk.crt.http.HttpRequest;
|
||||
import software.amazon.awssdk.crt.mqtt.MqttClientConnection;
|
||||
import software.amazon.awssdk.crt.mqtt.MqttClientConnectionEvents;
|
||||
import software.amazon.awssdk.crt.mqtt.MqttException;
|
||||
import software.amazon.awssdk.crt.mqtt.MqttMessage;
|
||||
import software.amazon.awssdk.crt.mqtt.OnConnectionFailureReturn;
|
||||
import software.amazon.awssdk.crt.mqtt.OnConnectionSuccessReturn;
|
||||
import software.amazon.awssdk.crt.mqtt.QualityOfService;
|
||||
import software.amazon.awssdk.iot.AwsIotMqttConnectionBuilder;
|
||||
|
||||
/**
|
||||
* {@link AWSClient} AWS client
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AWSClient implements MqttClientConnectionEvents {
|
||||
private static final QualityOfService QOS = QualityOfService.AT_MOST_ONCE;
|
||||
private static final String AUTHORIZER_NAME = "com-worxlandroid-customer";
|
||||
private static final String MQTT_USERNAME = "openhab";
|
||||
|
||||
private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool("AWSClient");
|
||||
private final Map<String, Consumer<MqttMessage>> subscriptions = new HashMap<>();
|
||||
private final Logger logger = LoggerFactory.getLogger(AWSClient.class);
|
||||
private final AWSClientCallbackI clientCallback;
|
||||
|
||||
private @Nullable MqttClientConnection mqttClient;
|
||||
private @Nullable Instant lastResumed;
|
||||
private boolean connected;
|
||||
|
||||
public AWSClient(AWSClientCallbackI clientCallback) {
|
||||
this.clientCallback = clientCallback;
|
||||
}
|
||||
|
||||
public void connect(String endpoint, String userId, String productUuid, String token) {
|
||||
String[] tok = token.replaceAll("_", "/").replaceAll("-", "+").split("\\.");
|
||||
|
||||
try {
|
||||
MqttClientConnection connection = AwsIotMqttConnectionBuilder.newDefaultBuilder()
|
||||
.withClientId("WX/USER/%s/%s/%s".formatted(userId, MQTT_USERNAME, productUuid))
|
||||
.withEndpoint(endpoint).withUsername(MQTT_USERNAME).withCleanSession(false).withKeepAliveSecs(300)
|
||||
.withConnectionEventCallbacks(this).withWebsockets(true)
|
||||
.withWebsocketHandshakeTransform(handshakeArgs -> {
|
||||
HttpRequest httpRequest = handshakeArgs.getHttpRequest();
|
||||
httpRequest.addHeader("x-amz-customauthorizer-name", AUTHORIZER_NAME);
|
||||
httpRequest.addHeader("x-amz-customauthorizer-signature", tok[2]);
|
||||
httpRequest.addHeader("jwt", tok[0] + "." + tok[1]);
|
||||
handshakeArgs.complete(httpRequest);
|
||||
}).build();
|
||||
connection.connect().get();
|
||||
this.mqttClient = connection;
|
||||
} catch (MqttException | UnsupportedEncodingException | InterruptedException | ExecutionException e) {
|
||||
clientCallback.onAWSConnectionFailed(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
disconnect();
|
||||
subscriptions.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionSuccess(@NonNullByDefault({}) OnConnectionSuccessReturn data) {
|
||||
onConnectionResumed(data.getSessionPresent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionResumed(boolean sessionPresent) {
|
||||
connected = sessionPresent;
|
||||
if (sessionPresent) {
|
||||
lastResumed = Instant.now();
|
||||
logger.debug("last connection resume {}", lastResumed);
|
||||
subscriptions.forEach(this::subscribe);
|
||||
clientCallback.onAWSConnectionSuccess();
|
||||
} else {
|
||||
clientCallback.onAWSConnectionClosed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionInterrupted(int errorCode) {
|
||||
Instant interrupted = Instant.now();
|
||||
connected = false;
|
||||
String error = CRT.awsErrorString(errorCode);
|
||||
logger.debug("connection interrupted errorcode: {} : {}", errorCode, error);
|
||||
|
||||
scheduler.schedule(() -> {
|
||||
/**
|
||||
* workaround -> after 20 minutes the connection is interrupted but immediately resumed (~0,5sec).
|
||||
* ConnectionBuilder with ".withKeepAliveSecs(300)" doesn't work
|
||||
*/
|
||||
Instant resumed = lastResumed;
|
||||
boolean isBetween = resumed != null && resumed.isAfter(interrupted) && resumed.isBefore(Instant.now());
|
||||
logger.debug("lastResumed: {} interrupted: {} in: {}", lastResumed, interrupted, isBetween);
|
||||
if (!isBetween) {
|
||||
clientCallback.onAWSConnectionClosed();
|
||||
}
|
||||
}, 5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionFailure(@NonNullByDefault({}) OnConnectionFailureReturn data) {
|
||||
connected = false;
|
||||
if (data.getErrorCode() == 5134) {
|
||||
clientCallback.onAWSConnectionFailed("Error code 5134: banned 24h");
|
||||
} else {
|
||||
logger.debug("{}", data.toString());
|
||||
clientCallback.onAWSConnectionClosed();
|
||||
}
|
||||
};
|
||||
|
||||
public void disconnect() {
|
||||
MqttClientConnection connection = mqttClient;
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
connection.close();
|
||||
mqttClient = null;
|
||||
}
|
||||
connected = false;
|
||||
}
|
||||
|
||||
public void subscribe(String topic, Consumer<MqttMessage> handler) {
|
||||
MqttClientConnection connection = mqttClient;
|
||||
if (connection != null) {
|
||||
subscriptions.put(topic, handler);
|
||||
connection.subscribe(topic, QOS, handler);
|
||||
} else {
|
||||
logger.warn("Tried to subscribe on {} when connection is closed", topic);
|
||||
}
|
||||
}
|
||||
|
||||
public void unsubscribe(String topic) {
|
||||
MqttClientConnection connection = mqttClient;
|
||||
if (connection != null) {
|
||||
subscriptions.remove(topic);
|
||||
connection.unsubscribe(topic);
|
||||
} else {
|
||||
logger.warn("Tried to unsubscribe from {} when connection is closed", topic);
|
||||
}
|
||||
}
|
||||
|
||||
public void publish(String topic, String payload) {
|
||||
MqttClientConnection connection = mqttClient;
|
||||
if (connection != null) {
|
||||
connection.publish(new MqttMessage(topic, payload.getBytes(StandardCharsets.UTF_8), QOS));
|
||||
} else {
|
||||
logger.warn("Tried to publish on {} when connection is closed", topic);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return connected;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.mqtt;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* {@link AWSClientCallbackI} Callback for AWS connection events
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface AWSClientCallbackI {
|
||||
|
||||
/**
|
||||
* callback method on connection success
|
||||
*/
|
||||
public void onAWSConnectionSuccess();
|
||||
|
||||
/**
|
||||
* callback method on connection closed
|
||||
*/
|
||||
public void onAWSConnectionClosed();
|
||||
|
||||
/**
|
||||
* callback method on connection failed
|
||||
*/
|
||||
public void onAWSConnectionFailed(@Nullable String message);
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.vo;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Commands.ZoneMeterCommand;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.LastStatus;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Payload;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Payload.Battery;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Payload.Cfg;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Payload.Dat;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Payload.Dat.Axis;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Payload.Ots;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Payload.Rain;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Payload.Schedule;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.Payload.Stat;
|
||||
import org.openhab.binding.worxlandroid.internal.api.dto.ProductItemStatus;
|
||||
import org.openhab.binding.worxlandroid.internal.codes.WorxLandroidDayCodes;
|
||||
import org.openhab.binding.worxlandroid.internal.codes.WorxLandroidStatusCodes;
|
||||
import org.openhab.binding.worxlandroid.internal.handler.WorxLandroidMowerHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* {@link Mower}
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Mower {
|
||||
private static final int[] MULTI_ZONE_METER_DISABLE = { 0, 0, 0, 0 };
|
||||
private static final int[] MULTI_ZONE_METER_ENABLE = { 1, 0, 0, 0 };
|
||||
private static final int TIME_EXTENSION_DISABLE = -100;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(Mower.class);
|
||||
private final WorxLandroidMowerHandler mowerHandler;
|
||||
private final ProductItemStatus product;
|
||||
|
||||
private final int[] zoneMeter;
|
||||
private final int[] zoneMeterRestore;
|
||||
private final int[] allocations = new int[10];
|
||||
private final List<Map<WorxLandroidDayCodes, @Nullable ScheduledDay>> schedules = new ArrayList<>();
|
||||
|
||||
private boolean multiZoneEnable;
|
||||
private int timeExtension;
|
||||
private int timeExtensionRestore = 0;
|
||||
private @NonNullByDefault({}) LastStatus lastStatus;
|
||||
|
||||
private boolean restoreZoneMeter = false;
|
||||
private int[] zoneMeterRestoreValues = {};
|
||||
|
||||
public Mower(WorxLandroidMowerHandler mowerHandler, ProductItemStatus product) {
|
||||
this.mowerHandler = mowerHandler;
|
||||
this.product = product;
|
||||
this.zoneMeter = new int[getMultiZoneCount()];
|
||||
this.zoneMeterRestore = new int[getMultiZoneCount()];
|
||||
|
||||
schedules.add(new HashMap<WorxLandroidDayCodes, @Nullable ScheduledDay>(7));
|
||||
if (product.capabilities.contains("scheduler_two_slots")) {
|
||||
schedules.add(new HashMap<WorxLandroidDayCodes, @Nullable ScheduledDay>(7));
|
||||
}
|
||||
setStatus(product.lastStatus.payload);
|
||||
}
|
||||
|
||||
public String getSerialNumber() {
|
||||
return product.serialNumber;
|
||||
}
|
||||
|
||||
public int getTimeExtension() {
|
||||
return timeExtension;
|
||||
}
|
||||
|
||||
public String getFirmwareVersion() {
|
||||
return product.firmwareVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* timeExtension = -100 disables mowing (enable=false).
|
||||
* timeExtension > -100 enables mowing (enable=true).
|
||||
*
|
||||
* @param timeExtension
|
||||
*/
|
||||
public void setTimeExtension(int timeExtension) {
|
||||
if (timeExtension == TIME_EXTENSION_DISABLE) {
|
||||
storeTimeExtension();
|
||||
}
|
||||
this.timeExtension = timeExtension;
|
||||
}
|
||||
|
||||
public boolean lockSupported() {
|
||||
return product.capabilities.contains("lock");
|
||||
}
|
||||
|
||||
public boolean rainDelaySupported() {
|
||||
return product.capabilities.contains("rain_delay");
|
||||
}
|
||||
|
||||
public boolean rainDelayStartSupported() {
|
||||
return product.capabilities.contains("rain_delay_start");
|
||||
}
|
||||
|
||||
public boolean multiZoneSupported() {
|
||||
return product.capabilities.contains("multi_zone");
|
||||
}
|
||||
|
||||
public boolean scheduler2Supported() {
|
||||
return schedules.size() > 1;
|
||||
}
|
||||
|
||||
public boolean oneTimeSchedulerSupported() {
|
||||
return product.capabilities.contains("one_time_scheduler");
|
||||
}
|
||||
|
||||
public @Nullable ScheduledDay getScheduledDay(int scDSlot, WorxLandroidDayCodes dayCode) {
|
||||
return scDSlot == 1 ? schedules.get(0).get(dayCode)
|
||||
: scheduler2Supported() ? schedules.get(1).get(dayCode) : null;
|
||||
}
|
||||
|
||||
private Object[] getScheduleArray(Map<WorxLandroidDayCodes, @Nullable ScheduledDay> schedules) {
|
||||
Object[] result = new Object[7];
|
||||
for (WorxLandroidDayCodes dayCode : WorxLandroidDayCodes.values()) {
|
||||
ScheduledDay schedule = schedules.get(dayCode);
|
||||
result[dayCode.code] = schedule != null ? schedule.asArray() : ScheduledDay.BLANK.asArray();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Object[] getScheduleArray1() {
|
||||
return getScheduleArray(schedules.get(0));
|
||||
}
|
||||
|
||||
public Object[] getScheduleArray2() {
|
||||
return scheduler2Supported() ? getScheduleArray(schedules.get(1)) : new Object[] {};
|
||||
}
|
||||
|
||||
public boolean isMultiZoneEnable() {
|
||||
return multiZoneEnable;
|
||||
}
|
||||
|
||||
public void setMultiZoneEnable(boolean multiZoneEnable) {
|
||||
this.multiZoneEnable = multiZoneEnable;
|
||||
|
||||
if (multiZoneEnable && isZoneMeterDisabled()) {
|
||||
restoreZoneMeter();
|
||||
if (isZoneMeterDisabled()) {
|
||||
System.arraycopy(MULTI_ZONE_METER_ENABLE, 0, zoneMeter, 0, zoneMeter.length);
|
||||
}
|
||||
} else {
|
||||
storeZoneMeter();
|
||||
System.arraycopy(MULTI_ZONE_METER_DISABLE, 0, zoneMeter, 0, zoneMeter.length);
|
||||
}
|
||||
}
|
||||
|
||||
public int getZoneMeter(int zoneIndex) {
|
||||
return zoneMeter[zoneIndex];
|
||||
}
|
||||
|
||||
public int[] getZoneMeters() {
|
||||
return Arrays.copyOf(zoneMeter, zoneMeter.length);
|
||||
}
|
||||
|
||||
public int getZonesSize() {
|
||||
return getZoneMeters().length;
|
||||
}
|
||||
|
||||
public void setZoneMeters(int[] zoneMeterInput) {
|
||||
System.arraycopy(zoneMeterInput, 0, zoneMeter, 0, zoneMeter.length);
|
||||
}
|
||||
|
||||
public void setZoneMeter(int zoneIndex, int meter) {
|
||||
zoneMeter[zoneIndex] = meter;
|
||||
this.multiZoneEnable = !isZoneMeterDisabled();
|
||||
}
|
||||
|
||||
public int getAllocation(int allocationIndex) {
|
||||
return allocations[allocationIndex];
|
||||
}
|
||||
|
||||
public int[] getAllocations() {
|
||||
return Arrays.copyOf(allocations, allocations.length);
|
||||
}
|
||||
|
||||
public int getAllocationsSize() {
|
||||
return getZoneMeters().length;
|
||||
}
|
||||
|
||||
public void setAllocation(int allocationIndex, int zoneIndex) {
|
||||
allocations[allocationIndex] = zoneIndex;
|
||||
}
|
||||
|
||||
public boolean isEnable() {
|
||||
return timeExtension != TIME_EXTENSION_DISABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disables mowing using timeExtension.
|
||||
* disable: timeExtension = -100
|
||||
* enable: timeExtension > -100
|
||||
*
|
||||
*/
|
||||
public void setEnable(boolean enable) {
|
||||
if (enable && timeExtension == TIME_EXTENSION_DISABLE) {
|
||||
restoreTimeExtension();
|
||||
} else {
|
||||
storeTimeExtension();
|
||||
timeExtension = TIME_EXTENSION_DISABLE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores timeExtension to timeExtensionRestore for restore,
|
||||
*/
|
||||
private void storeTimeExtension() {
|
||||
if (this.timeExtension > TIME_EXTENSION_DISABLE) {
|
||||
this.timeExtensionRestore = this.timeExtension;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores timeExtension from timeExtensionRestore.
|
||||
*/
|
||||
private void restoreTimeExtension() {
|
||||
this.timeExtension = this.timeExtensionRestore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores zoneMeter to zoneMeterRestore for restore,
|
||||
*/
|
||||
private void storeZoneMeter() {
|
||||
if (!isZoneMeterDisabled()) {
|
||||
System.arraycopy(zoneMeter, 0, zoneMeterRestore, 0, zoneMeter.length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores zoneMeter from zoneMeterRestore.
|
||||
*/
|
||||
private void restoreZoneMeter() {
|
||||
System.arraycopy(zoneMeterRestore, 0, zoneMeter, 0, zoneMeter.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false if less than 2 meters are 0
|
||||
*/
|
||||
private boolean isZoneMeterDisabled() {
|
||||
return Arrays.stream(zoneMeter).sum() == 0;
|
||||
}
|
||||
|
||||
public int getMultiZoneCount() {
|
||||
return multiZoneSupported() ? product.lastStatus.payload.cfg.multizoneAllocations.size() : 0;
|
||||
}
|
||||
|
||||
public String getMqttCommandIn() {
|
||||
return product.mqttTopics.commandIn;
|
||||
}
|
||||
|
||||
public String getMqttCommandOut() {
|
||||
return product.mqttTopics.commandOut;
|
||||
}
|
||||
|
||||
public String getMacAddress() {
|
||||
return product.macAddress;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return product.id;
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
return getPayload().cfg.lg;
|
||||
}
|
||||
|
||||
public Payload getPayload() {
|
||||
return lastStatus.payload;
|
||||
}
|
||||
|
||||
public Dat getPayloadDat() {
|
||||
return getPayload().dat;
|
||||
}
|
||||
|
||||
public Cfg getPayloadCfg() {
|
||||
return getPayload().cfg;
|
||||
}
|
||||
|
||||
public void setStatus(Payload payload) {
|
||||
this.lastStatus = new LastStatus(payload);
|
||||
if (restoreZoneMeter && getStatusCode() != WorxLandroidStatusCodes.HOME
|
||||
&& getStatusCode() != WorxLandroidStatusCodes.START_SEQUENCE
|
||||
&& getStatusCode() != WorxLandroidStatusCodes.LEAVING_HOME
|
||||
&& getStatusCode() != WorxLandroidStatusCodes.SEARCHING_ZONE) {
|
||||
restoreZoneMeter = false;
|
||||
setZoneMeters(zoneMeterRestoreValues);
|
||||
sendCommand(new ZoneMeterCommand(getZoneMeters()));
|
||||
}
|
||||
|
||||
getSchedule().ifPresent(schedule -> {
|
||||
setTimeExtension(schedule.timeExtension);
|
||||
if (schedule.d != null) {
|
||||
updateSchedules(0, schedule.d);
|
||||
if (schedule.dd != null) {
|
||||
updateSchedules(1, schedule.dd);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Cfg cfg = getPayloadCfg();
|
||||
if (multiZoneSupported()) {
|
||||
for (int zoneIndex = 0; zoneIndex < cfg.multiZones.size(); zoneIndex++) {
|
||||
setZoneMeter(zoneIndex, cfg.multiZones.get(zoneIndex));
|
||||
}
|
||||
|
||||
for (int allocationIndex = 0; allocationIndex < cfg.multizoneAllocations.size(); allocationIndex++) {
|
||||
setAllocation(allocationIndex, cfg.multizoneAllocations.get(allocationIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSchedules(int scDSlot, List<List<String>> d) {
|
||||
Map<WorxLandroidDayCodes, @Nullable ScheduledDay> planning = schedules.get(scDSlot);
|
||||
EnumSet.allOf(WorxLandroidDayCodes.class).stream().forEach(dayCode -> {
|
||||
List<String> schedule = d.get(dayCode.code);
|
||||
planning.put(dayCode,
|
||||
new ScheduledDay(schedule.get(0), Integer.valueOf(schedule.get(1)), "1".equals(schedule.get(2))));
|
||||
});
|
||||
}
|
||||
|
||||
public Optional<Battery> getBattery() {
|
||||
return Optional.ofNullable(getPayloadDat().battery);
|
||||
}
|
||||
|
||||
public Optional<Rain> getRain() {
|
||||
return Optional.ofNullable(getPayloadDat().rain);
|
||||
}
|
||||
|
||||
public double getAngle(Axis axis) {
|
||||
return getPayloadDat().getAngle(axis);
|
||||
}
|
||||
|
||||
public Optional<Stat> getStats() {
|
||||
return Optional.ofNullable(getPayloadDat().st);
|
||||
}
|
||||
|
||||
public int getLastZone() {
|
||||
return getAllocation(getPayloadDat().lastZone);
|
||||
}
|
||||
|
||||
public long getCurrentBladeTime() {
|
||||
return getTotalBladeTime() - product.bladeWorkTimeReset;
|
||||
}
|
||||
|
||||
public long getTotalBladeTime() {
|
||||
return lastStatus.payload.dat.st.bladeWorkTime;
|
||||
}
|
||||
|
||||
public int getCurrentChargeCycles() {
|
||||
return product.batteryChargeCycles - product.batteryChargeCyclesReset;
|
||||
}
|
||||
|
||||
public int getTotalChargeCycles() {
|
||||
return lastStatus.payload.dat.battery.chargeCycle;
|
||||
}
|
||||
|
||||
public WorxLandroidStatusCodes getStatusCode() {
|
||||
return getPayloadDat().statusCode;
|
||||
}
|
||||
|
||||
public void setZoneTo(int zoneIndex) {
|
||||
zoneMeterRestoreValues = getZoneMeters();
|
||||
restoreZoneMeter = true;
|
||||
|
||||
int meter = getZoneMeter(zoneIndex);
|
||||
for (int index = 0; index < 4; index++) {
|
||||
setZoneMeter(index, meter);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendCommand(Object command) {
|
||||
logger.debug("send command: {}", command);
|
||||
mowerHandler.publishMessage(getMqttCommandIn(), command);
|
||||
}
|
||||
|
||||
public ZonedDateTime getLastUpdate() {
|
||||
return getPayloadCfg().getDateTime().atZone(product.timeZone);
|
||||
}
|
||||
|
||||
public Optional<Schedule> getSchedule() {
|
||||
return Optional.ofNullable(getPayloadCfg().sc);
|
||||
}
|
||||
|
||||
public Optional<Ots> getOneTimeSchedule() {
|
||||
return getSchedule().isPresent() ? Optional.ofNullable(getSchedule().get().ots) : Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.worxlandroid.internal.vo;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* {@link ScheduledDay} holds data of the schedule details for a given day
|
||||
*
|
||||
* @author Nils Billing - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ScheduledDay {
|
||||
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm");
|
||||
private static final int DEFAULT_DURATION = 15;
|
||||
|
||||
public static final ScheduledDay BLANK = new ScheduledDay("00:00", 0, false);
|
||||
|
||||
private LocalTime startTime = LocalTime.MIN;
|
||||
private boolean edgecut;
|
||||
private int durationRestore = DEFAULT_DURATION;
|
||||
private int duration;
|
||||
|
||||
public ScheduledDay(String hhMm, int newDuration, boolean edgecut) {
|
||||
this.startTime = LocalTime.parse(hhMm);
|
||||
this.duration = newDuration;
|
||||
this.edgecut = edgecut;
|
||||
}
|
||||
|
||||
public LocalTime getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(String hhMm) throws DateTimeParseException {
|
||||
startTime = LocalTime.parse(hhMm);
|
||||
}
|
||||
|
||||
public void setStartTime(Instant instant) {
|
||||
startTime = instant.atZone(ZoneOffset.UTC).toLocalTime();
|
||||
}
|
||||
|
||||
public int getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public void setDuration(int newDuration) {
|
||||
if (newDuration == 0 && duration > 0) {
|
||||
durationRestore = duration;
|
||||
}
|
||||
|
||||
duration = newDuration;
|
||||
}
|
||||
|
||||
public boolean isEdgecut() {
|
||||
return edgecut;
|
||||
}
|
||||
|
||||
public void setEdgecut(boolean edgecut) {
|
||||
this.edgecut = edgecut;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return duration != 0;
|
||||
}
|
||||
|
||||
public void setEnable(boolean newStatus) {
|
||||
setDuration(newStatus && duration == 0 ? durationRestore : 0);
|
||||
}
|
||||
|
||||
public Object[] asArray() {
|
||||
return new Object[] { startTime.format(TIME_FORMAT), duration, edgecut ? 1 : 0 };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<addon:addon id="worxlandroid" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
|
||||
|
||||
<type>binding</type>
|
||||
<name>WorxLandroid Binding</name>
|
||||
<description>This is the binding for Worx Landroid.</description>
|
||||
<connection>cloud</connection>
|
||||
|
||||
</addon:addon>
|
||||
@@ -0,0 +1,356 @@
|
||||
# add-on
|
||||
|
||||
addon.worxlandroid.name = WorxLandroid Binding
|
||||
addon.worxlandroid.description = This is the binding for Worx Landroid.
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.worxlandroid.bridge.label = Bridge Worx Landroid API
|
||||
thing-type.worxlandroid.bridge.description = Represents the API and handler for Worx Landroid.
|
||||
thing-type.worxlandroid.mower.label = Landroid Mower
|
||||
thing-type.worxlandroid.mower.description = Represents a Landroid Worx Mower
|
||||
thing-type.worxlandroid.mower.group.friday.label = Friday Schedule
|
||||
thing-type.worxlandroid.mower.group.friday2.label = Friday Slot 2 Schedule
|
||||
thing-type.worxlandroid.mower.group.monday.label = Monday Schedule
|
||||
thing-type.worxlandroid.mower.group.monday2.label = Monday Slot 2 Schedule
|
||||
thing-type.worxlandroid.mower.group.saturday.label = Saturday Schedule
|
||||
thing-type.worxlandroid.mower.group.saturday2.label = Saturday Slot 2 Schedule
|
||||
thing-type.worxlandroid.mower.group.sunday.label = Sunday Schedule
|
||||
thing-type.worxlandroid.mower.group.sunday2.label = Sunday Slot 2 Schedule
|
||||
thing-type.worxlandroid.mower.group.thursday.label = Thursday Schedule
|
||||
thing-type.worxlandroid.mower.group.thursday2.label = Thursday Slot 2 Schedule
|
||||
thing-type.worxlandroid.mower.group.tuesday.label = Tuesday Schedule
|
||||
thing-type.worxlandroid.mower.group.tuesday2.label = Tuesday Slot 2 Schedule
|
||||
thing-type.worxlandroid.mower.group.wednesday.label = Wednesday Schedule
|
||||
thing-type.worxlandroid.mower.group.wednesday2.label = Wednesday Slot 2 Schedule
|
||||
|
||||
# thing types config
|
||||
|
||||
thing-type.config.worxlandroid.bridge.password.label = Password
|
||||
thing-type.config.worxlandroid.bridge.password.description = Password to access the Landroid WebAPI.
|
||||
thing-type.config.worxlandroid.bridge.username.label = Username
|
||||
thing-type.config.worxlandroid.bridge.username.description = Username to access the Landroid WebAPI.
|
||||
thing-type.config.worxlandroid.mower.pollingInterval.label = Polling Interval
|
||||
thing-type.config.worxlandroid.mower.pollingInterval.description = Interval for polling in seconds
|
||||
thing-type.config.worxlandroid.mower.refreshStatusInterval.label = Refresh Status Interval
|
||||
thing-type.config.worxlandroid.mower.refreshStatusInterval.description = Interval for refreshing mower status in seconds
|
||||
thing-type.config.worxlandroid.mower.serialNumber.label = Serial Number
|
||||
thing-type.config.worxlandroid.mower.serialNumber.description = Serial number of the mower
|
||||
|
||||
# channel group types
|
||||
|
||||
channel-group-type.worxlandroid.aws-group-type.label = Aws
|
||||
channel-group-type.worxlandroid.aws-group-type.description = MQTT connection to AWS
|
||||
channel-group-type.worxlandroid.aws-group-type.channel.connected.label = Connected
|
||||
channel-group-type.worxlandroid.aws-group-type.channel.connected.description = Connection to AWS is alive
|
||||
channel-group-type.worxlandroid.aws-group-type.channel.poll.label = Poll AWS
|
||||
channel-group-type.worxlandroid.aws-group-type.channel.poll.description = Enables or disables polling Worx AWS
|
||||
channel-group-type.worxlandroid.battery-group-type.label = Battery
|
||||
channel-group-type.worxlandroid.battery-group-type.description = Battery channels of your mower
|
||||
channel-group-type.worxlandroid.battery-group-type.channel.charge-cycles.label = Charge Cycles
|
||||
channel-group-type.worxlandroid.battery-group-type.channel.charge-cycles-total.label = Total Charge Cycles
|
||||
channel-group-type.worxlandroid.common-group-type.label = Common
|
||||
channel-group-type.worxlandroid.common-group-type.description = Common channels of the mower
|
||||
channel-group-type.worxlandroid.common-group-type.channel.enable.label = Mowing enabled
|
||||
channel-group-type.worxlandroid.common-group-type.channel.online.label = Online
|
||||
channel-group-type.worxlandroid.common-group-type.channel.online.description = Online status of the mower
|
||||
channel-group-type.worxlandroid.common-group-type.channel.online-timestamp.label = Online Status Timestamp
|
||||
channel-group-type.worxlandroid.config-group-type.label = Configuration
|
||||
channel-group-type.worxlandroid.config-group-type.description = Configuration channels of your mower
|
||||
channel-group-type.worxlandroid.config-group-type.channel.command.label = Command
|
||||
channel-group-type.worxlandroid.day-sched-group-type.label = Daily Schedule
|
||||
channel-group-type.worxlandroid.day-sched-group-type.description = Schedule For Each Day
|
||||
channel-group-type.worxlandroid.day-sched-group-type.channel.edgecut.label = Edgecut
|
||||
channel-group-type.worxlandroid.day-sched-group-type.channel.enable.label = Active
|
||||
channel-group-type.worxlandroid.day-sched-group-type.channel.enable.description = Defines if this day schedule is active or not
|
||||
channel-group-type.worxlandroid.metrics-group-type.label = Metrics
|
||||
channel-group-type.worxlandroid.metrics-group-type.description = Stat channels of your mower
|
||||
channel-group-type.worxlandroid.metrics-group-type.channel.blade-time.label = Current Blade Time
|
||||
channel-group-type.worxlandroid.metrics-group-type.channel.blade-time-total.label = Total Blade Time
|
||||
channel-group-type.worxlandroid.metrics-group-type.channel.total-time.label = Total Time
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.label = Multi-Zone
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.description = Multi-Zones configuration of your mower
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.channel.allocation-0.label = Zone Allocation 1
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.channel.allocation-1.label = Zone Allocation 2
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.channel.allocation-2.label = Zone Allocation 3
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.channel.allocation-3.label = Zone Allocation 4
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.channel.allocation-4.label = Zone Allocation 5
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.channel.allocation-5.label = Zone Allocation 6
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.channel.allocation-6.label = Zone Allocation 7
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.channel.allocation-7.label = Zone Allocation 8
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.channel.allocation-8.label = Zone Allocation 9
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.channel.allocation-9.label = Zone Allocation 10
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.channel.enable.label = Multizone Enabled
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.channel.last-zone.label = Last Zone
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.channel.zone-1.label = Meters Zone 1
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.channel.zone-2.label = Meters Zone 2
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.channel.zone-3.label = Meters Zone 3
|
||||
channel-group-type.worxlandroid.multi-zones-group-type.channel.zone-4.label = Meters Zone 4
|
||||
channel-group-type.worxlandroid.orientation-group-type.label = Orientation
|
||||
channel-group-type.worxlandroid.orientation-group-type.description = Orientation of your mower
|
||||
channel-group-type.worxlandroid.orientation-group-type.channel.pitch.label = Pitch
|
||||
channel-group-type.worxlandroid.orientation-group-type.channel.roll.label = Roll
|
||||
channel-group-type.worxlandroid.orientation-group-type.channel.yaw.label = Yaw
|
||||
channel-group-type.worxlandroid.ot-sched-group-type.label = One-Time Schedule
|
||||
channel-group-type.worxlandroid.ot-sched-group-type.description = One time schedule configuration of your mower
|
||||
channel-group-type.worxlandroid.ot-sched-group-type.channel.edgecut.label = Schedule Edgecut
|
||||
channel-group-type.worxlandroid.rain-group-type.label = Rain
|
||||
channel-group-type.worxlandroid.rain-group-type.description = Data rain channels of your mower
|
||||
channel-group-type.worxlandroid.sched-group-type.label = Schedule
|
||||
channel-group-type.worxlandroid.sched-group-type.description = Schedule channels configuration of your mower
|
||||
channel-group-type.worxlandroid.sched-group-type.channel.next-start.label = Next Start
|
||||
channel-group-type.worxlandroid.sched-group-type.channel.next-start.description = Next mowing start based on schedule
|
||||
channel-group-type.worxlandroid.sched-group-type.channel.next-stop.label = Next Stop
|
||||
channel-group-type.worxlandroid.sched-group-type.channel.next-stop.description = Next mowing stop based on schedule
|
||||
channel-group-type.worxlandroid.wifi-group-type.label = Wi-Fi Information
|
||||
|
||||
# channel types
|
||||
|
||||
channel-type.worxlandroid.action-type.label = Action
|
||||
channel-type.worxlandroid.action-type.description = Action channel for your mower
|
||||
channel-type.worxlandroid.action-type.state.option.START = Start
|
||||
channel-type.worxlandroid.action-type.state.option.STOP = Stop
|
||||
channel-type.worxlandroid.action-type.state.option.HOME = Home
|
||||
channel-type.worxlandroid.axis-type.label = Axis
|
||||
channel-type.worxlandroid.battery-temp-type.label = Battery Temperature
|
||||
channel-type.worxlandroid.battery-temp-type.description = Current temperature of the battery
|
||||
channel-type.worxlandroid.charging-type.label = Battery Charging
|
||||
channel-type.worxlandroid.distance-type.label = Total Distance
|
||||
channel-type.worxlandroid.error-type.label = Error Code
|
||||
channel-type.worxlandroid.error-type.state.option.UNKNOWN = Unknown
|
||||
channel-type.worxlandroid.error-type.state.option.NO_ERR = No error
|
||||
channel-type.worxlandroid.error-type.state.option.TRAPPED = Trapped
|
||||
channel-type.worxlandroid.error-type.state.option.LIFTED = Lifted
|
||||
channel-type.worxlandroid.error-type.state.option.WIRE_MISSING = Wire missing
|
||||
channel-type.worxlandroid.error-type.state.option.OUTSIDE_WIRE = Outside wire
|
||||
channel-type.worxlandroid.error-type.state.option.RAINING = Raining
|
||||
channel-type.worxlandroid.error-type.state.option.CLOSE_DOOR_TO_MOW = Close door to mow
|
||||
channel-type.worxlandroid.error-type.state.option.CLOSE_DOOR_TO_GO_HOME = Close door to go home
|
||||
channel-type.worxlandroid.error-type.state.option.BLADE_MOTOR_BLOCKED = Blade motor blocked
|
||||
channel-type.worxlandroid.error-type.state.option.WHEEL_MOTOR_BLOKED = Wheel motor blocked
|
||||
channel-type.worxlandroid.error-type.state.option.TRAPPED_TIMEOUT = Trapped timeout
|
||||
channel-type.worxlandroid.error-type.state.option.UPSIDE_DOWN = Upside down
|
||||
channel-type.worxlandroid.error-type.state.option.BATTERY_LOW = Battery low
|
||||
channel-type.worxlandroid.error-type.state.option.REVERSE_WIRE = Reverse wire
|
||||
channel-type.worxlandroid.error-type.state.option.CHARGE_ERROR = Charge error
|
||||
channel-type.worxlandroid.error-type.state.option.TIMEOUT_FINDING_HOME = Timeout finding home
|
||||
channel-type.worxlandroid.error-type.state.option.MOWER_LOCKED = Mower locked
|
||||
channel-type.worxlandroid.error-type.state.option.BATTERY_OVER_TEMPERATURE = Battery over temperature
|
||||
channel-type.worxlandroid.error-type.state.option.MOWER_OUTSIDE_WIRE = Mower outside wire
|
||||
channel-type.worxlandroid.lock-type.label = Lock mower
|
||||
channel-type.worxlandroid.lock-type.description = Lock or unlock your mower.
|
||||
channel-type.worxlandroid.metrics-duration.label = Time
|
||||
channel-type.worxlandroid.number-ro.label = A Number
|
||||
channel-type.worxlandroid.rain-counter.label = Rain Counter
|
||||
channel-type.worxlandroid.rain-delay-type.label = Delay
|
||||
channel-type.worxlandroid.rain-state-type.label = State
|
||||
channel-type.worxlandroid.rssi.label = RSSI
|
||||
channel-type.worxlandroid.rssi.description = Received signal strength indicator
|
||||
channel-type.worxlandroid.schedule-duration-type.label = Duration
|
||||
channel-type.worxlandroid.schedule-mode-type.label = Schedule Mode
|
||||
channel-type.worxlandroid.schedule-mode-type.description = Sets Normal or Party mode
|
||||
channel-type.worxlandroid.schedule-mode-type.state.option.NORMAL = Normal
|
||||
channel-type.worxlandroid.schedule-mode-type.state.option.PARTY = Party
|
||||
channel-type.worxlandroid.schedule-time.label = Start Time
|
||||
channel-type.worxlandroid.schedule-time.description = Start time of the mowing on this day
|
||||
channel-type.worxlandroid.schedule-time.state.pattern = %1$tH:%1$tM
|
||||
channel-type.worxlandroid.status-type.label = Status Code
|
||||
channel-type.worxlandroid.status-type.state.option.UNKNOWN = Unknown
|
||||
channel-type.worxlandroid.status-type.state.option.IDLE = Idle
|
||||
channel-type.worxlandroid.status-type.state.option.HOME = Home
|
||||
channel-type.worxlandroid.status-type.state.option.START_SEQUENCE = Start sequence
|
||||
channel-type.worxlandroid.status-type.state.option.LEAVING_HOME = Leaving home
|
||||
channel-type.worxlandroid.status-type.state.option.FOLLOW_WIRE = Follow wire
|
||||
channel-type.worxlandroid.status-type.state.option.SEARCHING_HOME = Searching home
|
||||
channel-type.worxlandroid.status-type.state.option.SEARCHING_WIRE = Searching wire
|
||||
channel-type.worxlandroid.status-type.state.option.MOWING = Mowing
|
||||
channel-type.worxlandroid.status-type.state.option.LIFTED = Lifted
|
||||
channel-type.worxlandroid.status-type.state.option.TRAPPED = Trapped
|
||||
channel-type.worxlandroid.status-type.state.option.BLADE_BLOCKED = Blade blocked
|
||||
channel-type.worxlandroid.status-type.state.option.DEBUG = Debug
|
||||
channel-type.worxlandroid.status-type.state.option.REMOTE_CONTROL = Remote control
|
||||
channel-type.worxlandroid.status-type.state.option.ESCAPE_FROM_OLM = Escape from OLM
|
||||
channel-type.worxlandroid.status-type.state.option.GOING_HOME = Going home
|
||||
channel-type.worxlandroid.status-type.state.option.ZONE_TRAINING = Zone training
|
||||
channel-type.worxlandroid.status-type.state.option.BORDER_CUT = Border cut
|
||||
channel-type.worxlandroid.status-type.state.option.SEARCHING_ZONE = Searching zone
|
||||
channel-type.worxlandroid.status-type.state.option.PAUSE = Pause
|
||||
channel-type.worxlandroid.status-type.state.option.MANUAL_STOP = Manual stop
|
||||
channel-type.worxlandroid.switch-ro.label = Read Only Switch
|
||||
channel-type.worxlandroid.switch-rw.label = Read Write Switch
|
||||
channel-type.worxlandroid.time-extension-type.label = Schedule Time Extension
|
||||
channel-type.worxlandroid.timestamp.label = Last Update
|
||||
channel-type.worxlandroid.timestamp.description = Last device update
|
||||
channel-type.worxlandroid.voltage-type.label = Battery Voltage
|
||||
channel-type.worxlandroid.voltage-type.description = Battery voltage reported by the mower
|
||||
channel-type.worxlandroid.zone-meter-type.label = Length of the zone
|
||||
channel-type.worxlandroid.zone-type.label = Zone Number
|
||||
channel-type.worxlandroid.zone-type.state.option.0 = Zone 1
|
||||
channel-type.worxlandroid.zone-type.state.option.1 = Zone 2
|
||||
channel-type.worxlandroid.zone-type.state.option.2 = Zone 3
|
||||
channel-type.worxlandroid.zone-type.state.option.3 = Zone 4
|
||||
|
||||
# channel group types
|
||||
|
||||
channel-group-type.worxlandroid.battery-group-type.channel.charge-cycles-current.label = Current Charge Cycles
|
||||
channel-group-type.worxlandroid.metrics-group-type.channel.blade-time-current.label = Current Blade Time
|
||||
|
||||
# thing types config
|
||||
|
||||
thing-type.config.worxlandroid.bridge.reconnectInterval.label = Reconnect Interval
|
||||
thing-type.config.worxlandroid.bridge.reconnectInterval.description = Interval for reconnecting in seconds (after 600 seconds of inactivity, the connection is closed)
|
||||
|
||||
# channel group types
|
||||
|
||||
channel-group-type.worxlandroid.battery-group-type.channel.charge-cycle.label = Battery Charge Cycle Total
|
||||
channel-group-type.worxlandroid.battery-group-type.channel.charge-cycle-current.label = Battery Charge Cycle Current
|
||||
channel-group-type.worxlandroid.common-group-type.channel.poll.label = Poll AWS
|
||||
channel-group-type.worxlandroid.common-group-type.channel.poll.description = Enables or disables polling Worx AWS
|
||||
channel-group-type.worxlandroid.metrics-group-type.channel.current-blade-time.label = Current Blade Time
|
||||
channel-group-type.worxlandroid.metrics-group-type.channel.total-blade-time.label = Total Blade Time
|
||||
channel-group-type.worxlandroid.rain-group-type.channel.counter.label = Counter
|
||||
|
||||
# channel types
|
||||
|
||||
channel-type.worxlandroid.schedule-mode-type.state.option.1 = Normal
|
||||
channel-type.worxlandroid.schedule-mode-type.state.option.2 = Party
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.worxlandroid.mower.group.cfgScFriday.label = Schedule For Friday
|
||||
thing-type.worxlandroid.mower.group.cfgScFriday2.label = Schedule For Friday Slot 2
|
||||
thing-type.worxlandroid.mower.group.cfgScMonday.label = Schedule For Monday
|
||||
thing-type.worxlandroid.mower.group.cfgScMonday2.label = Schedule For Monday Slot 2
|
||||
thing-type.worxlandroid.mower.group.cfgScSaturday.label = Schedule For Saturday
|
||||
thing-type.worxlandroid.mower.group.cfgScSaturday2.label = Schedule For Saturday Slot 2
|
||||
thing-type.worxlandroid.mower.group.cfgScSunday.label = Schedule For Sunday
|
||||
thing-type.worxlandroid.mower.group.cfgScSunday2.label = Schedule For Sunday Slot 2
|
||||
thing-type.worxlandroid.mower.group.cfgScThursday.label = Schedule For Thursday
|
||||
thing-type.worxlandroid.mower.group.cfgScThursday2.label = Schedule For Thursday Slot 2
|
||||
thing-type.worxlandroid.mower.group.cfgScTuesday.label = Schedule For Tuesday
|
||||
thing-type.worxlandroid.mower.group.cfgScTuesday2.label = Schedule For Tuesday Slot 2
|
||||
thing-type.worxlandroid.mower.group.cfgScWednesday.label = Schedule For Wednesday
|
||||
thing-type.worxlandroid.mower.group.cfgScWednesday2.label = Schedule For Wednesday Slot 2
|
||||
|
||||
# thing types config
|
||||
|
||||
thing-type.config.worxlandroid.mower.reconnectInterval.label = Reconnect Interval
|
||||
thing-type.config.worxlandroid.mower.reconnectInterval.description = Interval for reconnecting in seconds (after 10 minutes / 600 seconds of inactivity, the connection is closed)
|
||||
thing-type.config.worxlandroid.bridge.webapiPassword.label = Password
|
||||
thing-type.config.worxlandroid.bridge.webapiPassword.description = Password to access the Landroid WebAPI.
|
||||
thing-type.config.worxlandroid.bridge.webapiUsername.label = Username
|
||||
thing-type.config.worxlandroid.bridge.webapiUsername.description = Username to access the Landroid WebAPI.
|
||||
|
||||
# channel group types
|
||||
|
||||
channel-group-type.worxlandroid.cfgCommon-type.label = Configuration common channels
|
||||
channel-group-type.worxlandroid.cfgCommon-type.description = Configuration common channels of your mower
|
||||
channel-group-type.worxlandroid.cfgMultiZones-type.label = Configuration multi zone channels
|
||||
channel-group-type.worxlandroid.cfgMultiZones-type.description = Configuration multi zones of your mower
|
||||
channel-group-type.worxlandroid.cfgMultiZones-type.channel.allocation0.label = Zone Allocation 1
|
||||
channel-group-type.worxlandroid.cfgMultiZones-type.channel.allocation1.label = Zone Allocation 2
|
||||
channel-group-type.worxlandroid.cfgMultiZones-type.channel.allocation2.label = Zone Allocation 3
|
||||
channel-group-type.worxlandroid.cfgMultiZones-type.channel.allocation3.label = Zone Allocation 4
|
||||
channel-group-type.worxlandroid.cfgMultiZones-type.channel.allocation4.label = Zone Allocation 5
|
||||
channel-group-type.worxlandroid.cfgMultiZones-type.channel.allocation5.label = Zone Allocation 6
|
||||
channel-group-type.worxlandroid.cfgMultiZones-type.channel.allocation6.label = Zone Allocation 7
|
||||
channel-group-type.worxlandroid.cfgMultiZones-type.channel.allocation7.label = Zone Allocation 8
|
||||
channel-group-type.worxlandroid.cfgMultiZones-type.channel.allocation8.label = Zone Allocation 9
|
||||
channel-group-type.worxlandroid.cfgMultiZones-type.channel.allocation9.label = Zone Allocation 10
|
||||
channel-group-type.worxlandroid.cfgMultiZones-type.channel.enable.label = Multizone enabled
|
||||
channel-group-type.worxlandroid.cfgMultiZones-type.channel.zone1Meter.label = Meters Zone 1
|
||||
channel-group-type.worxlandroid.cfgMultiZones-type.channel.zone2Meter.label = Meters Zone 2
|
||||
channel-group-type.worxlandroid.cfgMultiZones-type.channel.zone3Meter.label = Meters Zone 3
|
||||
channel-group-type.worxlandroid.cfgMultiZones-type.channel.zone4Meter.label = Meters Zone 4
|
||||
channel-group-type.worxlandroid.cfgOneTimeSc-type.label = Configuration one time schedule channels
|
||||
channel-group-type.worxlandroid.cfgOneTimeSc-type.description = Configuration one time schedule channels of your mower
|
||||
channel-group-type.worxlandroid.cfgSc-type.label = Schedule For
|
||||
channel-group-type.worxlandroid.cfgSc-type.description = Schedule For of your mower
|
||||
channel-group-type.worxlandroid.cfgScDay-type.label = Schedule For for each day
|
||||
channel-group-type.worxlandroid.cfgScDay-type.description = Schedule For for each day
|
||||
channel-group-type.worxlandroid.common-type.label = Common channels
|
||||
channel-group-type.worxlandroid.common-type.description = Common channels of your mower
|
||||
channel-group-type.worxlandroid.common-type.channel.enable.label = Mowing enabled
|
||||
channel-group-type.worxlandroid.datBattery-type.label = Data battery channels
|
||||
channel-group-type.worxlandroid.datBattery-type.description = Data battery channels of your mower
|
||||
channel-group-type.worxlandroid.datBattery-type.channel.batteryChargeCycle.label = Battery Charge Cycle Total
|
||||
channel-group-type.worxlandroid.datBattery-type.channel.batteryChargeCycleCurrent.label = Battery Charge Cycle Current
|
||||
channel-group-type.worxlandroid.datCommon-type.label = Data common channels
|
||||
channel-group-type.worxlandroid.datCommon-type.description = Data common channels of your mower
|
||||
channel-group-type.worxlandroid.datDmp-type.label = Data dmp channels
|
||||
channel-group-type.worxlandroid.datDmp-type.description = Data dmp channels of your mower
|
||||
channel-group-type.worxlandroid.datRain-type.label = Data rain channels
|
||||
channel-group-type.worxlandroid.datRain-type.description = Data rain channels of your mower
|
||||
channel-group-type.worxlandroid.datSt-type.label = Data st channels
|
||||
channel-group-type.worxlandroid.datSt-type.description = Data st channels of your mower
|
||||
channel-group-type.worxlandroid.datSt-type.channel.currentBladeTime.label = Current Blade Time
|
||||
channel-group-type.worxlandroid.datSt-type.channel.totalBladeTime.label = Total Blade Time
|
||||
channel-group-type.worxlandroid.datSt-type.channel.totalTime.label = Total Time
|
||||
|
||||
# channel types
|
||||
|
||||
channel-type.worxlandroid.chAction.label = Action
|
||||
channel-type.worxlandroid.chAction.description = Action channel for your mower
|
||||
channel-type.worxlandroid.chAction.state.option.START = Start
|
||||
channel-type.worxlandroid.chAction.state.option.STOP = Stop
|
||||
channel-type.worxlandroid.chAction.state.option.HOME = Home
|
||||
channel-type.worxlandroid.chAllocation.label = Zone Allocation
|
||||
channel-type.worxlandroid.chAllocation.state.option.0 = Zone 1
|
||||
channel-type.worxlandroid.chAllocation.state.option.1 = Zone 2
|
||||
channel-type.worxlandroid.chAllocation.state.option.2 = Zone 3
|
||||
channel-type.worxlandroid.chAllocation.state.option.3 = Zone 4
|
||||
channel-type.worxlandroid.chBatteryChargeCycle.label = Battery Charge Cycle
|
||||
channel-type.worxlandroid.chBatteryCharging.label = Battery Charging
|
||||
channel-type.worxlandroid.chBatteryLevel.label = Battery Level
|
||||
channel-type.worxlandroid.chBatteryTemperature.label = Battery Temperature
|
||||
channel-type.worxlandroid.chBatteryVoltage.label = Battery Voltage
|
||||
channel-type.worxlandroid.chCommand.label = Command
|
||||
channel-type.worxlandroid.chEnable.label = Activation / Deactivation
|
||||
channel-type.worxlandroid.chErrorCode.label = Error Code
|
||||
channel-type.worxlandroid.chErrorDescription.label = Error Description
|
||||
channel-type.worxlandroid.chFirmware.label = Firmware
|
||||
channel-type.worxlandroid.chId.label = Id
|
||||
channel-type.worxlandroid.chLanguage.label = Language
|
||||
channel-type.worxlandroid.chLastUpdate.label = Last Update
|
||||
channel-type.worxlandroid.chLastUpdateOnlineStatus.label = Last Update Online Status
|
||||
channel-type.worxlandroid.chLastZone.label = Last Zone
|
||||
channel-type.worxlandroid.chLastZone.state.option.0 = Zone 1
|
||||
channel-type.worxlandroid.chLastZone.state.option.1 = Zone 2
|
||||
channel-type.worxlandroid.chLastZone.state.option.2 = Zone 3
|
||||
channel-type.worxlandroid.chLastZone.state.option.3 = Zone 4
|
||||
channel-type.worxlandroid.chLock.label = Lock mower
|
||||
channel-type.worxlandroid.chLock.description = Lock or unlock your mower.
|
||||
channel-type.worxlandroid.chMacAdress.label = MacAdress
|
||||
channel-type.worxlandroid.chOnline.label = Online
|
||||
channel-type.worxlandroid.chPitch.label = Pitch
|
||||
channel-type.worxlandroid.chPoll.label = Poll Worx AWS
|
||||
channel-type.worxlandroid.chPoll.description = Poll Worx AWS
|
||||
channel-type.worxlandroid.chRainCounter.label = Rain counter
|
||||
channel-type.worxlandroid.chRainDelay.label = Rain Delay
|
||||
channel-type.worxlandroid.chRainState.label = Rain state
|
||||
channel-type.worxlandroid.chRoll.label = Roll
|
||||
channel-type.worxlandroid.chScheduleDuration.label = Schedule Duration
|
||||
channel-type.worxlandroid.chScheduleEdgecut.label = Schedule Edgecut
|
||||
channel-type.worxlandroid.chScheduleMode.label = Schedule Mode (Party)
|
||||
channel-type.worxlandroid.chScheduleMode.state.option.1 = Normal
|
||||
channel-type.worxlandroid.chScheduleMode.state.option.2 = Party
|
||||
channel-type.worxlandroid.chScheduleStartHour.label = Schedule Start Hour
|
||||
channel-type.worxlandroid.chScheduleStartMinutes.label = Schedule Start Minutes
|
||||
channel-type.worxlandroid.chScheduleTimeExtension.label = Schedule Time Extension
|
||||
channel-type.worxlandroid.chStatusCode.label = Status Code
|
||||
channel-type.worxlandroid.chStatusDescription.label = Status Description
|
||||
channel-type.worxlandroid.chTimeMinutes.label = Time
|
||||
channel-type.worxlandroid.chTotalDistance.label = Total Distance
|
||||
channel-type.worxlandroid.chWifiQuality.label = Wifi Quality
|
||||
channel-type.worxlandroid.chYaw.label = Yaw
|
||||
channel-type.worxlandroid.chZoneMeter.label = Meters of zone
|
||||
|
||||
# error messages
|
||||
|
||||
conf-error-no-username = Cannot connect to Landroid bridge as no username is configured
|
||||
conf-error-no-password = Cannot connect to Landroid bridge as no password is configured
|
||||
conf-error-no-serial = No serial number configured for this mower
|
||||
oauth-connection-error = Error getting access token
|
||||
oauth-refresh-error = To many token refresh failed
|
||||
oauth-connection-error = Unable to connect to Worx Api (403) - check your credentials
|
||||
incorrect-bridge = Wrong bridge type
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bindingId="worxlandroid"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
<bridge-type id="bridge">
|
||||
<label>Bridge Worx Landroid API</label>
|
||||
<description>Represents the API and handler for Worx Landroid.</description>
|
||||
<config-description>
|
||||
<parameter name="username" type="text">
|
||||
<label>Username</label>
|
||||
<description>Username to access the Landroid WebAPI.</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
<parameter name="password" type="text">
|
||||
<context>password</context>
|
||||
<label>Password</label>
|
||||
<description>Password to access the Landroid WebAPI.</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,241 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="worxlandroid"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<channel-type id="timestamp">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Last Update</label>
|
||||
<description>Last device update</description>
|
||||
<category>Time</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="battery-temp-type" advanced="true">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Battery Temperature</label>
|
||||
<description>Current temperature of the battery</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="voltage-type">
|
||||
<item-type>Number:ElectricPotential</item-type>
|
||||
<label>Battery Voltage</label>
|
||||
<description>Battery voltage reported by the mower</description>
|
||||
<category>Energy</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="axis-type">
|
||||
<item-type>Number:Angle</item-type>
|
||||
<label>Axis</label>
|
||||
<category>Incline</category>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="distance-type">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Total Distance</label>
|
||||
<category>oh:worxlandroid:distance</category>
|
||||
<state readOnly="true" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="metrics-duration">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Time</label>
|
||||
<category>time</category>
|
||||
<state readOnly="true" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="status-type">
|
||||
<item-type>String</item-type>
|
||||
<label>Status Code</label>
|
||||
<state readOnly="true" pattern="%s">
|
||||
<options>
|
||||
<option value="UNKNOWN">Unknown</option>
|
||||
<option value="IDLE">Idle</option>
|
||||
<option value="HOME">Home</option>
|
||||
<option value="START_SEQUENCE">Start sequence</option>
|
||||
<option value="LEAVING_HOME">Leaving home</option>
|
||||
<option value="FOLLOW_WIRE">Follow wire</option>
|
||||
<option value="SEARCHING_HOME">Searching home</option>
|
||||
<option value="SEARCHING_WIRE">Searching wire</option>
|
||||
<option value="MOWING">Mowing</option>
|
||||
<option value="LIFTED">Lifted</option>
|
||||
<option value="TRAPPED">Trapped</option>
|
||||
<option value="BLADE_BLOCKED">Blade blocked</option>
|
||||
<option value="DEBUG">Debug</option>
|
||||
<option value="REMOTE_CONTROL">Remote control</option>
|
||||
<option value="ESCAPE_FROM_OLM">Escape from OLM</option>
|
||||
<option value="GOING_HOME">Going home</option>
|
||||
<option value="ZONE_TRAINING">Zone training</option>
|
||||
<option value="BORDER_CUT">Border cut</option>
|
||||
<option value="SEARCHING_ZONE">Searching zone</option>
|
||||
<option value="PAUSE">Pause</option>
|
||||
<option value="MANUAL_STOP">Manual stop</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="error-type">
|
||||
<item-type>String</item-type>
|
||||
<label>Error Code</label>
|
||||
<category>Error</category>
|
||||
<state readOnly="true" pattern="%s">
|
||||
<options>
|
||||
<option value="UNKNOWN">Unknown</option>
|
||||
<option value="NO_ERR">No error</option>
|
||||
<option value="TRAPPED">Trapped</option>
|
||||
<option value="LIFTED">Lifted</option>
|
||||
<option value="WIRE_MISSING">Wire missing</option>
|
||||
<option value="OUTSIDE_WIRE">Outside wire</option>
|
||||
<option value="RAINING">Raining</option>
|
||||
<option value="CLOSE_DOOR_TO_MOW">Close door to mow</option>
|
||||
<option value="CLOSE_DOOR_TO_GO_HOME">Close door to go home</option>
|
||||
<option value="BLADE_MOTOR_BLOCKED">Blade motor blocked</option>
|
||||
<option value="WHEEL_MOTOR_BLOKED">Wheel motor blocked</option>
|
||||
<option value="TRAPPED_TIMEOUT">Trapped timeout</option>
|
||||
<option value="UPSIDE_DOWN">Upside down</option>
|
||||
<option value="BATTERY_LOW">Battery low</option>
|
||||
<option value="REVERSE_WIRE">Reverse wire</option>
|
||||
<option value="CHARGE_ERROR">Charge error</option>
|
||||
<option value="TIMEOUT_FINDING_HOME">Timeout finding home</option>
|
||||
<option value="MOWER_LOCKED">Mower locked</option>
|
||||
<option value="BATTERY_OVER_TEMPERATURE">Battery over temperature</option>
|
||||
<option value="MOWER_OUTSIDE_WIRE">Mower outside wire</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="time-extension-type">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Schedule Time Extension</label>
|
||||
|
||||
<state readOnly="false" min="-100" max="100" step="10" pattern="%d %%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="schedule-mode-type">
|
||||
<item-type>String</item-type>
|
||||
<label>Schedule Mode</label>
|
||||
<description>Sets Normal or Party mode</description>
|
||||
|
||||
<state readOnly="false">
|
||||
<options>
|
||||
<option value="NORMAL">Normal</option>
|
||||
<option value="PARTY">Party</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="schedule-duration-type">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Duration</label>
|
||||
<state readOnly="false" min="0" max="1425" step="15" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="zone-meter-type">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Length of the zone</label>
|
||||
<category>oh:worxlandroid:distance</category>
|
||||
<state readOnly="false" min="0" step="1" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="zone-type">
|
||||
<item-type>Number</item-type>
|
||||
<label>Zone Number</label>
|
||||
<category>oh:worxlandroid:zones</category>
|
||||
<state readOnly="false">
|
||||
<options>
|
||||
<option value="0">Zone 1</option>
|
||||
<option value="1">Zone 2</option>
|
||||
<option value="2">Zone 3</option>
|
||||
<option value="3">Zone 4</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="action-type">
|
||||
<item-type>String</item-type>
|
||||
<label>Action</label>
|
||||
<description>Action channel for your mower</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="START">Start</option>
|
||||
<option value="STOP">Stop</option>
|
||||
<option value="HOME">Home</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="switch-rw">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Read Write Switch</label>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="switch-ro">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Read Only Switch</label>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="charging-type">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Battery Charging</label>
|
||||
<category>oh:worxlandroid:charging</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="lock-type">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Lock mower</label>
|
||||
<description>Lock or unlock your mower.</description>
|
||||
<category>oh:worxlandroid:lock</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="number-ro">
|
||||
<item-type>Number</item-type>
|
||||
<label>A Number</label>
|
||||
<category>oh:worxlandroid:counter</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="schedule-time">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Start Time</label>
|
||||
<description>Start time of the mowing on this day</description>
|
||||
<category>Time</category>
|
||||
<state readOnly="false" pattern="%1$tH:%1$tM"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rain-delay-type">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Delay</label>
|
||||
<category>Time</category>
|
||||
<state min="0" max="750" step="30" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rain-counter">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Rain Counter</label>
|
||||
<category>Time</category>
|
||||
<state min="0" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rain-state-type">
|
||||
<item-type>Switch</item-type>
|
||||
<label>State</label>
|
||||
<category>oh:worxlandroid:rain</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rssi" advanced="true">
|
||||
<item-type unitHint="dBm">Number:Power</item-type>
|
||||
<label>RSSI</label>
|
||||
<description>Received signal strength indicator</description>
|
||||
<category>QualityOfService</category>
|
||||
<state readOnly="true" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,222 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="worxlandroid"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<channel-group-type id="common-group-type">
|
||||
<label>Common</label>
|
||||
<description>Common channels of the mower</description>
|
||||
<channels>
|
||||
<channel id="status" typeId="status-type"/>
|
||||
<channel id="error" typeId="error-type"/>
|
||||
<channel id="online" typeId="switch-ro">
|
||||
<label>Online</label>
|
||||
<description>Online status of the mower</description>
|
||||
</channel>
|
||||
<channel id="online-timestamp" typeId="timestamp">
|
||||
<label>Online Status Timestamp</label>
|
||||
</channel>
|
||||
<channel id="action" typeId="action-type"/>
|
||||
<channel id="enable" typeId="switch-rw">
|
||||
<label>Mowing enabled</label>
|
||||
</channel>
|
||||
<channel id="lock" typeId="lock-type"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="aws-group-type">
|
||||
<label>Aws</label>
|
||||
<description>MQTT connection to AWS</description>
|
||||
<channels>
|
||||
<channel id="poll" typeId="switch-rw">
|
||||
<label>Poll AWS</label>
|
||||
<description>Enables or disables polling Worx AWS</description>
|
||||
</channel>
|
||||
<channel id="connected" typeId="switch-ro">
|
||||
<label>Connected</label>
|
||||
<description>Connection to AWS is alive</description>
|
||||
</channel>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="config-group-type">
|
||||
<label>Configuration</label>
|
||||
<description>Configuration channels of your mower</description>
|
||||
<channels>
|
||||
<channel id="timestamp" typeId="timestamp"/>
|
||||
<channel id="command" typeId="number-ro">
|
||||
<label>Command</label>
|
||||
</channel>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="multi-zones-group-type">
|
||||
<label>Multi-Zone</label>
|
||||
<description>Multi-Zones configuration of your mower</description>
|
||||
<channels>
|
||||
<channel id="enable" typeId="switch-rw">
|
||||
<label>Multizone Enabled</label>
|
||||
</channel>
|
||||
<channel id="last-zone" typeId="zone-type">
|
||||
<label>Last Zone</label>
|
||||
</channel>
|
||||
<channel id="zone-1" typeId="zone-meter-type">
|
||||
<label>Meters Zone 1</label>
|
||||
</channel>
|
||||
<channel id="zone-2" typeId="zone-meter-type">
|
||||
<label>Meters Zone 2</label>
|
||||
</channel>
|
||||
<channel id="zone-3" typeId="zone-meter-type">
|
||||
<label>Meters Zone 3</label>
|
||||
</channel>
|
||||
<channel id="zone-4" typeId="zone-meter-type">
|
||||
<label>Meters Zone 4</label>
|
||||
</channel>
|
||||
<channel id="allocation-0" typeId="zone-type">
|
||||
<label>Zone Allocation 1</label>
|
||||
</channel>
|
||||
<channel id="allocation-1" typeId="zone-type">
|
||||
<label>Zone Allocation 2</label>
|
||||
</channel>
|
||||
<channel id="allocation-2" typeId="zone-type">
|
||||
<label>Zone Allocation 3</label>
|
||||
</channel>
|
||||
<channel id="allocation-3" typeId="zone-type">
|
||||
<label>Zone Allocation 4</label>
|
||||
</channel>
|
||||
<channel id="allocation-4" typeId="zone-type">
|
||||
<label>Zone Allocation 5</label>
|
||||
</channel>
|
||||
<channel id="allocation-5" typeId="zone-type">
|
||||
<label>Zone Allocation 6</label>
|
||||
</channel>
|
||||
<channel id="allocation-6" typeId="zone-type">
|
||||
<label>Zone Allocation 7</label>
|
||||
</channel>
|
||||
<channel id="allocation-7" typeId="zone-type">
|
||||
<label>Zone Allocation 8</label>
|
||||
</channel>
|
||||
<channel id="allocation-8" typeId="zone-type">
|
||||
<label>Zone Allocation 9</label>
|
||||
</channel>
|
||||
<channel id="allocation-9" typeId="zone-type">
|
||||
<label>Zone Allocation 10</label>
|
||||
</channel>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="battery-group-type">
|
||||
<label>Battery</label>
|
||||
<description>Battery channels of your mower</description>
|
||||
<channels>
|
||||
<channel id="temperature" typeId="battery-temp-type"/>
|
||||
<channel id="voltage" typeId="voltage-type"/>
|
||||
<channel id="level" typeId="system.battery-level"/>
|
||||
<channel id="charge-cycles" typeId="number-ro">
|
||||
<label>Charge Cycles</label>
|
||||
</channel>
|
||||
<channel id="charge-cycles-total" typeId="number-ro">
|
||||
<label>Total Charge Cycles</label>
|
||||
</channel>
|
||||
<channel id="charging" typeId="charging-type"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="orientation-group-type">
|
||||
<label>Orientation</label>
|
||||
<description>Orientation of your mower</description>
|
||||
<channels>
|
||||
<channel id="pitch" typeId="axis-type">
|
||||
<label>Pitch</label>
|
||||
</channel>
|
||||
<channel id="roll" typeId="axis-type">
|
||||
<label>Roll</label>
|
||||
</channel>
|
||||
<channel id="yaw" typeId="axis-type">
|
||||
<label>Yaw</label>
|
||||
</channel>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="metrics-group-type">
|
||||
<label>Metrics</label>
|
||||
<description>Stat channels of your mower</description>
|
||||
<channels>
|
||||
<channel id="blade-time" typeId="metrics-duration">
|
||||
<label>Current Blade Time</label>
|
||||
</channel>
|
||||
<channel id="blade-time-total" typeId="metrics-duration">
|
||||
<label>Total Blade Time</label>
|
||||
</channel>
|
||||
<channel id="distance" typeId="distance-type"/>
|
||||
<channel id="total-time" typeId="metrics-duration">
|
||||
<label>Total Time</label>
|
||||
</channel>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="sched-group-type">
|
||||
<label>Schedule</label>
|
||||
<description>Schedule channels configuration of your mower</description>
|
||||
<channels>
|
||||
<channel id="mode" typeId="schedule-mode-type"/>
|
||||
<channel id="time-extension" typeId="time-extension-type"/>
|
||||
<channel id="next-start" typeId="timestamp">
|
||||
<label>Next Start</label>
|
||||
<description>Next mowing start based on schedule</description>
|
||||
</channel>
|
||||
<channel id="next-stop" typeId="timestamp">
|
||||
<label>Next Stop</label>
|
||||
<description>Next mowing stop based on schedule</description>
|
||||
</channel>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="ot-sched-group-type">
|
||||
<label>One-Time Schedule</label>
|
||||
<description>One time schedule configuration of your mower</description>
|
||||
<channels>
|
||||
<channel id="edgecut" typeId="switch-rw">
|
||||
<label>Schedule Edgecut</label>
|
||||
</channel>
|
||||
<channel id="duration" typeId="schedule-duration-type"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
|
||||
<channel-group-type id="day-sched-group-type">
|
||||
<label>Daily Schedule</label>
|
||||
<description>Schedule For Each Day</description>
|
||||
<channels>
|
||||
<channel id="enable" typeId="switch-rw">
|
||||
<label>Active</label>
|
||||
<description>Defines if this day schedule is active or not</description>
|
||||
</channel>
|
||||
<channel id="time" typeId="schedule-time"/>
|
||||
<channel id="duration" typeId="schedule-duration-type"/>
|
||||
<channel id="edgecut" typeId="switch-rw">
|
||||
<label>Edgecut</label>
|
||||
</channel>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="rain-group-type">
|
||||
<label>Rain</label>
|
||||
<description>Data rain channels of your mower</description>
|
||||
<channels>
|
||||
<channel id="state" typeId="rain-state-type"/>
|
||||
<channel id="counter" typeId="rain-counter"/>
|
||||
<channel id="delay" typeId="rain-delay-type"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="wifi-group-type">
|
||||
<label>Wi-Fi Information</label>
|
||||
<channels>
|
||||
<channel id="rssi" typeId="rssi"/>
|
||||
<channel id="wifi-quality" typeId="system.signal-strength"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="worxlandroid"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="mower">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="bridge"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Landroid Mower</label>
|
||||
<description>Represents a Landroid Worx Mower</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="common" typeId="common-group-type"/>
|
||||
<channel-group id="config" typeId="config-group-type"/>
|
||||
<channel-group id="multi-zones" typeId="multi-zones-group-type"/>
|
||||
<channel-group id="schedule" typeId="sched-group-type"/>
|
||||
<channel-group id="aws" typeId="aws-group-type"/>
|
||||
<channel-group id="sunday" typeId="day-sched-group-type">
|
||||
<label>Sunday Schedule</label>
|
||||
</channel-group>
|
||||
<channel-group id="sunday2" typeId="day-sched-group-type">
|
||||
<label>Sunday Slot 2 Schedule</label>
|
||||
</channel-group>
|
||||
<channel-group id="monday" typeId="day-sched-group-type">
|
||||
<label>Monday Schedule</label>
|
||||
</channel-group>
|
||||
<channel-group id="monday2" typeId="day-sched-group-type">
|
||||
<label>Monday Slot 2 Schedule</label>
|
||||
</channel-group>
|
||||
<channel-group id="tuesday" typeId="day-sched-group-type">
|
||||
<label>Tuesday Schedule</label>
|
||||
</channel-group>
|
||||
<channel-group id="tuesday2" typeId="day-sched-group-type">
|
||||
<label>Tuesday Slot 2 Schedule</label>
|
||||
</channel-group>
|
||||
<channel-group id="wednesday" typeId="day-sched-group-type">
|
||||
<label>Wednesday Schedule</label>
|
||||
</channel-group>
|
||||
<channel-group id="wednesday2" typeId="day-sched-group-type">
|
||||
<label>Wednesday Slot 2 Schedule</label>
|
||||
</channel-group>
|
||||
<channel-group id="thursday" typeId="day-sched-group-type">
|
||||
<label>Thursday Schedule</label>
|
||||
</channel-group>
|
||||
<channel-group id="thursday2" typeId="day-sched-group-type">
|
||||
<label>Thursday Slot 2 Schedule</label>
|
||||
</channel-group>
|
||||
<channel-group id="friday" typeId="day-sched-group-type">
|
||||
<label>Friday Schedule</label>
|
||||
</channel-group>
|
||||
<channel-group id="friday2" typeId="day-sched-group-type">
|
||||
<label>Friday Slot 2 Schedule</label>
|
||||
</channel-group>
|
||||
<channel-group id="saturday" typeId="day-sched-group-type">
|
||||
<label>Saturday Schedule</label>
|
||||
</channel-group>
|
||||
<channel-group id="saturday2" typeId="day-sched-group-type">
|
||||
<label>Saturday Slot 2 Schedule</label>
|
||||
</channel-group>
|
||||
<channel-group id="one-time" typeId="ot-sched-group-type"/>
|
||||
<channel-group id="battery" typeId="battery-group-type"/>
|
||||
<channel-group id="orientation" typeId="orientation-group-type"/>
|
||||
<channel-group id="metrics" typeId="metrics-group-type"/>
|
||||
<channel-group id="rain" typeId="rain-group-type"/>
|
||||
<channel-group id="wifi" typeId="wifi-group-type"/>
|
||||
</channel-groups>
|
||||
|
||||
<representation-property>serialNumber</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="serialNumber" type="text">
|
||||
<label>Serial Number</label>
|
||||
<description>Serial number of the mower</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
|
||||
<parameter name="refreshStatusInterval" unit="s" type="integer" min="10">
|
||||
<label>Refresh Status Interval</label>
|
||||
<description>Interval for refreshing mower status in seconds</description>
|
||||
<default>3600</default>
|
||||
<required>true</required>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="pollingInterval" unit="s" type="integer" min="10" max="7200">
|
||||
<label>Polling Interval</label>
|
||||
<description>Interval for polling in seconds</description>
|
||||
<default>1200</default>
|
||||
<required>true</required>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg version="1.1" id="prefix__Layer_1" x="0" y="0" viewBox="0 0 283.46 283.46" height="32" width="32" xmlns="http://www.w3.org/2000/svg">
|
||||
<style></style>
|
||||
<path
|
||||
d="M199.7 34.99h-28.01v-8.37a7.16 7.16 0 00-7.16-7.16h-45.59a7.16 7.16 0 00-7.16 7.16v8.37H83.77c-8.45 0-15.31 6.85-15.31 15.31v198.41c0 8.45 6.85 15.31 15.31 15.31H199.7c8.45 0 15.31-6.85 15.31-15.31V50.29c-.01-8.45-6.86-15.3-15.31-15.3z"
|
||||
transform="rotate(90 141.735 141.74)" fill="#8bc53f" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 469 B |
@@ -0,0 +1,9 @@
|
||||
<svg version="1.1" id="prefix__Layer_1" x="0" y="0" height="32" width="32" viewBox="0 0 283.46 283.46" xmlns="http://www.w3.org/2000/svg">
|
||||
<style></style>
|
||||
<path
|
||||
d="M199.7 34.99h-28.01v-8.37a7.16 7.16 0 00-7.16-7.16h-45.59a7.16 7.16 0 00-7.16 7.16v8.37H83.77c-8.45 0-15.31 6.85-15.31 15.31v198.41c0 8.45 6.85 15.31 15.31 15.31H199.7c8.45 0 15.31-6.85 15.31-15.31V50.29c-.01-8.45-6.86-15.3-15.31-15.3z"
|
||||
transform="rotate(90 141.735 141.74)" fill="#dee0e1" />
|
||||
<path d="M7.964 80.61v106.97c0 8.45 6.85 15.31 15.31 15.31h115.93c8.45 0 15.31-6.85 15.31-15.31V80.61H7.964z"
|
||||
transform="rotate(90 81.239 141.75)" fill="#8bc53f" />
|
||||
<path fill="#febe10" d="M154.69 70.732l-46.35 76.549 32.06.074-13.36 66.308 50.09-78.596-34.83.127z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 742 B |
@@ -0,0 +1,6 @@
|
||||
<svg version="1.1" id="prefix__Layer_1" x="0" y="0" height="32" width="32" viewBox="0 0 283.46 283.46" xmlns="http://www.w3.org/2000/svg">
|
||||
<style></style>
|
||||
<path
|
||||
d="M199.7 34.99h-28.01v-8.37a7.16 7.16 0 00-7.16-7.16h-45.59a7.16 7.16 0 00-7.16 7.16v8.37H83.77c-8.45 0-15.31 6.85-15.31 15.31v198.41c0 8.45 6.85 15.31 15.31 15.31H199.7c8.45 0 15.31-6.85 15.31-15.31V50.29c-.01-8.45-6.86-15.3-15.31-15.3z"
|
||||
transform="rotate(90 141.735 141.74)" fill="#dee0e1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 469 B |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="32" width="32" viewBox="0 0 48 48">
|
||||
<path fill="#37474F" d="M45 38H3a2 2 0 01-2-2V12a2 2 0 012-2h42a2 2 0 012 2v24a2 2 0 01-2 2z" />
|
||||
<path fill="#FFF" d="M16 35H4V13h12v22zm14-22H18v22h12V13zm14 0H32v22h12V13z" />
|
||||
<path fill="#CFD8DC"
|
||||
d="M16 17H4v-4h12v4zm14-4H18v4h12v-4zm14 0H32v4h12v-4zM16 33H4v2h12v-2zm14 0H18v2h12v-2zm14 0H32v2h12v-2z" />
|
||||
<path fill="none"
|
||||
d="M37.995 17.812c.42 0 .757-.231 1.01-.694.019-.035.025-.082.043-.118h-2.093c.014.029.024.066.039.094.254.479.587.718 1.001.718z" />
|
||||
<path fill="#546E7A"
|
||||
d="M14 25.179c0 1.884-.345 3.325-1.035 4.324-.691.998-1.676 1.497-2.956 1.497-1.267 0-2.246-.485-2.936-1.455S6.025 27.185 6 25.376v-2.574c0-1.878.344-3.314 1.031-4.309S8.704 17 9.991 17s2.273.493 2.96 1.479c.687.986 1.037 2.393 1.05 4.221v2.479zm-2.686-2.767c0-1.077-.107-1.885-.322-2.424-.214-.539-.548-.808-1.002-.808-.851 0-1.292.987-1.324 2.959v3.411c0 1.102.109 1.923.326 2.461.217.539.556.808 1.017.808.435 0 .758-.252.969-.756.211-.504.323-1.276.336-2.316v-3.335zM28 25.179c0 1.884-.345 3.325-1.035 4.324-.691.998-1.676 1.497-2.956 1.497-1.267 0-2.246-.485-2.936-1.455s-1.048-2.36-1.073-4.169v-2.574c0-1.878.344-3.314 1.031-4.309S22.704 17 23.991 17c1.286 0 2.273.493 2.96 1.479.687.986 1.037 2.393 1.05 4.221v2.479zm-2.686-2.767c0-1.077-.107-1.885-.322-2.424-.214-.539-.548-.808-1.002-.808-.851 0-1.292.987-1.324 2.959v3.411c0 1.102.109 1.923.326 2.461.217.539.556.808 1.017.808.435 0 .758-.252.969-.756.211-.504.323-1.276.336-2.316v-3.335zM42 25.554V24h-8v2.24h5.303L36.892 33h2.609L42 25.554zm-2.994-8.436c-.253.462-.59.694-1.01.694-.414 0-.748-.239-1.001-.718-.015-.028-.025-.065-.039-.094h-2.589c.182.563.42 1.074.741 1.507.738.995 1.719 1.493 2.943 1.493.76 0 1.441-.199 2.044-.596s1.071-.956 1.404-1.676c.108-.232.192-.477.264-.728h-2.716c-.016.036-.022.083-.041.118z" />
|
||||
<path fill="#455A64"
|
||||
d="M36.178 35l.714-2h2.609l-.671 2h-2.652zm.436-20.078v-.963c.111-.315.28-.579.505-.793.078-.074.171-.118.262-.167H34.02c-.006.169-.02.331-.02.503v.986c0 .951.131 1.78.367 2.512h2.589c-.225-.477-.342-1.165-.342-2.078zM41.641 13h-3.144c.188.115.354.292.495.54.263.462.394 1.062.394 1.798 0 .67-.121 1.216-.338 1.662h2.716A6.032 6.032 0 0042 15.305c0-.884-.129-1.643-.359-2.305z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="32" height="32" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M92.6 21c-32 0-64.04 24-64.04 72L92.6 221l64-128c0-48-32-72-64-72zm282.3 39c-6.9.29-13.6 1.6-19.2 2.8l3.8 17.6c5.6-1.25 11.4-2.04 16.3-2.4zM92.6 61c17.7 0 32 14.33 32 32 0 17.7-14.3 32-32 32-17.67 0-32-14.3-32-32 0-17.67 14.33-32 32-32zm302.2.2l-3 17.7c4.9 1.03 9.8 2.32 14.1 4.9l8.7-15.8c-6.1-3.25-12.9-6.17-19.8-6.8zm-57.5 6.7c-6.1 2.38-12.2 4.51-17.4 6.6L327 91c5.5-2.34 11.3-4.38 16.2-6.1zM431 81.3L417.3 93c3.6 4.12 6.4 9.2 8.6 13.3l16.1-8.1c-3.4-6.55-6.4-11.51-11-16.9zm-127.8.9c-6.1 3.11-11.1 5.88-16.5 8.6l8.8 15.8c5.2-3 10.9-5.9 15.5-8.2zm-32.3 17.9c-5.3 3.1-10.5 6.2-15.6 9.6l9.8 15c4.9-3.2 10-6.2 15-9.2zM448.2 118c-5.9 1-11.9 1.7-17.8 2.4.4 5 .1 10.4-.9 14.6l17.5 4.1c1-7.2 1.9-14.6 1.2-21.1zm-208.1 1.7c-5 3.4-9.9 6.9-14.9 10.3l10.4 14.7c4.8-3.5 9.7-6.8 14.6-10.2zm-29.6 21.1c-5 3.6-10.2 7.6-14.5 10.9l10.9 14.3c5.5-4 9.3-7 14.3-10.7zm213 8c-3 4.6-6.5 9.2-10 12.7l13.1 12.5c4.3-5.1 8.9-10.3 12.1-15.5zm-241.8 14.1c-4.9 3.8-9.8 7.7-14.1 11.3l11.4 13.9c4.7-3.9 9.5-7.9 13.9-11.1zM401.1 173c-4.6 3.7-9.4 7.3-13.8 10.3l10.3 14.8c5.3-3.6 10.5-7.5 15-11.1zm-247.4 12.9c-4.7 3.8-9.2 7.8-13.8 11.7l11.7 13.7c4.5-3.9 9-7.8 13.6-11.6zm218.9 7c-5.1 3-10.4 6.1-15.2 8.7l8.6 15.9c5.4-3.3 11.5-6.2 16-9.2zm-246.4 16.6c-4.5 4-8.9 8-13.4 12.1l12.1 13.4c4.4-4 8.9-8 13.3-12zm215.5.4c-5.3 2.6-10.6 5.3-15.9 7.9l7.7 16.2c6.2-3 10.8-5.5 16.4-8.1zm-32 15.4c-5.5 2.5-10.8 4.9-16.4 7.2l7.3 16.5c5.5-2.4 11-4.9 16.5-7.4zM99.6 234c-5.1 4.5-8.65 8-13.3 12.5l12.7 13c4.7-4.5 8.5-8.4 12.9-12.2zm177.3 5.8c-5.5 2.3-11 4.7-16.5 7l7 16.7c5.6-2.3 11.1-4.7 16.6-7.1zm-33.1 14c-5.5 2.4-11 4.8-16.6 7l7 16.7c5.5-2.3 11.1-4.7 16.6-7zm184.8 7.2c-32 0-64 24-64 72l64 128 64-128c0-48-32-72-64-72zm-218 6.8c-5.7 2.6-11.7 5-16.6 7.1l7.1 16.6c5.9-2.5 11.5-4.9 16.5-7.1zM177.4 282c-5.4 2.5-11.7 5.3-16.5 7.5l7.4 16.4c5.9-2.6 11.1-5.2 16.3-7.4zm-33 15c-5.6 2.7-11.4 5.5-16.4 8l8.1 16.1c5.4-2.8 11-5.4 15.9-7.8zm284.2 4c17.7 0 32 14.3 32 32s-14.3 32-32 32-32-14.3-32-32 14.3-32 32-32zm-316.8 12.3c-5.3 2.9-10.6 5.9-16 9l9 15.6c5.1-3 10.3-5.8 15.5-8.6zM80.1 332c-5.61 3.2-11.03 7.5-15.7 10.6L75.3 357c4.97-3.6 10.32-7.3 14.6-9.9zm-29.9 22.6c-4.8 4.4-9.53 9.5-13.2 13.8l13.7 11.7c3.85-4.7 7.2-8.2 11.7-12.2zm217.8 1.3l1.6 17.9c5.2-.9 10.4-.3 15.6.5l3.1-17.7c-6.6-1-13.6-1.7-20.3-.7zm-37.2 10l6.8 16.7c5.2-2.3 10.6-4.1 16.1-5.8-1.9-5.7-3.3-11.5-4.8-17.3-6.3 1.8-12.6 4.2-18.1 6.4zm77.5-.9l-10.2 14.8c4.2 3.1 8.3 6.4 11.6 10.5l13.6-11.8c-5.1-5.2-9-10.1-15-13.5zm-94.5 9c-5.5 2.8-10.8 6-16.1 9.1l9.1 15.5c5.2-2.8 10.3-6.1 15.4-8.8zM26.01 385c-3.02 6.5-5.47 13.5-6.61 19.7l17.7 3.1c1.08-5.7 2.63-9.8 4.9-14.7-5.49-2.4-10.73-5.3-15.99-8.1zm156.09 7.8c-5.1 3.3-10.1 6.6-15.1 10l10 15c5-3.3 9.9-6.7 14.9-10zm152.7 1.2l-15.1 9.8c3.2 4.8 6.3 9.8 9.2 14.9l15.6-9c-3.5-5.6-6-10.6-9.7-15.7zm-182.7 19c-5 3.3-10 6.5-14.9 10l10 15c4.8-3.5 9.9-6.8 15-10.2zm-114.8 9.5c-5.79 1.2-11.63 2.2-17.45 3.3 1.05 7 3.86 13.8 6.4 19.2l16.25-7.8c-2.17-5-4.23-10.2-5.2-14.7zm316.1 2.8l-15.6 9c3.1 5.4 6.7 11.2 9.6 15.8l15.1-9.7c-3.4-5.3-6.3-10.3-9.1-15.1zm-231 7.5c-5 3.1-9.9 6.1-15.1 9l8.9 15.7c5.3-3.1 10.6-6.2 15.7-9.5zm-71.3 16.3l-12.3 13.2c5.56 5.3 12.42 8.8 19.9 10.4l4-17.5c-4.44-.9-8.59-3.1-11.6-6.1zm41 .3c-5.01 2.3-10.21 4.1-15.6 5.2l4.1 17.6c6.42-1.3 12.46-3.7 18.5-6.2zm280.3 4.8l-13.9 11.3c4.3 5.3 9.6 10.4 14.2 14l11.1-14.2c-4.4-3.4-8.2-7.5-11.4-11.1zm24.1 17.5l-4.5 17.5c7.9 1.6 13.8 2.1 21.2 1.3l-2.2-17.9c-4.9.8-9.7.3-14.5-.9z"
|
||||
fill="#a8a8a8" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="32" height="32" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M92.6 21c-32 0-64.04 24-64.04 72L92.6 221l64-128c0-48-32-72-64-72zm282.3 39c-6.9.29-13.6 1.6-19.2 2.8l3.8 17.6c5.6-1.25 11.4-2.04 16.3-2.4zM92.6 61c17.7 0 32 14.33 32 32 0 17.7-14.3 32-32 32-17.67 0-32-14.3-32-32 0-17.67 14.33-32 32-32zm302.2.2l-3 17.7c4.9 1.03 9.8 2.32 14.1 4.9l8.7-15.8c-6.1-3.25-12.9-6.17-19.8-6.8zm-57.5 6.7c-6.1 2.38-12.2 4.51-17.4 6.6L327 91c5.5-2.34 11.3-4.38 16.2-6.1zM431 81.3L417.3 93c3.6 4.12 6.4 9.2 8.6 13.3l16.1-8.1c-3.4-6.55-6.4-11.51-11-16.9zm-127.8.9c-6.1 3.11-11.1 5.88-16.5 8.6l8.8 15.8c5.2-3 10.9-5.9 15.5-8.2zm-32.3 17.9c-5.3 3.1-10.5 6.2-15.6 9.6l9.8 15c4.9-3.2 10-6.2 15-9.2zM448.2 118c-5.9 1-11.9 1.7-17.8 2.4.4 5 .1 10.4-.9 14.6l17.5 4.1c1-7.2 1.9-14.6 1.2-21.1zm-208.1 1.7c-5 3.4-9.9 6.9-14.9 10.3l10.4 14.7c4.8-3.5 9.7-6.8 14.6-10.2zm-29.6 21.1c-5 3.6-10.2 7.6-14.5 10.9l10.9 14.3c5.5-4 9.3-7 14.3-10.7zm213 8c-3 4.6-6.5 9.2-10 12.7l13.1 12.5c4.3-5.1 8.9-10.3 12.1-15.5zm-241.8 14.1c-4.9 3.8-9.8 7.7-14.1 11.3l11.4 13.9c4.7-3.9 9.5-7.9 13.9-11.1zM401.1 173c-4.6 3.7-9.4 7.3-13.8 10.3l10.3 14.8c5.3-3.6 10.5-7.5 15-11.1zm-247.4 12.9c-4.7 3.8-9.2 7.8-13.8 11.7l11.7 13.7c4.5-3.9 9-7.8 13.6-11.6zm218.9 7c-5.1 3-10.4 6.1-15.2 8.7l8.6 15.9c5.4-3.3 11.5-6.2 16-9.2zm-246.4 16.6c-4.5 4-8.9 8-13.4 12.1l12.1 13.4c4.4-4 8.9-8 13.3-12zm215.5.4c-5.3 2.6-10.6 5.3-15.9 7.9l7.7 16.2c6.2-3 10.8-5.5 16.4-8.1zm-32 15.4c-5.5 2.5-10.8 4.9-16.4 7.2l7.3 16.5c5.5-2.4 11-4.9 16.5-7.4zM99.6 234c-5.1 4.5-8.65 8-13.3 12.5l12.7 13c4.7-4.5 8.5-8.4 12.9-12.2zm177.3 5.8c-5.5 2.3-11 4.7-16.5 7l7 16.7c5.6-2.3 11.1-4.7 16.6-7.1zm-33.1 14c-5.5 2.4-11 4.8-16.6 7l7 16.7c5.5-2.3 11.1-4.7 16.6-7zm184.8 7.2c-32 0-64 24-64 72l64 128 64-128c0-48-32-72-64-72zm-218 6.8c-5.7 2.6-11.7 5-16.6 7.1l7.1 16.6c5.9-2.5 11.5-4.9 16.5-7.1zM177.4 282c-5.4 2.5-11.7 5.3-16.5 7.5l7.4 16.4c5.9-2.6 11.1-5.2 16.3-7.4zm-33 15c-5.6 2.7-11.4 5.5-16.4 8l8.1 16.1c5.4-2.8 11-5.4 15.9-7.8zm284.2 4c17.7 0 32 14.3 32 32s-14.3 32-32 32-32-14.3-32-32 14.3-32 32-32zm-316.8 12.3c-5.3 2.9-10.6 5.9-16 9l9 15.6c5.1-3 10.3-5.8 15.5-8.6zM80.1 332c-5.61 3.2-11.03 7.5-15.7 10.6L75.3 357c4.97-3.6 10.32-7.3 14.6-9.9zm-29.9 22.6c-4.8 4.4-9.53 9.5-13.2 13.8l13.7 11.7c3.85-4.7 7.2-8.2 11.7-12.2zm217.8 1.3l1.6 17.9c5.2-.9 10.4-.3 15.6.5l3.1-17.7c-6.6-1-13.6-1.7-20.3-.7zm-37.2 10l6.8 16.7c5.2-2.3 10.6-4.1 16.1-5.8-1.9-5.7-3.3-11.5-4.8-17.3-6.3 1.8-12.6 4.2-18.1 6.4zm77.5-.9l-10.2 14.8c4.2 3.1 8.3 6.4 11.6 10.5l13.6-11.8c-5.1-5.2-9-10.1-15-13.5zm-94.5 9c-5.5 2.8-10.8 6-16.1 9.1l9.1 15.5c5.2-2.8 10.3-6.1 15.4-8.8zM26.01 385c-3.02 6.5-5.47 13.5-6.61 19.7l17.7 3.1c1.08-5.7 2.63-9.8 4.9-14.7-5.49-2.4-10.73-5.3-15.99-8.1zm156.09 7.8c-5.1 3.3-10.1 6.6-15.1 10l10 15c5-3.3 9.9-6.7 14.9-10zm152.7 1.2l-15.1 9.8c3.2 4.8 6.3 9.8 9.2 14.9l15.6-9c-3.5-5.6-6-10.6-9.7-15.7zm-182.7 19c-5 3.3-10 6.5-14.9 10l10 15c4.8-3.5 9.9-6.8 15-10.2zm-114.8 9.5c-5.79 1.2-11.63 2.2-17.45 3.3 1.05 7 3.86 13.8 6.4 19.2l16.25-7.8c-2.17-5-4.23-10.2-5.2-14.7zm316.1 2.8l-15.6 9c3.1 5.4 6.7 11.2 9.6 15.8l15.1-9.7c-3.4-5.3-6.3-10.3-9.1-15.1zm-231 7.5c-5 3.1-9.9 6.1-15.1 9l8.9 15.7c5.3-3.1 10.6-6.2 15.7-9.5zm-71.3 16.3l-12.3 13.2c5.56 5.3 12.42 8.8 19.9 10.4l4-17.5c-4.44-.9-8.59-3.1-11.6-6.1zm41 .3c-5.01 2.3-10.21 4.1-15.6 5.2l4.1 17.6c6.42-1.3 12.46-3.7 18.5-6.2zm280.3 4.8l-13.9 11.3c4.3 5.3 9.6 10.4 14.2 14l11.1-14.2c-4.4-3.4-8.2-7.5-11.4-11.1zm24.1 17.5l-4.5 17.5c7.9 1.6 13.8 2.1 21.2 1.3l-2.2-17.9c-4.9.8-9.7.3-14.5-.9z"
|
||||
fill="#4ba82f" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,33 @@
|
||||
<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 511.843 511.843">
|
||||
<path
|
||||
d="M84.201 61.399c-1.156-4.64-5.342-8.076-10.333-8.076H10.661C4.772 53.323 0 58.095 0 63.984c0 5.889 4.772 10.661 10.661 10.661H65.62l60.771 234.624 20.642-5.342L84.201 61.399z"
|
||||
fill="#424953" />
|
||||
<path
|
||||
d="M198.268 267.031h-96.635c-11.731 0-22.767 9.481-24.516 21.087l-12.902 85.334c-1.75 11.605 6.412 21.087 18.143 21.087h115.91c11.73 0 21.329-9.591 21.329-21.306v-84.881c0-11.714-9.599-21.321-21.329-21.321z"
|
||||
fill="#fecd57" />
|
||||
<path d="M490.522 330.559C294.77 223.935 104.288 223.935 104.288 223.935l49.407 190.613 336.827 1.328v-85.317z"
|
||||
fill="#ec5564" />
|
||||
<path
|
||||
d="M170.612 447.866c-29.397 0-53.312-23.914-53.312-53.327 0-29.397 23.915-53.296 53.312-53.296 29.405 0 53.32 23.898 53.32 53.296 0 29.413-23.915 53.327-53.32 53.327z"
|
||||
fill="#646c77" />
|
||||
<path
|
||||
d="M170.612 330.559c-35.333 0-63.98 28.647-63.98 63.98 0 35.349 28.647 63.98 63.98 63.98 35.341 0 63.98-28.632 63.98-63.98 0-35.333-28.639-63.98-63.98-63.98zm0 106.655c-23.516 0-42.651-19.15-42.651-42.675 0-23.509 19.135-42.644 42.651-42.644 23.524 0 42.659 19.135 42.659 42.644 0 23.525-19.135 42.675-42.659 42.675z"
|
||||
fill="#424953" />
|
||||
<path
|
||||
d="M458.531 447.866c-23.523 0-42.658-19.135-42.658-42.643 0-23.524 19.135-42.675 42.658-42.675 23.509 0 42.644 19.15 42.644 42.675 0 23.509-19.135 42.643-42.644 42.643z"
|
||||
fill="#646c77" />
|
||||
<path
|
||||
d="M458.531 351.896c-29.443 0-53.327 23.868-53.327 53.328 0 29.428 23.884 53.296 53.327 53.296 29.445 0 53.312-23.868 53.312-53.296.001-29.46-23.866-53.328-53.312-53.328zm0 85.318c-17.65 0-31.99-14.371-31.99-31.99 0-17.651 14.34-31.99 31.99-31.99 17.636 0 31.99 14.339 31.99 31.99.001 17.619-14.354 31.99-31.99 31.99z"
|
||||
fill="#424953" />
|
||||
<g fill="#e5e8ec">
|
||||
<path
|
||||
d="M469.185 405.224c0 5.873-4.765 10.652-10.653 10.652s-10.668-4.779-10.668-10.652c0-5.904 4.779-10.685 10.668-10.685s10.653 4.78 10.653 10.685zM181.28 394.539c0 5.904-4.78 10.685-10.668 10.685s-10.661-4.78-10.661-10.685c0-5.873 4.772-10.653 10.661-10.653s10.668 4.78 10.668 10.653z" />
|
||||
</g>
|
||||
<path
|
||||
d="M203.118 233.65c20.017 44.55 64.777 75.603 116.785 75.603 27.585 0 53.14-8.748 74.039-23.603-72.399-28.389-139.481-43.721-190.824-52z"
|
||||
fill="#424953" />
|
||||
<g fill="#f5ba45">
|
||||
<path
|
||||
d="M93.846 309.691H73.86l-3.226 21.321h23.212c5.889 0 10.661-4.765 10.661-10.669 0-5.873-4.772-10.652-10.661-10.652zM65.8 363.002h23.243a10.663 10.663 0 0010.668-10.669c0-5.873-4.78-10.652-10.668-10.652H69.026L65.8 363.002z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg height="32" width="32" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9 25v-5c0-5.52 4.48-10 10-10s10 4.48 10 10v12.73" fill="none" stroke="#b4b4b4" stroke-miterlimit="10"
|
||||
stroke-width="4" />
|
||||
<path d="M23.5 29.5h29v25h-29z" fill="#f8cf99" />
|
||||
<path d="M52 30v24H24V30h28m1-1H23v26h30V29z" fill="#cb9d61" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 351 B |
@@ -0,0 +1,6 @@
|
||||
<svg height="32" width="32" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M42 29.59v-5.45c0-5.52-4.48-10-10-10s-10 4.48-10 10v12.73" fill="none" stroke="#b4b4b4"
|
||||
stroke-miterlimit="10" stroke-width="4" />
|
||||
<path d="M17.5 29.5h29v25h-29z" fill="#f8cf99" />
|
||||
<path d="M46 30v24H18V30h28m1-1H17v26h30V29z" fill="#cb9d61" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 359 B |
@@ -0,0 +1,6 @@
|
||||
<svg height="32" width="32" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M42 29.59v-5.45c0-5.52-4.48-10-10-10s-10 4.48-10 10v12.73" fill="none" stroke="#b4b4b4"
|
||||
stroke-miterlimit="10" stroke-width="4" />
|
||||
<path d="M17.5 29.5h29v25h-29z" fill="#f8cf99" />
|
||||
<path d="M46 30v24H18V30h28m1-1H17v26h30V29z" fill="#cb9d61" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 359 B |
@@ -0,0 +1,81 @@
|
||||
<svg height="32" width="32" viewBox="0 0 55 63" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__e">
|
||||
<feMorphology radius="2" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
|
||||
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
|
||||
<feGaussianBlur stdDeviation="1.5" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
|
||||
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
|
||||
</filter>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__g">
|
||||
<feMorphology radius="1.08" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
|
||||
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
|
||||
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
|
||||
</filter>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__i">
|
||||
<feMorphology radius="1.44" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
|
||||
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
|
||||
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
|
||||
</filter>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__k">
|
||||
<feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1" />
|
||||
<feGaussianBlur stdDeviation="1.5" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
|
||||
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
|
||||
</filter>
|
||||
<mask id="prefix__f" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-2" y="-2"
|
||||
width="27.056" height="36.375">
|
||||
<path fill="#fff" d="M9 21h27.056v36.375H9z" />
|
||||
<use xlink:href="#prefix__a" />
|
||||
</mask>
|
||||
<mask id="prefix__h" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.08" y="-1.08"
|
||||
width="14.41" height="19.66">
|
||||
<path fill="#fff" d="M2.92 3.92h14.41v19.66H2.92z" />
|
||||
<use xlink:href="#prefix__b" />
|
||||
</mask>
|
||||
<mask id="prefix__j" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.44" y="-1.44"
|
||||
width="19.505" height="26.505">
|
||||
<path fill="#fff" d="M25.56-1.44h19.505v26.505H25.56z" />
|
||||
<use xlink:href="#prefix__c" />
|
||||
</mask>
|
||||
<mask id="prefix__l" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="0" y="0" width="67"
|
||||
height="4" fill="#fff">
|
||||
<use xlink:href="#prefix__d" />
|
||||
</mask>
|
||||
<path
|
||||
d="M22.528 23C20.887 23 11 38.241 11 44.803c0 5.174 2.987 10.572 11.528 10.572 7.82 0 11.528-5.346 11.528-10.572C34.056 38.158 24.169 23 22.528 23z"
|
||||
id="prefix__a" />
|
||||
<path
|
||||
d="M10.125 5C9.253 5 4 13.238 4 16.785c0 2.797 1.587 5.715 6.125 5.715 4.155 0 6.125-2.89 6.125-5.715C16.25 13.193 10.997 5 10.125 5z"
|
||||
id="prefix__b" />
|
||||
<path
|
||||
d="M35.313 0C34.129 0 27 11.122 27 15.91c0 3.776 2.154 7.715 8.313 7.715 5.64 0 8.312-3.901 8.312-7.715 0-4.849-7.129-15.91-8.313-15.91z"
|
||||
id="prefix__c" />
|
||||
<rect id="prefix__d" x="-8.105" y="28.395" width="67" height="4" rx="2" />
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<g transform="translate(2 2)">
|
||||
<use fill="#000" filter="url(#prefix__e)" xlink:href="#prefix__a" />
|
||||
<use fill-opacity=".3" fill="#55A4FF" xlink:href="#prefix__a" />
|
||||
<use stroke="#FFF" mask="url(#prefix__f)" stroke-width="4" xlink:href="#prefix__a" />
|
||||
</g>
|
||||
<g transform="translate(2 2)">
|
||||
<use fill="#000" filter="url(#prefix__g)" xlink:href="#prefix__b" />
|
||||
<use fill-opacity=".3" fill="#55A4FF" xlink:href="#prefix__b" />
|
||||
<use stroke="#FFF" mask="url(#prefix__h)" stroke-width="2.16" xlink:href="#prefix__b" />
|
||||
</g>
|
||||
<g transform="translate(2 2)">
|
||||
<use fill="#000" filter="url(#prefix__i)" xlink:href="#prefix__c" />
|
||||
<use fill-opacity=".3" fill="#55A4FF" xlink:href="#prefix__c" />
|
||||
<use stroke="#FFF" mask="url(#prefix__j)" stroke-width="2.88" xlink:href="#prefix__c" />
|
||||
</g>
|
||||
<g transform="rotate(-45 28.81 28.981)">
|
||||
<use fill="#000" filter="url(#prefix__k)" xlink:href="#prefix__d" />
|
||||
<use stroke="#FFF" mask="url(#prefix__l)" stroke-width="2" fill="#464646" xlink:href="#prefix__d" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
@@ -0,0 +1,66 @@
|
||||
<svg height="32" width="32" viewBox="0 0 46 63" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__d">
|
||||
<feMorphology radius="2" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
|
||||
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
|
||||
<feGaussianBlur stdDeviation="1.5" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
|
||||
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
|
||||
</filter>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__f">
|
||||
<feMorphology radius="1.08" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
|
||||
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
|
||||
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
|
||||
</filter>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__h">
|
||||
<feMorphology radius="1.44" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
|
||||
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
|
||||
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
|
||||
</filter>
|
||||
<path
|
||||
d="M18.528 23C16.887 23 7 38.241 7 44.803c0 5.174 2.987 10.572 11.528 10.572 7.82 0 11.528-5.346 11.528-10.572C30.056 38.158 20.169 23 18.528 23z"
|
||||
id="prefix__a" />
|
||||
<path
|
||||
d="M6.125 5C5.253 5 0 13.238 0 16.785 0 19.582 1.587 22.5 6.125 22.5c4.155 0 6.125-2.89 6.125-5.715C12.25 13.193 6.997 5 6.125 5z"
|
||||
id="prefix__b" />
|
||||
<path
|
||||
d="M31.313 0C30.128 0 23 11.122 23 15.91c0 3.776 2.154 7.715 8.313 7.715 5.64 0 8.312-3.901 8.312-7.715 0-4.849-7.129-15.91-8.313-15.91z"
|
||||
id="prefix__c" />
|
||||
<mask id="prefix__e" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-2" y="-2"
|
||||
width="27.056" height="36.375">
|
||||
<path fill="#fff" d="M5 21h27.056v36.375H5z" />
|
||||
<use xlink:href="#prefix__a" />
|
||||
</mask>
|
||||
<mask id="prefix__g" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.08" y="-1.08"
|
||||
width="14.41" height="19.66">
|
||||
<path fill="#fff" d="M-1.08 3.92h14.41v19.66H-1.08z" />
|
||||
<use xlink:href="#prefix__b" />
|
||||
</mask>
|
||||
<mask id="prefix__i" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.44" y="-1.44"
|
||||
width="19.505" height="26.505">
|
||||
<path fill="#fff" d="M21.56-1.44h19.505v26.505H21.56z" />
|
||||
<use xlink:href="#prefix__c" />
|
||||
</mask>
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<g transform="translate(3 2)">
|
||||
<use fill="#000" filter="url(#prefix__d)" xlink:href="#prefix__a" />
|
||||
<use fill="#55A4FF" xlink:href="#prefix__a" />
|
||||
<use stroke="#FFF" mask="url(#prefix__e)" stroke-width="4" xlink:href="#prefix__a" />
|
||||
</g>
|
||||
<g transform="translate(3 2)">
|
||||
<use fill="#000" filter="url(#prefix__f)" xlink:href="#prefix__b" />
|
||||
<use fill="#55A4FF" xlink:href="#prefix__b" />
|
||||
<use stroke="#FFF" mask="url(#prefix__g)" stroke-width="2.16" xlink:href="#prefix__b" />
|
||||
</g>
|
||||
<g transform="translate(3 2)">
|
||||
<use fill="#000" filter="url(#prefix__h)" xlink:href="#prefix__c" />
|
||||
<use fill="#55A4FF" xlink:href="#prefix__c" />
|
||||
<use stroke="#FFF" mask="url(#prefix__i)" stroke-width="2.88" xlink:href="#prefix__c" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,67 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="32" width="32" viewBox="0 0 55 63">
|
||||
<defs>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__e">
|
||||
<feMorphology radius="2" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
|
||||
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
|
||||
<feGaussianBlur stdDeviation="1.5" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
|
||||
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
|
||||
</filter>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__g">
|
||||
<feMorphology radius="1.08" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
|
||||
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
|
||||
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
|
||||
</filter>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="prefix__i">
|
||||
<feMorphology radius="1.44" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1" />
|
||||
<feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1" />
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1" />
|
||||
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1" />
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" in="shadowBlurOuter1" />
|
||||
</filter>
|
||||
<path
|
||||
d="M22.528 23C20.887 23 11 38.241 11 44.803c0 5.174 2.987 10.572 11.528 10.572 7.82 0 11.528-5.346 11.528-10.572C34.056 38.158 24.169 23 22.528 23z"
|
||||
id="prefix__a" />
|
||||
<path
|
||||
d="M10.125 5C9.253 5 4 13.238 4 16.785c0 2.797 1.587 5.715 6.125 5.715 4.155 0 6.125-2.89 6.125-5.715C16.25 13.193 10.997 5 10.125 5z"
|
||||
id="prefix__b" />
|
||||
<path
|
||||
d="M35.313 0C34.129 0 27 11.122 27 15.91c0 3.776 2.154 7.715 8.313 7.715 5.64 0 8.312-3.901 8.312-7.715 0-4.849-7.129-15.91-8.313-15.91z"
|
||||
id="prefix__c" />
|
||||
<mask id="prefix__f" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-2" y="-2" width="27.056"
|
||||
height="36.375">
|
||||
<path fill="#fff" d="M9 21h27.056v36.375H9z" />
|
||||
<use xlink:href="#prefix__a" />
|
||||
</mask>
|
||||
<mask id="prefix__h" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.08" y="-1.08"
|
||||
width="14.41" height="19.66">
|
||||
<path fill="#fff" d="M2.92 3.92h14.41v19.66H2.92z" />
|
||||
<use xlink:href="#prefix__b" />
|
||||
</mask>
|
||||
<mask id="prefix__j" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="-1.44" y="-1.44"
|
||||
width="19.505" height="26.505">
|
||||
<path fill="#fff" d="M25.56-1.44h19.505v26.505H25.56z" />
|
||||
<use xlink:href="#prefix__c" />
|
||||
</mask>
|
||||
<rect id="prefix__d" x="-8.105" y="28.395" width="67" height="4" rx="2" />
|
||||
</defs>
|
||||
<g transform="translate(2 2)" fill="none" fill-rule="evenodd">
|
||||
<use height="100%" width="100%" xlink:href="#prefix__a" fill="#000" filter="url(#prefix__e)" />
|
||||
<use height="100%" width="100%" xlink:href="#prefix__a" fill="#55a4ff" fill-opacity=".3" />
|
||||
<use height="100%" width="100%" xlink:href="#prefix__a" mask="url(#prefix__f)" stroke="#fff" stroke-width="4" />
|
||||
</g>
|
||||
<g transform="translate(2 2)" fill="none" fill-rule="evenodd">
|
||||
<use height="100%" width="100%" xlink:href="#prefix__b" fill="#000" filter="url(#prefix__g)" />
|
||||
<use height="100%" width="100%" xlink:href="#prefix__b" fill="#55a4ff" fill-opacity=".3" />
|
||||
<use height="100%" width="100%" xlink:href="#prefix__b" mask="url(#prefix__h)" stroke="#fff"
|
||||
stroke-width="2.16" />
|
||||
</g>
|
||||
<g transform="translate(2 2)" fill="none" fill-rule="evenodd">
|
||||
<use height="100%" width="100%" xlink:href="#prefix__c" fill="#000" filter="url(#prefix__i)" />
|
||||
<use height="100%" width="100%" xlink:href="#prefix__c" fill="#55a4ff" fill-opacity=".3" />
|
||||
<use height="100%" width="100%" xlink:href="#prefix__c" mask="url(#prefix__j)" stroke="#fff"
|
||||
stroke-width="2.88" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496.166 496.166">
|
||||
<path
|
||||
d="M.005 248.087C.005 111.063 111.073 0 248.079 0c137.014 0 248.082 111.062 248.082 248.087 0 137.002-111.068 248.079-248.082 248.079C111.073 496.166.005 385.089.005 248.087z"
|
||||
fill="#32bea6" />
|
||||
<path
|
||||
d="M400.813 169.581c-2.502-4.865-14.695-16.012-35.262-5.891-20.564 10.122-10.625 32.351-10.625 32.351 7.666 15.722 11.98 33.371 11.98 52.046 0 65.622-53.201 118.824-118.828 118.824-65.619 0-118.82-53.202-118.82-118.824 0-61.422 46.6-111.946 106.357-118.173v30.793s-.084 1.836 1.828 2.999c1.906 1.163 3.818 0 3.818 0l98.576-58.083s2.211-1.162 2.211-3.436c0-1.873-2.211-3.205-2.211-3.205l-98.248-57.754s-2.24-1.605-4.23-.826c-1.988.773-1.744 3.481-1.744 3.481v32.993c-88.998 6.392-159.23 80.563-159.23 171.21 0 94.824 76.873 171.696 171.693 171.696 94.828 0 171.707-76.872 171.707-171.696.001-28.298-6.852-54.98-18.972-78.505z"
|
||||
fill="#f7f7f7" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 967 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11 11H1C1 5.477 5.477 1 11 1v10z" fill="#39762b" />
|
||||
<path d="M12 12V2A10 10 0 112 12h10z" opacity=".5" />
|
||||
<path d="M12 12V2c5.523 0 10 4.477 10 10H12zM12 12h10c0 5.523-4.477 10-10 10V12zM12 12v10C6.477 22 2 17.523 2 12h10z"
|
||||
fill="#767676" /><text style="white-space:pre" x="5.187" y="9.486" fill="#fff" font-family="Arial,sans-serif"
|
||||
font-size="9" font-weight="700">1</text><text style="white-space:pre" x="37.928" y="7.522" fill="#333"
|
||||
font-family="Arial,sans-serif" font-size=".9">Enter your text here</text><text style="white-space:pre" x="12.861"
|
||||
y="10.486" fill="#fff" font-family="Arial,sans-serif" font-size="9" font-weight="700">2</text><text
|
||||
style="white-space:pre" x="6.187" y="19.585" fill="#fff" font-family="Arial,sans-serif" font-size="9"
|
||||
font-weight="700">3</text><text style="white-space:pre" x="12.861" y="19.585" fill="#fff"
|
||||
font-family="Arial,sans-serif" font-size="9" font-weight="700">4</text>
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,13 @@
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 12H2A10 10 0 0112 2z" fill="#767676" />
|
||||
<path d="M13 11V1c5.523 0 10 4.477 10 10H13z" fill="#2e7614" />
|
||||
<path d="M12 12h10c0 5.523-4.477 10-10 10V12zm0 0v10C6.477 22 2 17.523 2 12h10z" fill="#767676" /><text
|
||||
style="white-space:pre" x="6.187" y="10.486" fill="#fff" font-family="Arial,sans-serif" font-size="9"
|
||||
font-weight="700">1</text><text style="white-space:pre" x="37.928" y="7.522" fill="#333"
|
||||
font-family="Arial,sans-serif" font-size=".9">Enter your text here</text><text style="white-space:pre" x="13.861"
|
||||
y="9.486" fill="#fff" font-family="Arial,sans-serif" font-size="9" font-weight="700">2</text><text
|
||||
style="white-space:pre" x="6.187" y="19.585" fill="#fff" font-family="Arial,sans-serif" font-size="9"
|
||||
font-weight="700">3</text><text style="white-space:pre" x="12.861" y="19.585" fill="#fff"
|
||||
font-family="Arial,sans-serif" font-size="9" font-weight="700">4</text>
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 12H2A10 10 0 0112 2zM12 12V2c5.523 0 10 4.477 10 10H12zM12 12h10c0 5.523-4.477 10-10 10V12z"
|
||||
fill="#767676" />
|
||||
<path d="M11 13v10C5.477 23 1 18.523 1 13h10z" fill="#37761e" /><text style="white-space:pre" x="6.187" y="10.486"
|
||||
fill="#fff" font-family="Arial,sans-serif" font-size="9" font-weight="700">1</text><text style="white-space:pre"
|
||||
x="37.928" y="7.522" fill="#333" font-family="Arial,sans-serif" font-size=".9">Enter your text here</text><text
|
||||
style="white-space:pre" x="12.861" y="10.486" fill="#fff" font-family="Arial,sans-serif" font-size="9"
|
||||
font-weight="700">2</text><text style="white-space:pre" x="5.187" y="20.585" fill="#fff"
|
||||
font-family="Arial,sans-serif" font-size="9" font-weight="700">3</text><text style="white-space:pre" x="12.861"
|
||||
y="19.585" fill="#fff" font-family="Arial,sans-serif" font-size="9" font-weight="700">4</text>
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 12H2A10 10 0 0112 2zM12 12V2c5.523 0 10 4.477 10 10H12z" fill="#767676" />
|
||||
<path d="M13 13h10c0 5.523-4.477 10-10 10V13z" fill="#2a7617" />
|
||||
<path d="M12 12v10C6.477 22 2 17.523 2 12h10z" fill="#767676" /><text style="white-space:pre" x="6.187" y="10.486"
|
||||
fill="#fff" font-family="Arial,sans-serif" font-size="9" font-weight="700">1</text><text style="white-space:pre"
|
||||
x="37.928" y="7.522" fill="#333" font-family="Arial,sans-serif" font-size=".9">Enter your text here</text><text
|
||||
style="white-space:pre" x="12.861" y="10.486" fill="#fff" font-family="Arial,sans-serif" font-size="9"
|
||||
font-weight="700">2</text><text style="white-space:pre" x="6.187" y="19.585" fill="#fff"
|
||||
font-family="Arial,sans-serif" font-size="9" font-weight="700">3</text><text style="white-space:pre" x="13.861"
|
||||
y="20.585" fill="#fff" font-family="Arial,sans-serif" font-size="9" font-weight="700">4</text>
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,13 @@
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 12H2A10 10 0 0112 2z" fill="#767676" />
|
||||
<path d="M12 12V2A10 10 0 112 12h10z" opacity=".5" />
|
||||
<path d="M12 12V2c5.523 0 10 4.477 10 10H12zM12 12h10c0 5.523-4.477 10-10 10V12zM12 12v10C6.477 22 2 17.523 2 12h10z"
|
||||
fill="#767676" /><text style="white-space:pre" x="6.187" y="10.486" fill="#fff" font-family="Arial,sans-serif"
|
||||
font-size="9" font-weight="700">1</text><text style="white-space:pre" x="37.928" y="7.522" fill="#333"
|
||||
font-family="Arial,sans-serif" font-size=".9">Enter your text here</text><text style="white-space:pre" x="12.861"
|
||||
y="10.486" fill="#fff" font-family="Arial,sans-serif" font-size="9" font-weight="700">2</text><text
|
||||
style="white-space:pre" x="6.187" y="19.585" fill="#fff" font-family="Arial,sans-serif" font-size="9"
|
||||
font-weight="700">3</text><text style="white-space:pre" x="12.861" y="19.585" fill="#fff"
|
||||
font-family="Arial,sans-serif" font-size="9" font-weight="700">4</text>
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,20 @@
|
||||
UNKNOWN=unknown
|
||||
NO_ERR=no error
|
||||
TRAPPED=trapped
|
||||
LIFTED=lifted
|
||||
WIRE_MISSING=wire missing
|
||||
OUTSIDE_WIRE=outside wire
|
||||
RAINING=raining
|
||||
CLOSE_DOOR_TO_MOW=close door to mow
|
||||
CLOSE_DOOR_TO_GO_HOME=close door to go home
|
||||
BLADE_MOTOR_BLOCKED=blade motor blocked
|
||||
WHEEL_MOTOR_BLOCKED=wheel motor blocked
|
||||
TRAPPED_TIMEOUT=trapped timeout
|
||||
UPSIDE_DOWN=upside down
|
||||
BATTERY_LOW=battery low
|
||||
REVERSE_WIRE=reverse wire
|
||||
CHARGE_ERROR=charge errir
|
||||
TIMEOUT_FINDING_HOME=timeout finding home
|
||||
MOWER_LOCKED=mower locked
|
||||
BATTERY_OVER_TEMPERATURE=batter over temperature
|
||||
MOWER_OUTSIDE_WIRE=mower outside wire
|
||||
@@ -0,0 +1,20 @@
|
||||
UNKNOWN=unknown
|
||||
IDLE=idle
|
||||
HOME=home
|
||||
START_SEQUENCE=start sequence
|
||||
LEAVING_HOME=leaving home
|
||||
FOLLOW_WIRE=follow wire
|
||||
SEARCHING_HOME=searching home
|
||||
SEARCHING_WIRE=searching wire
|
||||
MOWING=mowing
|
||||
LIFTED=lifted
|
||||
TRAPPED=trapped
|
||||
BLADE_BLOCKED=blade blocked
|
||||
DEBUG=debug
|
||||
REMOTE_CONTROL=remote control
|
||||
GOING_HOME=going home
|
||||
ZONE_TRAINING=zone training
|
||||
BORDER_CUT=border cut
|
||||
SEARCHING_ZONE=searching zone
|
||||
PAUSE=pause
|
||||
MANUAL_STOP=manual stop
|
||||
@@ -489,6 +489,7 @@
|
||||
<module>org.openhab.binding.wlanthermo</module>
|
||||
<module>org.openhab.binding.wled</module>
|
||||
<module>org.openhab.binding.wolfsmartset</module>
|
||||
<module>org.openhab.binding.worxlandroid</module>
|
||||
<module>org.openhab.binding.wundergroundupdatereceiver</module>
|
||||
<module>org.openhab.binding.x</module>
|
||||
<module>org.openhab.binding.xmltv</module>
|
||||
|
||||