]> git.basschouten.com Git - openhab-addons.git/commitdiff
[myq] Remove MyQ Binding (#15911)
authorDan Cunningham <dan@digitaldan.com>
Sat, 18 Nov 2023 19:30:48 +0000 (11:30 -0800)
committerGitHub <noreply@github.com>
Sat, 18 Nov 2023 19:30:48 +0000 (20:30 +0100)
Fixes #15910

Signed-off-by: Dan Cunningham <dan@digitaldan.com>
25 files changed:
CODEOWNERS
bom/openhab-addons/pom.xml
bundles/org.openhab.binding.myq/NOTICE [deleted file]
bundles/org.openhab.binding.myq/README.md [deleted file]
bundles/org.openhab.binding.myq/pom.xml [deleted file]
bundles/org.openhab.binding.myq/src/main/feature/feature.xml [deleted file]
bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQBindingConstants.java [deleted file]
bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQDiscoveryService.java [deleted file]
bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQHandlerFactory.java [deleted file]
bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/config/MyQAccountConfiguration.java [deleted file]
bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/config/MyQDeviceConfiguration.java [deleted file]
bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/AccountDTO.java [deleted file]
bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/AccountsDTO.java [deleted file]
bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DeviceDTO.java [deleted file]
bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DeviceStateDTO.java [deleted file]
bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DevicesDTO.java [deleted file]
bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQAccountHandler.java [deleted file]
bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQDeviceHandler.java [deleted file]
bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQGarageDoorHandler.java [deleted file]
bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQLampHandler.java [deleted file]
bundles/org.openhab.binding.myq/src/main/resources/OH-INF/addon/addon.xml [deleted file]
bundles/org.openhab.binding.myq/src/main/resources/OH-INF/config/config.xml [deleted file]
bundles/org.openhab.binding.myq/src/main/resources/OH-INF/i18n/myq.properties [deleted file]
bundles/org.openhab.binding.myq/src/main/resources/OH-INF/thing/thing-types.xml [deleted file]
bundles/pom.xml

index 4b4e4a859c7908eac1bc9a1170cea6727eaee586..17363df8e07e8e943eb95dc9cd83f0abd2e0369d 100644 (file)
 /bundles/org.openhab.binding.mycroft/ @dalgwen
 /bundles/org.openhab.binding.mybmw/ @weymann @ntruchsess
 /bundles/org.openhab.binding.mynice/ @clinique
-/bundles/org.openhab.binding.myq/ @digitaldan
 /bundles/org.openhab.binding.mystrom/ @pail23
 /bundles/org.openhab.binding.nanoleaf/ @stefan-hoehn
 /bundles/org.openhab.binding.neato/ @jjlauterbach
index bfee3215ad361a62f4a360596bf5a5aee8195710..dadea0ba22ef7dcac59bc4f291980f754c367aaa 100644 (file)
       <artifactId>org.openhab.binding.mynice</artifactId>
       <version>${project.version}</version>
     </dependency>
-    <dependency>
-      <groupId>org.openhab.addons.bundles</groupId>
-      <artifactId>org.openhab.binding.myq</artifactId>
-      <version>${project.version}</version>
-    </dependency>
     <dependency>
       <groupId>org.openhab.addons.bundles</groupId>
       <artifactId>org.openhab.binding.mystrom</artifactId>
diff --git a/bundles/org.openhab.binding.myq/NOTICE b/bundles/org.openhab.binding.myq/NOTICE
deleted file mode 100644 (file)
index 0ca708b..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-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
-
-== Third-party Content
-
-jsoup
-* License: MIT License
-* Project: https://jsoup.org/
-* Source:  https://github.com/jhy/jsoup
diff --git a/bundles/org.openhab.binding.myq/README.md b/bundles/org.openhab.binding.myq/README.md
deleted file mode 100644 (file)
index 510285c..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-# MyQ Binding
-
-This binding integrates with the [The Chamberlain Group MyQ](https://www.myq.com) cloud service. It allows monitoring and control over [MyQ](https://www.myq.com) enabled garage doors manufactured by LiftMaster, Chamberlain and Craftsman.
-
-## Supported Things
-
-### Account
-
-This represents the MyQ cloud account and uses the same credentials needed when using the MyQ mobile application.
-
-ThingTypeUID: `account`
-
-### Garage Door
-
-This represents a garage door associated with an account. Multiple garage doors are supported.
-
-ThingTypeUID: `garagedoor`
-
-### Lamp
-
-This represents a lamp associated with an account. Multiple lamps are supported.
-
-ThingTypeUID: `lamp`
-
-## Discovery
-
-Once an account has been added, garage doors and lamps will automatically be discovered and added to the inbox.
-
-## Channels
-
-| Channel       | Item Type     | Thing Type       | States                                                 |
-|---------------|---------------|------------------|--------------------------------------------------------|
-| status        | String        | garagedoor       | opening, closed, closing, stopped, transition, unknown |
-| rollershutter | Rollershutter | garagedoor       | UP, DOWN, 0%, 100%                                     |
-| closeError    | Switch        | garagedoor       | ON (has error), OFF (doesn't have error)               |
-| openError     | Switch        | garagedoor       | ON (has error), OFF (doesn't have error)               |
-| switch        | Switch        | garagedoor, lamp | ON (open), OFF (closed)                                |
-
-## Full Example
-
-### Thing Configuration
-
-```java
-Bridge myq:account:home "MyQ Account" [ username="foo@bar.com", password="secret", refreshInterval=60 ] {
-    Thing garagedoor abcd12345 "MyQ Garage Door" [ serialNumber="abcd12345" ]
-    Thing lamp efgh6789 "MyQ Lamp" [ serialNumber="efgh6789" ]
-}
-```
-
-### Items
-
-```java
-String MyQGarageDoor1Status "Door Status [%s]" {channel = "myq:garagedoor:home:abcd12345:status"}
-Switch MyQGarageDoor1Switch "Door Switch [%s]" {channel = "myq:garagedoor:home:abcd12345:switch"}
-Switch MyQGarageDoor1CloseError "Door Close Error [%s]" {channel = "myq:garagedoor:home:abcd12345:closeError"}
-Switch MyQGarageDoor1OpenError "Door OpenError [%s]" {channel = "myq:garagedoor:home:abcd12345:openError"}
-Rollershutter MyQGarageDoor1Rollershutter "Door Rollershutter [%s]" {channel = "myq:garagedoor:home:abcd12345:rollershutter"}
-Switch MyQGarageDoorLamp "Lamp [%s]" {channel = "myq:lamp:home:efgh6789:switch"}
-}
-```
-
-### Sitemaps
-
-```perl
-sitemap MyQ label="MyQ Demo Sitemap" {
-  Frame label="Garage Door" {
-    String item=MyQGarageDoor1Status
-    Switch item=MyQGarageDoor1Switch
-    Rollershutter item=MyQGarageDoor1Rollershutter
-  }                
-}
-```
diff --git a/bundles/org.openhab.binding.myq/pom.xml b/bundles/org.openhab.binding.myq/pom.xml
deleted file mode 100644 (file)
index 7a776f7..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<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 https://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>4.1.0-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>org.openhab.binding.myq</artifactId>
-
-  <name>openHAB Add-ons :: Bundles :: MyQ Binding</name>
-
-  <dependencies>
-    <dependency>
-      <groupId>org.jsoup</groupId>
-      <artifactId>jsoup</artifactId>
-      <version>1.14.3</version>
-      <scope>provided</scope>
-    </dependency>
-  </dependencies>
-</project>
diff --git a/bundles/org.openhab.binding.myq/src/main/feature/feature.xml b/bundles/org.openhab.binding.myq/src/main/feature/feature.xml
deleted file mode 100644 (file)
index 0c938aa..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<features name="org.openhab.binding.myq-${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-myq" description="MyQ Binding" version="${project.version}">
-               <feature>openhab-runtime-base</feature>
-               <bundle dependency="true">mvn:org.jsoup/jsoup/1.14.3</bundle>
-               <bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.myq/${project.version}</bundle>
-       </feature>
-</features>
diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQBindingConstants.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQBindingConstants.java
deleted file mode 100644 (file)
index fc31bb3..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * Copyright (c) 2010-2023 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.myq.internal;
-
-import java.util.Set;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.core.thing.ThingTypeUID;
-
-/**
- * The {@link MyQBindingConstants} class defines common constants, which are
- * used across the whole binding.
- *
- * @author Dan Cunningham - Initial contribution
- */
-@NonNullByDefault
-public class MyQBindingConstants {
-
-    public static final String BINDING_ID = "myq";
-
-    public static final ThingTypeUID THING_TYPE_ACCOUNT = new ThingTypeUID(BINDING_ID, "account");
-    public static final ThingTypeUID THING_TYPE_GARAGEDOOR = new ThingTypeUID(BINDING_ID, "garagedoor");
-    public static final ThingTypeUID THING_TYPE_LAMP = new ThingTypeUID(BINDING_ID, "lamp");
-    public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_ACCOUNT, THING_TYPE_GARAGEDOOR,
-            THING_TYPE_LAMP);
-    public static final Set<ThingTypeUID> SUPPORTED_DISCOVERY_THING_TYPES_UIDS = Set.of(THING_TYPE_GARAGEDOOR,
-            THING_TYPE_LAMP);
-}
diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQDiscoveryService.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQDiscoveryService.java
deleted file mode 100644 (file)
index 48863f2..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/**
- * Copyright (c) 2010-2023 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.myq.internal;
-
-import static org.openhab.binding.myq.internal.MyQBindingConstants.BINDING_ID;
-
-import java.util.List;
-import java.util.Set;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.myq.internal.dto.DeviceDTO;
-import org.openhab.binding.myq.internal.handler.MyQAccountHandler;
-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.config.discovery.DiscoveryService;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingTypeUID;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.openhab.core.thing.binding.ThingHandlerService;
-
-/**
- * The {@link MyQDiscoveryService} is responsible for discovering MyQ things
- *
- * @author Dan Cunningham - Initial contribution
- */
-@NonNullByDefault
-public class MyQDiscoveryService extends AbstractDiscoveryService implements DiscoveryService, ThingHandlerService {
-
-    private static final Set<ThingTypeUID> SUPPORTED_DISCOVERY_THING_TYPES_UIDS = Set
-            .of(MyQBindingConstants.THING_TYPE_GARAGEDOOR, MyQBindingConstants.THING_TYPE_LAMP);
-    private @Nullable MyQAccountHandler accountHandler;
-
-    public MyQDiscoveryService() {
-        super(SUPPORTED_DISCOVERY_THING_TYPES_UIDS, 1, true);
-    }
-
-    @Override
-    public Set<ThingTypeUID> getSupportedThingTypes() {
-        return SUPPORTED_DISCOVERY_THING_TYPES_UIDS;
-    }
-
-    @Override
-    public void startScan() {
-        MyQAccountHandler accountHandler = this.accountHandler;
-        if (accountHandler != null) {
-            List<DeviceDTO> devices = accountHandler.devicesCache();
-            if (devices != null) {
-                devices.forEach(device -> {
-                    ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, device.deviceFamily);
-                    if (SUPPORTED_DISCOVERY_THING_TYPES_UIDS.contains(thingTypeUID)) {
-                        ThingUID thingUID = new ThingUID(thingTypeUID, accountHandler.getThing().getUID(),
-                                device.serialNumber.toLowerCase());
-                        DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withLabel("MyQ " + device.name)
-                                .withProperty(Thing.PROPERTY_SERIAL_NUMBER, thingUID.getId())
-                                .withRepresentationProperty(Thing.PROPERTY_SERIAL_NUMBER)
-                                .withBridge(accountHandler.getThing().getUID()).build();
-                        thingDiscovered(result);
-                    }
-                });
-            }
-        }
-    }
-
-    @Override
-    public void startBackgroundDiscovery() {
-        startScan();
-    }
-
-    @Override
-    public void setThingHandler(ThingHandler handler) {
-        if (handler instanceof MyQAccountHandler myqAccountHandler) {
-            accountHandler = myqAccountHandler;
-        }
-    }
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return accountHandler;
-    }
-
-    @Override
-    public void activate() {
-        super.activate(null);
-    }
-
-    @Override
-    public void deactivate() {
-        super.deactivate();
-    }
-}
diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQHandlerFactory.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/MyQHandlerFactory.java
deleted file mode 100644 (file)
index 475c9fc..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/**
- * Copyright (c) 2010-2023 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.myq.internal;
-
-import static org.openhab.binding.myq.internal.MyQBindingConstants.*;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.jetty.client.HttpClient;
-import org.openhab.binding.myq.internal.handler.MyQAccountHandler;
-import org.openhab.binding.myq.internal.handler.MyQGarageDoorHandler;
-import org.openhab.binding.myq.internal.handler.MyQLampHandler;
-import org.openhab.core.auth.client.oauth2.OAuthFactory;
-import org.openhab.core.io.net.http.HttpClientFactory;
-import org.openhab.core.thing.Bridge;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingTypeUID;
-import org.openhab.core.thing.binding.BaseThingHandlerFactory;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.openhab.core.thing.binding.ThingHandlerFactory;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-
-/**
- * The {@link MyQHandlerFactory} is responsible for creating things and thing
- * handlers.
- *
- * @author Dan Cunningham - Initial contribution
- */
-@NonNullByDefault
-@Component(configurationPid = "binding.myq", service = ThingHandlerFactory.class)
-public class MyQHandlerFactory extends BaseThingHandlerFactory {
-    private final HttpClient httpClient;
-    private OAuthFactory oAuthFactory;
-
-    @Activate
-    public MyQHandlerFactory(final @Reference HttpClientFactory httpClientFactory,
-            final @Reference OAuthFactory oAuthFactory) {
-        this.httpClient = httpClientFactory.getCommonHttpClient();
-        this.oAuthFactory = oAuthFactory;
-    }
-
-    @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_ACCOUNT.equals(thingTypeUID)) {
-            return new MyQAccountHandler((Bridge) thing, httpClient, oAuthFactory);
-        }
-
-        if (THING_TYPE_GARAGEDOOR.equals(thingTypeUID)) {
-            return new MyQGarageDoorHandler(thing);
-        }
-
-        if (THING_TYPE_LAMP.equals(thingTypeUID)) {
-            return new MyQLampHandler(thing);
-        }
-
-        return null;
-    }
-}
diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/config/MyQAccountConfiguration.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/config/MyQAccountConfiguration.java
deleted file mode 100644 (file)
index 245261f..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2010-2023 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.myq.internal.config;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * The {@link MyQAccountConfiguration} class contains fields mapping thing configuration parameters.
- *
- * @author Dan Cunningham - Initial contribution
- */
-@NonNullByDefault
-public class MyQAccountConfiguration {
-    public String username = "";
-    public String password = "";
-    public Integer refreshInterval = 60;
-}
diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/config/MyQDeviceConfiguration.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/config/MyQDeviceConfiguration.java
deleted file mode 100644 (file)
index 1a5ed87..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * Copyright (c) 2010-2023 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.myq.internal.config;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * The {@link MyQDeviceConfiguration} class contains fields mapping thing configuration parameters.
- *
- * @author Dan Cunningham - Initial contribution
- */
-@NonNullByDefault
-public class MyQDeviceConfiguration {
-    public String serialNumber = "";
-}
diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/AccountDTO.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/AccountDTO.java
deleted file mode 100644 (file)
index 7a74a39..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Copyright (c) 2010-2023 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.myq.internal.dto;
-
-/**
- * The {@link AccountDTO} entity from the MyQ API
- *
- * @author Dan Cunningham - Initial contribution
- */
-public class AccountDTO {
-    public String id;
-    public String name;
-    public String createdBy;
-}
diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/AccountsDTO.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/AccountsDTO.java
deleted file mode 100644 (file)
index 6346476..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Copyright (c) 2010-2023 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.myq.internal.dto;
-
-import java.util.List;
-
-/**
- * The {@link AccountsDTO} entity from the MyQ API
- *
- * @author Dan Cunningham - Initial contribution
- */
-public class AccountsDTO {
-    public List<AccountDTO> accounts;
-}
diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DeviceDTO.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DeviceDTO.java
deleted file mode 100644 (file)
index 96b7749..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright (c) 2010-2023 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.myq.internal.dto;
-
-/**
- * The {@link DeviceDTO} entity from the MyQ API
- *
- * @author Dan Cunningham - Initial contribution
- */
-public class DeviceDTO {
-    public String href;
-    public String serialNumber;
-    public String deviceFamily;
-    public String devicePlatform;
-    public String deviceType;
-    public String name;
-    public String createdDate;
-    public String accountId;
-    public DeviceStateDTO state;
-    public String parentDevice;
-    public String parentDeviceId;
-}
diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DeviceStateDTO.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DeviceStateDTO.java
deleted file mode 100644 (file)
index 02d306e..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * Copyright (c) 2010-2023 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.myq.internal.dto;
-
-/**
- * The {@link DeviceStateDTO} entity from the MyQ API
- *
- * @author Dan Cunningham - Initial contribution
- */
-public class DeviceStateDTO {
-
-    public Boolean gdoLockConnected;
-    public Boolean attachedWorkLightErrorPresent;
-    public String learnStatus;
-    public Boolean hasCamera;
-    public String lampState;
-    public String batteryBackupState;
-    public String doorState;
-    public String lastUpdate;
-    public Boolean isUnattendedOpenAllowed;
-    public Boolean isUnattendedCloseAllowed;
-    public Integer serviceCycleCount;
-    public Integer absoluteCycleCount;
-    public Boolean online;
-    public String lastStatus;
-}
diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DevicesDTO.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/dto/DevicesDTO.java
deleted file mode 100644 (file)
index 9f1e997..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Copyright (c) 2010-2023 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.myq.internal.dto;
-
-import java.util.List;
-
-/**
- * The {@link DevicesDTO} entity from the MyQ API
- *
- * @author Dan Cunningham - Initial contribution
- */
-public class DevicesDTO {
-    public String href;
-    public Integer count;
-    public List<DeviceDTO> items;
-}
diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQAccountHandler.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQAccountHandler.java
deleted file mode 100644 (file)
index 1911c73..0000000
+++ /dev/null
@@ -1,675 +0,0 @@
-/**
- * Copyright (c) 2010-2023 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.myq.internal.handler;
-
-import static org.openhab.binding.myq.internal.MyQBindingConstants.*;
-
-import java.io.IOException;
-import java.net.CookieStore;
-import java.net.HttpCookie;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Base64;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.stream.Collectors;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.HttpContentResponse;
-import org.eclipse.jetty.client.api.ContentProvider;
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.api.Result;
-import org.eclipse.jetty.client.util.BufferingResponseListener;
-import org.eclipse.jetty.client.util.FormContentProvider;
-import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.util.Fields;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Document;
-import org.jsoup.nodes.Element;
-import org.openhab.binding.myq.internal.MyQDiscoveryService;
-import org.openhab.binding.myq.internal.config.MyQAccountConfiguration;
-import org.openhab.binding.myq.internal.dto.AccountDTO;
-import org.openhab.binding.myq.internal.dto.AccountsDTO;
-import org.openhab.binding.myq.internal.dto.DeviceDTO;
-import org.openhab.binding.myq.internal.dto.DevicesDTO;
-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.Thing;
-import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.ThingStatusDetail;
-import org.openhab.core.thing.ThingTypeUID;
-import org.openhab.core.thing.binding.BaseBridgeHandler;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.openhab.core.thing.binding.ThingHandlerService;
-import org.openhab.core.types.Command;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.FieldNamingPolicy;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonSyntaxException;
-
-/**
- * The {@link MyQAccountHandler} is responsible for communicating with the MyQ API based on an account.
- *
- * @author Dan Cunningham - Initial contribution
- */
-@NonNullByDefault
-public class MyQAccountHandler extends BaseBridgeHandler implements AccessTokenRefreshListener {
-    private static final int REQUEST_TIMEOUT_MS = 10_000;
-
-    /*
-     * MyQ oAuth relate fields
-     */
-    private static final String CLIENT_SECRET = "VUQ0RFhuS3lQV3EyNUJTdw==";
-    private static final String CLIENT_ID = "ANDROID_CGI_MYQ";
-    private static final String REDIRECT_URI = "com.myqops://android";
-    private static final String SCOPE = "MyQ_Residential offline_access";
-    /*
-     * MyQ authentication API endpoints
-     */
-    private static final String LOGIN_BASE_URL = "https://partner-identity.myq-cloud.com";
-    private static final String LOGIN_AUTHORIZE_URL = LOGIN_BASE_URL + "/connect/authorize";
-    private static final String LOGIN_TOKEN_URL = LOGIN_BASE_URL + "/connect/token";
-    // this should never happen, but lets be safe and give up after so many redirects
-    private static final int LOGIN_MAX_REDIRECTS = 30;
-    /*
-     * MyQ device and account API endpoints
-     */
-    private static final String ACCOUNTS_URL = "https://accounts.myq-cloud.com/api/v6.0/accounts";
-    private static final String DEVICES_URL = "https://devices.myq-cloud.com/api/v5.2/Accounts/%s/Devices";
-    private static final String CMD_LAMP_URL = "https://account-devices-lamp.myq-cloud.com/api/v5.2/Accounts/%s/lamps/%s/%s";
-    private static final String CMD_DOOR_URL = "https://account-devices-gdo.myq-cloud.com/api/v5.2/Accounts/%s/door_openers/%s/%s";
-
-    private static final Integer RAPID_REFRESH_SECONDS = 5;
-    private final Logger logger = LoggerFactory.getLogger(MyQAccountHandler.class);
-    private final Gson gsonLowerCase = new GsonBuilder()
-            .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
-    private final OAuthFactory oAuthFactory;
-    private @Nullable Future<?> normalPollFuture;
-    private @Nullable Future<?> rapidPollFuture;
-    private @Nullable AccountsDTO accounts;
-
-    private List<DeviceDTO> devicesCache = new ArrayList<DeviceDTO>();
-    private @Nullable OAuthClientService oAuthService;
-    private Integer normalRefreshSeconds = 60;
-    private HttpClient httpClient;
-    private String username = "";
-    private String password = "";
-    private String userAgent = "";
-    // force login, even if we have a token
-    private boolean needsLogin = false;
-
-    public MyQAccountHandler(Bridge bridge, HttpClient httpClient, final OAuthFactory oAuthFactory) {
-        super(bridge);
-        this.httpClient = httpClient;
-        this.oAuthFactory = oAuthFactory;
-    }
-
-    @Override
-    public void handleCommand(ChannelUID channelUID, Command command) {
-    }
-
-    @Override
-    public void initialize() {
-        MyQAccountConfiguration config = getConfigAs(MyQAccountConfiguration.class);
-        normalRefreshSeconds = config.refreshInterval;
-        username = config.username;
-        password = config.password;
-        // MyQ can get picky about blocking user agents apparently
-        userAgent = ""; // no agent string
-        needsLogin = true;
-        updateStatus(ThingStatus.UNKNOWN);
-        restartPolls(false);
-    }
-
-    @Override
-    public void dispose() {
-        stopPolls();
-        OAuthClientService oAuthService = this.oAuthService;
-        if (oAuthService != null) {
-            oAuthService.removeAccessTokenRefreshListener(this);
-            oAuthFactory.ungetOAuthService(getThing().toString());
-            this.oAuthService = null;
-        }
-    }
-
-    @Override
-    public void handleRemoval() {
-        oAuthFactory.deleteServiceAndAccessToken(getThing().toString());
-        super.handleRemoval();
-    }
-
-    @Override
-    public Collection<Class<? extends ThingHandlerService>> getServices() {
-        return Set.of(MyQDiscoveryService.class);
-    }
-
-    @Override
-    public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) {
-        List<DeviceDTO> localDeviceCaches = devicesCache;
-        if (childHandler instanceof MyQDeviceHandler deviceHandler) {
-            localDeviceCaches.stream().filter(d -> deviceHandler.getSerialNumber().equalsIgnoreCase(d.serialNumber))
-                    .findFirst().ifPresent(deviceHandler::handleDeviceUpdate);
-        }
-    }
-
-    @Override
-    public void onAccessTokenResponse(AccessTokenResponse tokenResponse) {
-        logger.debug("Auth Token Refreshed, expires in {}", tokenResponse.getExpiresIn());
-    }
-
-    /**
-     * Sends a door action to the MyQ API
-     *
-     * @param device
-     * @param action
-     */
-    public void sendDoorAction(DeviceDTO device, String action) {
-        sendAction(device, action, CMD_DOOR_URL);
-    }
-
-    /**
-     * Sends a lamp action to the MyQ API
-     *
-     * @param device
-     * @param action
-     */
-    public void sendLampAction(DeviceDTO device, String action) {
-        sendAction(device, action, CMD_LAMP_URL);
-    }
-
-    private void sendAction(DeviceDTO device, String action, String urlFormat) {
-        if (getThing().getStatus() != ThingStatus.ONLINE) {
-            logger.debug("Account offline, ignoring action {}", action);
-            return;
-        }
-
-        try {
-            ContentResponse response = sendRequest(
-                    String.format(urlFormat, device.accountId, device.serialNumber, action), HttpMethod.PUT, null,
-                    null);
-            if (HttpStatus.isSuccess(response.getStatus())) {
-                restartPolls(true);
-            } else {
-                logger.debug("Failed to send action {} : {}", action, response.getContentAsString());
-            }
-        } catch (InterruptedException | MyQCommunicationException | MyQAuthenticationException e) {
-            logger.debug("Could not send action", e);
-        }
-    }
-
-    /**
-     * Last known state of MyQ Devices
-     *
-     * @return cached MyQ devices
-     */
-    public @Nullable List<DeviceDTO> devicesCache() {
-        return devicesCache;
-    }
-
-    private void stopPolls() {
-        stopNormalPoll();
-        stopRapidPoll();
-    }
-
-    private synchronized void stopNormalPoll() {
-        stopFuture(normalPollFuture);
-        normalPollFuture = null;
-    }
-
-    private synchronized void stopRapidPoll() {
-        stopFuture(rapidPollFuture);
-        rapidPollFuture = null;
-    }
-
-    private void stopFuture(@Nullable Future<?> future) {
-        if (future != null) {
-            future.cancel(true);
-        }
-    }
-
-    private synchronized void restartPolls(boolean rapid) {
-        stopPolls();
-        if (rapid) {
-            normalPollFuture = scheduler.scheduleWithFixedDelay(this::normalPoll, 35, normalRefreshSeconds,
-                    TimeUnit.SECONDS);
-            rapidPollFuture = scheduler.scheduleWithFixedDelay(this::rapidPoll, 3, RAPID_REFRESH_SECONDS,
-                    TimeUnit.SECONDS);
-        } else {
-            normalPollFuture = scheduler.scheduleWithFixedDelay(this::normalPoll, 0, normalRefreshSeconds,
-                    TimeUnit.SECONDS);
-        }
-    }
-
-    private void normalPoll() {
-        stopRapidPoll();
-        fetchData();
-    }
-
-    private void rapidPoll() {
-        fetchData();
-    }
-
-    private synchronized void fetchData() {
-        try {
-            if (accounts == null) {
-                getAccounts();
-            }
-            getDevices();
-        } catch (MyQCommunicationException e) {
-            logger.debug("MyQ communication error", e);
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
-        } catch (MyQAuthenticationException e) {
-            logger.debug("MyQ authentication error", e);
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
-            stopPolls();
-        } catch (InterruptedException e) {
-            // we were shut down, ignore
-        }
-    }
-
-    /**
-     * This attempts to navigate the MyQ oAuth login flow in order to obtain a @AccessTokenResponse
-     *
-     * @return AccessTokenResponse token
-     * @throws InterruptedException
-     * @throws MyQCommunicationException
-     * @throws MyQAuthenticationException
-     */
-    private AccessTokenResponse login()
-            throws InterruptedException, MyQCommunicationException, MyQAuthenticationException {
-        try {
-            // make sure we have a fresh session
-            URI authUri = new URI(LOGIN_BASE_URL);
-            CookieStore store = httpClient.getCookieStore();
-            store.get(authUri).forEach(cookie -> {
-                store.remove(authUri, cookie);
-            });
-
-            String codeVerifier = generateCodeVerifier();
-
-            ContentResponse loginPageResponse = getLoginPage(codeVerifier);
-
-            // load the login page to get cookies and form parameters
-            Document loginPage = Jsoup.parse(loginPageResponse.getContentAsString());
-            Element form = loginPage.select("form").first();
-            Element requestToken = loginPage.select("input[name=__RequestVerificationToken]").first();
-            Element returnURL = loginPage.select("input[name=ReturnUrl]").first();
-
-            if (form == null || requestToken == null) {
-                throw new MyQCommunicationException("Could not load login page");
-            }
-
-            // url that the form will submit to
-            String action = LOGIN_BASE_URL + form.attr("action");
-
-            // post our user name and password along with elements from the scraped form
-            String location = postLogin(action, requestToken.attr("value"), returnURL.attr("value"));
-            if (location == null) {
-                throw new MyQAuthenticationException("Could not login with credentials");
-            }
-
-            // finally complete the oAuth flow and retrieve a JSON oAuth token response
-            ContentResponse tokenResponse = getLoginToken(location, codeVerifier);
-            String loginToken = tokenResponse.getContentAsString();
-
-            try {
-                AccessTokenResponse accessTokenResponse = gsonLowerCase.fromJson(loginToken, AccessTokenResponse.class);
-                if (accessTokenResponse == null) {
-                    throw new MyQAuthenticationException("Could not parse token response");
-                }
-                getOAuthService().importAccessTokenResponse(accessTokenResponse);
-                return accessTokenResponse;
-            } catch (JsonSyntaxException e) {
-                throw new MyQCommunicationException("Invalid Token Response " + loginToken);
-            }
-        } catch (IOException | ExecutionException | TimeoutException | OAuthException | URISyntaxException e) {
-            throw new MyQCommunicationException(e.getMessage());
-        }
-    }
-
-    private void getAccounts() throws InterruptedException, MyQCommunicationException, MyQAuthenticationException {
-        ContentResponse response = sendRequest(ACCOUNTS_URL, HttpMethod.GET, null, null);
-        accounts = parseResultAndUpdateStatus(response, gsonLowerCase, AccountsDTO.class);
-    }
-
-    private void getDevices() throws InterruptedException, MyQCommunicationException, MyQAuthenticationException {
-        AccountsDTO localAccounts = accounts;
-        if (localAccounts == null) {
-            return;
-        }
-
-        List<DeviceDTO> currentDevices = new ArrayList<DeviceDTO>();
-
-        for (AccountDTO account : localAccounts.accounts) {
-            ContentResponse response = sendRequest(String.format(DEVICES_URL, account.id), HttpMethod.GET, null, null);
-            DevicesDTO devices = parseResultAndUpdateStatus(response, gsonLowerCase, DevicesDTO.class);
-            currentDevices.addAll(devices.items);
-            devices.items.forEach(device -> {
-                ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, device.deviceFamily);
-                if (SUPPORTED_DISCOVERY_THING_TYPES_UIDS.contains(thingTypeUID)) {
-                    for (Thing thing : getThing().getThings()) {
-                        ThingHandler handler = thing.getHandler();
-                        if (handler != null && ((MyQDeviceHandler) handler).getSerialNumber()
-                                .equalsIgnoreCase(device.serialNumber)) {
-                            ((MyQDeviceHandler) handler).handleDeviceUpdate(device);
-                        }
-                    }
-                }
-            });
-        }
-        devicesCache = currentDevices;
-    }
-
-    private synchronized ContentResponse sendRequest(String url, HttpMethod method, @Nullable ContentProvider content,
-            @Nullable String contentType)
-            throws InterruptedException, MyQCommunicationException, MyQAuthenticationException {
-        AccessTokenResponse tokenResponse = null;
-        // if we don't need to force a login, attempt to use the token we have
-        if (!needsLogin) {
-            try {
-                tokenResponse = getOAuthService().getAccessTokenResponse();
-            } catch (OAuthException | IOException | OAuthResponseException e) {
-                // ignore error, will try to login below
-                logger.debug("Error accessing token, will attempt to login again", e);
-            }
-        }
-
-        // if no token, or we need to login, do so now
-        if (tokenResponse == null) {
-            tokenResponse = login();
-            needsLogin = false;
-        }
-
-        Request request = httpClient.newRequest(url).method(method).agent(userAgent)
-                .timeout(REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)
-                .header("Authorization", authTokenHeader(tokenResponse));
-        if (content != null & contentType != null) {
-            request = request.content(content, contentType);
-        }
-
-        // use asyc jetty as the API service will response with a 401 error when credentials are wrong,
-        // but not a WWW-Authenticate header which causes Jetty to throw a generic execution exception which
-        // prevents us from knowing the response code
-        logger.trace("Sending {} to {}", request.getMethod(), request.getURI());
-        final CompletableFuture<ContentResponse> futureResult = new CompletableFuture<>();
-        request.send(new BufferingResponseListener() {
-            @NonNullByDefault({})
-            @Override
-            public void onComplete(Result result) {
-                Response response = result.getResponse();
-                futureResult.complete(new HttpContentResponse(response, getContent(), getMediaType(), getEncoding()));
-            }
-        });
-
-        try {
-            ContentResponse result = futureResult.get();
-            logger.trace("Account Response - status: {} content: {}", result.getStatus(), result.getContentAsString());
-            return result;
-        } catch (ExecutionException e) {
-            throw new MyQCommunicationException(e.getMessage());
-        }
-    }
-
-    private <T> T parseResultAndUpdateStatus(ContentResponse response, Gson parser, Class<T> classOfT)
-            throws MyQCommunicationException {
-        if (HttpStatus.isSuccess(response.getStatus())) {
-            try {
-                T responseObject = parser.fromJson(response.getContentAsString(), classOfT);
-                if (responseObject != null) {
-                    if (getThing().getStatus() != ThingStatus.ONLINE) {
-                        updateStatus(ThingStatus.ONLINE);
-                    }
-                    return responseObject;
-                } else {
-                    throw new MyQCommunicationException("Bad response from server");
-                }
-            } catch (JsonSyntaxException e) {
-                throw new MyQCommunicationException("Invalid JSON Response " + response.getContentAsString());
-            }
-        } else if (response.getStatus() == HttpStatus.UNAUTHORIZED_401) {
-            // our tokens no longer work, will need to login again
-            needsLogin = true;
-            throw new MyQCommunicationException("Token was rejected for request");
-        } else {
-            throw new MyQCommunicationException(
-                    "Invalid Response Code " + response.getStatus() + " : " + response.getContentAsString());
-        }
-    }
-
-    /**
-     * Returns the MyQ login page which contains form elements and cookies needed to login
-     *
-     * @param codeVerifier
-     * @return
-     * @throws InterruptedException
-     * @throws ExecutionException
-     * @throws TimeoutException
-     */
-    private ContentResponse getLoginPage(String codeVerifier)
-            throws InterruptedException, ExecutionException, TimeoutException {
-        try {
-            Request request = httpClient.newRequest(LOGIN_AUTHORIZE_URL) //
-                    .timeout(REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS) //
-                    .param("client_id", CLIENT_ID) //
-                    .param("code_challenge", generateCodeChallange(codeVerifier)) //
-                    .param("code_challenge_method", "S256") //
-                    .param("redirect_uri", REDIRECT_URI) //
-                    .param("response_type", "code") //
-                    .param("scope", SCOPE) //
-                    .agent(userAgent).followRedirects(true);
-            request.header("Accept", "\"*/*\"");
-            request.header("Authorization",
-                    "Basic " + Base64.getEncoder().encodeToString((CLIENT_ID + ":").getBytes()));
-            logger.debug("Sending {} to {}", request.getMethod(), request.getURI());
-            ContentResponse response = request.send();
-            logger.debug("Login Code {} Response {}", response.getStatus(), response.getContentAsString());
-            return response;
-        } catch (NoSuchAlgorithmException e) {
-            throw new ExecutionException(e.getCause());
-        }
-    }
-
-    /**
-     * Sends configured credentials and elements from the login page in order to obtain a redirect location header value
-     *
-     * @param url
-     * @param requestToken
-     * @param returnURL
-     * @return The location header value
-     * @throws InterruptedException
-     * @throws ExecutionException
-     * @throws TimeoutException
-     */
-    @Nullable
-    private String postLogin(String url, String requestToken, String returnURL)
-            throws InterruptedException, ExecutionException, TimeoutException {
-        /*
-         * on a successful post to this page we will get several redirects, and a final 301 to:
-         * com.myqops://ios?code=0123456789&scope=MyQ_Residential%20offline_access&iss=https%3A%2F%2Fpartner-identity.
-         * myq-cloud.com
-         *
-         * We can then take the parameters out of this location and continue the process
-         */
-        Fields fields = new Fields();
-        fields.add("Email", username);
-        fields.add("Password", password);
-        fields.add("__RequestVerificationToken", requestToken);
-        fields.add("ReturnUrl", returnURL);
-
-        Request request = httpClient.newRequest(url).method(HttpMethod.POST) //
-                .timeout(REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS) //
-                .content(new FormContentProvider(fields)) //
-                .agent(userAgent) //
-                .followRedirects(false);
-        setCookies(request);
-
-        logger.debug("Posting Login to {}", url);
-        ContentResponse response = request.send();
-
-        String location = null;
-
-        // follow redirects until we match our REDIRECT_URI or hit a redirect safety limit
-        for (int i = 0; i < LOGIN_MAX_REDIRECTS && HttpStatus.isRedirection(response.getStatus()); i++) {
-
-            String loc = response.getHeaders().get("location");
-            if (logger.isTraceEnabled()) {
-                logger.trace("Redirect Login: Code {} Location Header: {} Response {}", response.getStatus(), loc,
-                        response.getContentAsString());
-            }
-            if (loc == null) {
-                logger.debug("No location value");
-                break;
-            }
-            if (loc.indexOf(REDIRECT_URI) == 0) {
-                location = loc;
-                break;
-            }
-            request = httpClient.newRequest(LOGIN_BASE_URL + loc).timeout(REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)
-                    .agent(userAgent).followRedirects(false);
-            setCookies(request);
-            response = request.send();
-        }
-        return location;
-    }
-
-    /**
-     * Final step of the login process to get an oAuth access response token
-     *
-     * @param redirectLocation
-     * @param codeVerifier
-     * @return
-     * @throws InterruptedException
-     * @throws ExecutionException
-     * @throws TimeoutException
-     */
-    private ContentResponse getLoginToken(String redirectLocation, String codeVerifier)
-            throws InterruptedException, ExecutionException, TimeoutException {
-        try {
-            Map<String, String> params = parseLocationQuery(redirectLocation);
-
-            Fields fields = new Fields();
-            fields.add("client_id", CLIENT_ID);
-            fields.add("client_secret", Base64.getEncoder().encodeToString(CLIENT_SECRET.getBytes()));
-            fields.add("code", params.get("code"));
-            fields.add("code_verifier", codeVerifier);
-            fields.add("grant_type", "authorization_code");
-            fields.add("redirect_uri", REDIRECT_URI);
-            fields.add("scope", params.get("scope"));
-
-            Request request = httpClient.newRequest(LOGIN_TOKEN_URL) //
-                    .timeout(REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS) //
-                    .content(new FormContentProvider(fields)) //
-                    .method(HttpMethod.POST) //
-                    .agent(userAgent).followRedirects(true);
-            setCookies(request);
-            ContentResponse response = request.send();
-            if (logger.isTraceEnabled()) {
-                logger.trace("Login Code {} Response {}", response.getStatus(), response.getContentAsString());
-            }
-            return response;
-        } catch (URISyntaxException e) {
-            throw new ExecutionException(e.getCause());
-        }
-    }
-
-    private OAuthClientService getOAuthService() {
-        OAuthClientService oAuthService = this.oAuthService;
-        if (oAuthService == null || oAuthService.isClosed()) {
-            oAuthService = oAuthFactory.createOAuthClientService(getThing().toString(), LOGIN_TOKEN_URL,
-                    LOGIN_AUTHORIZE_URL, CLIENT_ID, CLIENT_SECRET, SCOPE, false);
-            oAuthService.addAccessTokenRefreshListener(this);
-            this.oAuthService = oAuthService;
-        }
-        return oAuthService;
-    }
-
-    private String generateCodeVerifier() {
-        SecureRandom secureRandom = new SecureRandom();
-        byte[] codeVerifier = new byte[32];
-        secureRandom.nextBytes(codeVerifier);
-        return Base64.getUrlEncoder().withoutPadding().encodeToString(codeVerifier);
-    }
-
-    private String generateCodeChallange(String codeVerifier) throws NoSuchAlgorithmException {
-        byte[] bytes = codeVerifier.getBytes(StandardCharsets.US_ASCII);
-        MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
-        messageDigest.update(bytes, 0, bytes.length);
-        byte[] digest = messageDigest.digest();
-        return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
-    }
-
-    private Map<String, String> parseLocationQuery(String location) throws URISyntaxException {
-        URI uri = new URI(location);
-        return Arrays.stream(uri.getQuery().split("&")).map(str -> str.split("="))
-                .collect(Collectors.toMap(str -> str[0], str -> str[1]));
-    }
-
-    private void setCookies(Request request) {
-        for (HttpCookie c : httpClient.getCookieStore().getCookies()) {
-            request.cookie(c);
-        }
-    }
-
-    private String authTokenHeader(AccessTokenResponse tokenResponse) {
-        return tokenResponse.getTokenType() + " " + tokenResponse.getAccessToken();
-    }
-
-    /**
-     * Exception for authenticated related errors
-     */
-    class MyQAuthenticationException extends Exception {
-        private static final long serialVersionUID = 1L;
-
-        public MyQAuthenticationException(String message) {
-            super(message);
-        }
-    }
-
-    /**
-     * Generic exception for non authentication related errors when communicating with the MyQ service.
-     */
-    class MyQCommunicationException extends IOException {
-        private static final long serialVersionUID = 1L;
-
-        public MyQCommunicationException(@Nullable String message) {
-            super(message);
-        }
-    }
-}
diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQDeviceHandler.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQDeviceHandler.java
deleted file mode 100644 (file)
index b216daa..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * Copyright (c) 2010-2023 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.myq.internal.handler;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.myq.internal.dto.DeviceDTO;
-
-/**
- * The {@link MyQDeviceHandler} is responsible for handling device updates
- *
- * @author Dan Cunningham - Initial contribution
- */
-@NonNullByDefault
-public interface MyQDeviceHandler {
-    void handleDeviceUpdate(DeviceDTO device);
-
-    String getSerialNumber();
-}
diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQGarageDoorHandler.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQGarageDoorHandler.java
deleted file mode 100644 (file)
index 7f75916..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-/**
- * Copyright (c) 2010-2023 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.myq.internal.handler;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.myq.internal.MyQBindingConstants;
-import org.openhab.binding.myq.internal.config.MyQDeviceConfiguration;
-import org.openhab.binding.myq.internal.dto.DeviceDTO;
-import org.openhab.core.library.types.OnOffType;
-import org.openhab.core.library.types.PercentType;
-import org.openhab.core.library.types.StringType;
-import org.openhab.core.library.types.UpDownType;
-import org.openhab.core.thing.Bridge;
-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.BaseThingHandler;
-import org.openhab.core.thing.binding.BridgeHandler;
-import org.openhab.core.types.Command;
-import org.openhab.core.types.RefreshType;
-import org.openhab.core.types.UnDefType;
-
-/**
- * The {@link MyQGarageDoorHandler} is responsible for handling commands for a garage door thing, which are
- * sent to one of the channels.
- *
- * @author Dan Cunningham - Initial contribution
- */
-@NonNullByDefault
-public class MyQGarageDoorHandler extends BaseThingHandler implements MyQDeviceHandler {
-    private @Nullable DeviceDTO device;
-    private String serialNumber;
-
-    public MyQGarageDoorHandler(Thing thing) {
-        super(thing);
-        serialNumber = getConfigAs(MyQDeviceConfiguration.class).serialNumber;
-    }
-
-    @Override
-    public void initialize() {
-        updateStatus(ThingStatus.UNKNOWN);
-    }
-
-    @Override
-    public void handleCommand(ChannelUID channelUID, Command command) {
-        if (command instanceof RefreshType) {
-            updateState();
-            return;
-        }
-        Bridge bridge = getBridge();
-        final DeviceDTO localDevice = device;
-        if (bridge != null && localDevice != null) {
-            BridgeHandler handler = bridge.getHandler();
-            if (handler != null) {
-                String cmd = null;
-                if (command instanceof OnOffType) {
-                    cmd = command == OnOffType.ON ? "open" : "close";
-                }
-                if (command instanceof UpDownType) {
-                    cmd = command == UpDownType.UP ? "open" : "close";
-                }
-                if (command instanceof PercentType percentage) {
-                    cmd = percentage.as(UpDownType.class) == UpDownType.UP ? "open" : "close";
-                }
-                if (command instanceof StringType) {
-                    cmd = command.toString();
-                }
-                if (cmd != null) {
-                    ((MyQAccountHandler) handler).sendDoorAction(localDevice, cmd);
-                }
-            }
-        }
-    }
-
-    @Override
-    public String getSerialNumber() {
-        return serialNumber;
-    }
-
-    protected void updateState() {
-        final DeviceDTO localDevice = device;
-        if (localDevice != null) {
-            String doorState = localDevice.state.doorState;
-            updateState("status", new StringType(doorState));
-            switch (doorState) {
-                case "open":
-                case "opening":
-                case "closing":
-                case "stopped":
-                case "transition":
-                    updateState("switch", OnOffType.ON);
-                    updateState("rollershutter", UpDownType.UP);
-                    break;
-                case "closed":
-                    updateState("switch", OnOffType.OFF);
-                    updateState("rollershutter", UpDownType.DOWN);
-                    break;
-                default:
-                    updateState("switch", UnDefType.UNDEF);
-                    updateState("rollershutter", UnDefType.UNDEF);
-                    break;
-            }
-            updateState("closeerror", localDevice.state.isUnattendedCloseAllowed ? OnOffType.OFF : OnOffType.ON);
-            updateState("openerror", localDevice.state.isUnattendedOpenAllowed ? OnOffType.OFF : OnOffType.ON);
-        }
-    }
-
-    @Override
-    public void handleDeviceUpdate(DeviceDTO device) {
-        if (!MyQBindingConstants.THING_TYPE_GARAGEDOOR.getId().equals(device.deviceFamily)) {
-            return;
-        }
-        this.device = device;
-        if (device.state.online) {
-            updateStatus(ThingStatus.ONLINE);
-            updateState();
-        } else {
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Device reports as offline");
-        }
-    }
-}
diff --git a/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQLampHandler.java b/bundles/org.openhab.binding.myq/src/main/java/org/openhab/binding/myq/internal/handler/MyQLampHandler.java
deleted file mode 100644 (file)
index 04cb77b..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/**
- * Copyright (c) 2010-2023 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.myq.internal.handler;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.myq.internal.MyQBindingConstants;
-import org.openhab.binding.myq.internal.config.MyQDeviceConfiguration;
-import org.openhab.binding.myq.internal.dto.DeviceDTO;
-import org.openhab.core.library.types.OnOffType;
-import org.openhab.core.thing.Bridge;
-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.BaseThingHandler;
-import org.openhab.core.thing.binding.BridgeHandler;
-import org.openhab.core.types.Command;
-import org.openhab.core.types.RefreshType;
-
-/**
- * The {@link MyQLampHandler} is responsible for handling commands for a lamp thing, which are
- * sent to one of the channels.
- *
- * @author Dan Cunningham - Initial contribution
- */
-@NonNullByDefault
-public class MyQLampHandler extends BaseThingHandler implements MyQDeviceHandler {
-    private @Nullable DeviceDTO device;
-    private String serialNumber;
-
-    public MyQLampHandler(Thing thing) {
-        super(thing);
-        serialNumber = getConfigAs(MyQDeviceConfiguration.class).serialNumber;
-    }
-
-    @Override
-    public void initialize() {
-        updateStatus(ThingStatus.UNKNOWN);
-    }
-
-    @Override
-    public void handleCommand(ChannelUID channelUID, Command command) {
-        if (command instanceof RefreshType) {
-            updateState();
-            return;
-        }
-
-        if (command instanceof OnOffType) {
-            Bridge bridge = getBridge();
-            final DeviceDTO localDevice = device;
-            if (bridge != null && localDevice != null) {
-                BridgeHandler handler = bridge.getHandler();
-                if (handler != null) {
-                    ((MyQAccountHandler) handler).sendLampAction(localDevice, command == OnOffType.ON ? "on" : "off");
-                }
-            }
-        }
-    }
-
-    @Override
-    public String getSerialNumber() {
-        return serialNumber;
-    }
-
-    protected void updateState() {
-        final DeviceDTO localDevice = device;
-        if (localDevice != null) {
-            String lampState = localDevice.state.lampState;
-            updateState("switch", "on".equals(lampState) ? OnOffType.ON : OnOffType.OFF);
-        }
-    }
-
-    @Override
-    public void handleDeviceUpdate(DeviceDTO device) {
-        if (!MyQBindingConstants.THING_TYPE_LAMP.getId().equals(device.deviceFamily)) {
-            return;
-        }
-        this.device = device;
-        if (device.state.online) {
-            updateStatus(ThingStatus.ONLINE);
-            updateState();
-        } else {
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Device reports as offline");
-        }
-    }
-}
diff --git a/bundles/org.openhab.binding.myq/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.myq/src/main/resources/OH-INF/addon/addon.xml
deleted file mode 100644 (file)
index a63b7da..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<addon:addon id="myq" 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>MyQ Binding</name>
-       <description>The MyQ binding allows monitoring and control of garage doors that are MyQ enabled.</description>
-       <connection>cloud</connection>
-
-</addon:addon>
diff --git a/bundles/org.openhab.binding.myq/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.myq/src/main/resources/OH-INF/config/config.xml
deleted file mode 100644 (file)
index beaf772..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-<config-description:config-descriptions
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
-       xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
-
-       <config-description uri="thing-type:myq:account">
-               <parameter name="username" type="text" required="true">
-                       <label>User Name</label>
-                       <description>Account username</description>
-               </parameter>
-               <parameter name="password" type="text" required="true">
-                       <label>password</label>
-                       <description>Account password</description>
-                       <context>password</context>
-               </parameter>
-               <parameter name="refreshInterval" type="integer" min="30" required="true" unit="s">
-                       <label>Refresh Interval</label>
-                       <description>Specifies the refresh interval in seconds</description>
-                       <default>60</default>
-               </parameter>
-       </config-description>
-
-       <config-description uri="thing-type:myq:garagedoor">
-               <parameter name="serialNumber" type="text" required="true">
-                       <label>Serial Number</label>
-                       <description>Serial number of the garage door</description>
-               </parameter>
-       </config-description>
-
-       <config-description uri="thing-type:myq:lamp">
-               <parameter name="serialNumber" type="text" required="true">
-                       <label>Serial Number</label>
-                       <description>Serial number of the lamp</description>
-               </parameter>
-       </config-description>
-
-</config-description:config-descriptions>
diff --git a/bundles/org.openhab.binding.myq/src/main/resources/OH-INF/i18n/myq.properties b/bundles/org.openhab.binding.myq/src/main/resources/OH-INF/i18n/myq.properties
deleted file mode 100644 (file)
index 7fafc35..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-# add-on
-
-addon.myq.name = MyQ Binding
-addon.myq.description = The MyQ binding allows monitoring and control of garage doors that are MyQ enabled.
-
-# thing types
-
-thing-type.myq.account.label = MyQ Account
-thing-type.myq.account.description = MyQ Cloud Account
-thing-type.myq.garagedoor.label = MyQ Garage Door
-thing-type.myq.garagedoor.description = MyQ Garage Door
-thing-type.myq.lamp.label = MyQ Lamp
-thing-type.myq.lamp.description = MyQ Lamp
-
-# thing types config
-
-thing-type.config.myq.account.password.label = password
-thing-type.config.myq.account.password.description = Account password
-thing-type.config.myq.account.refreshInterval.label = Refresh Interval
-thing-type.config.myq.account.refreshInterval.description = Specifies the refresh interval in seconds
-thing-type.config.myq.account.username.label = User Name
-thing-type.config.myq.account.username.description = Account username
-thing-type.config.myq.garagedoor.serialNumber.label = Serial Number
-thing-type.config.myq.garagedoor.serialNumber.description = Serial number of the garage door
-thing-type.config.myq.lamp.serialNumber.label = Serial Number
-thing-type.config.myq.lamp.serialNumber.description = Serial number of the lamp
-
-# channel types
-
-channel-type.myq.doorcloseerror.label = Garage Door Close Error
-channel-type.myq.dooropenerror.label = Garage Door Open Error
-channel-type.myq.doorrollershutter.label = Garage Door Rollershutter
-channel-type.myq.doorstatus.label = Garage Door Status
-channel-type.myq.doorstatus.state.option.open = Open
-channel-type.myq.doorstatus.state.option.opening = Opening
-channel-type.myq.doorstatus.state.option.closed = Closed
-channel-type.myq.doorstatus.state.option.closing = Closing
-channel-type.myq.doorstatus.state.option.stopped = Stopped
-channel-type.myq.doorstatus.state.option.transition = Transitioning
-channel-type.myq.doorstatus.state.option.unknown = Unknown
-channel-type.myq.doorswitch.label = Garage Door Switch
-channel-type.myq.lampswitch.label = Lamp Switch
diff --git a/bundles/org.openhab.binding.myq/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.myq/src/main/resources/OH-INF/thing/thing-types.xml
deleted file mode 100644 (file)
index 56fb3cb..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="myq" 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">
-
-       <bridge-type id="account">
-               <label>MyQ Account</label>
-               <description>MyQ Cloud Account</description>
-               <config-description-ref uri="thing-type:myq:account"/>
-       </bridge-type>
-
-       <thing-type id="garagedoor">
-               <supported-bridge-type-refs>
-                       <bridge-type-ref id="account"/>
-               </supported-bridge-type-refs>
-               <label>MyQ Garage Door</label>
-               <description>MyQ Garage Door</description>
-               <channels>
-                       <channel id="status" typeId="doorstatus"/>
-                       <channel id="switch" typeId="doorswitch"/>
-                       <channel id="rollershutter" typeId="doorrollershutter"/>
-                       <channel id="closeerror" typeId="doorcloseerror"/>
-                       <channel id="openerror" typeId="dooropenerror"/>
-               </channels>
-               <representation-property>serialNumber</representation-property>
-               <config-description-ref uri="thing-type:myq:garagedoor"/>
-       </thing-type>
-
-       <thing-type id="lamp">
-               <supported-bridge-type-refs>
-                       <bridge-type-ref id="account"/>
-               </supported-bridge-type-refs>
-               <label>MyQ Lamp</label>
-               <description>MyQ Lamp</description>
-               <channels>
-                       <channel id="switch" typeId="lampswitch"/>
-               </channels>
-               <representation-property>serialNumber</representation-property>
-               <config-description-ref uri="thing-type:myq:lamp"/>
-       </thing-type>
-
-       <channel-type id="doorstatus">
-               <item-type>String</item-type>
-               <label>Garage Door Status</label>
-               <state readOnly="true">
-                       <options>
-                               <option value="open">Open</option>
-                               <option value="opening">Opening</option>
-                               <option value="closed">Closed</option>
-                               <option value="closing">Closing</option>
-                               <option value="stopped">Stopped</option>
-                               <option value="transition">Transitioning</option>
-                               <option value="unknown">Unknown</option>
-                       </options>
-               </state>
-       </channel-type>
-       <channel-type id="doorswitch">
-               <item-type>Switch</item-type>
-               <label>Garage Door Switch</label>
-       </channel-type>
-       <channel-type id="doorrollershutter">
-               <item-type>Rollershutter</item-type>
-               <label>Garage Door Rollershutter</label>
-       </channel-type>
-       <channel-type id="doorcloseerror">
-               <item-type>Switch</item-type>
-               <label>Garage Door Close Error</label>
-               <state readOnly="true"/>
-       </channel-type>
-       <channel-type id="dooropenerror">
-               <item-type>Switch</item-type>
-               <label>Garage Door Open Error</label>
-               <state readOnly="true"/>
-       </channel-type>
-       <channel-type id="lampswitch">
-               <item-type>Switch</item-type>
-               <label>Lamp Switch</label>
-       </channel-type>
-</thing:thing-descriptions>
index 24ba021ef0c0a69530eddd1a8a76a56086dca57b..bc0ef858994b9ab871d54239e3f51633db860866 100644 (file)
     <module>org.openhab.binding.mybmw</module>
     <module>org.openhab.binding.mycroft</module>
     <module>org.openhab.binding.mynice</module>
-    <module>org.openhab.binding.myq</module>
     <module>org.openhab.binding.mystrom</module>
     <module>org.openhab.binding.nanoleaf</module>
     <module>org.openhab.binding.neato</module>