]> git.basschouten.com Git - openhab-addons.git/commitdiff
[twitter] rename binding to X (#15809)
authorlsiepel <leosiepel@gmail.com>
Wed, 15 Nov 2023 08:12:24 +0000 (09:12 +0100)
committerGitHub <noreply@github.com>
Wed, 15 Nov 2023 08:12:24 +0000 (09:12 +0100)
Signed-off-by: Leo Siepel <leosiepel@gmail.com>
29 files changed:
CODEOWNERS
bom/openhab-addons/pom.xml
bundles/org.openhab.binding.twitter/NOTICE [deleted file]
bundles/org.openhab.binding.twitter/README.md [deleted file]
bundles/org.openhab.binding.twitter/pom.xml [deleted file]
bundles/org.openhab.binding.twitter/src/main/feature/feature.xml [deleted file]
bundles/org.openhab.binding.twitter/src/main/java/org/openhab/binding/twitter/internal/TwitterBindingConstants.java [deleted file]
bundles/org.openhab.binding.twitter/src/main/java/org/openhab/binding/twitter/internal/TwitterHandler.java [deleted file]
bundles/org.openhab.binding.twitter/src/main/java/org/openhab/binding/twitter/internal/TwitterHandlerFactory.java [deleted file]
bundles/org.openhab.binding.twitter/src/main/java/org/openhab/binding/twitter/internal/action/TwitterActions.java [deleted file]
bundles/org.openhab.binding.twitter/src/main/java/org/openhab/binding/twitter/internal/config/TwitterConfig.java [deleted file]
bundles/org.openhab.binding.twitter/src/main/resources/OH-INF/addon/addon.xml [deleted file]
bundles/org.openhab.binding.twitter/src/main/resources/OH-INF/i18n/twitter.properties [deleted file]
bundles/org.openhab.binding.twitter/src/main/resources/OH-INF/i18n/twitter_fr.properties [deleted file]
bundles/org.openhab.binding.twitter/src/main/resources/OH-INF/i18n/twitter_ru.properties [deleted file]
bundles/org.openhab.binding.twitter/src/main/resources/OH-INF/thing/thing-types.xml [deleted file]
bundles/org.openhab.binding.x/NOTICE [new file with mode: 0644]
bundles/org.openhab.binding.x/README.md [new file with mode: 0644]
bundles/org.openhab.binding.x/pom.xml [new file with mode: 0644]
bundles/org.openhab.binding.x/src/main/feature/feature.xml [new file with mode: 0644]
bundles/org.openhab.binding.x/src/main/java/org/openhab/binding/x/internal/XBindingConstants.java [new file with mode: 0644]
bundles/org.openhab.binding.x/src/main/java/org/openhab/binding/x/internal/XHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.x/src/main/java/org/openhab/binding/x/internal/XHandlerFactory.java [new file with mode: 0644]
bundles/org.openhab.binding.x/src/main/java/org/openhab/binding/x/internal/action/XActions.java [new file with mode: 0644]
bundles/org.openhab.binding.x/src/main/java/org/openhab/binding/x/internal/config/XConfig.java [new file with mode: 0644]
bundles/org.openhab.binding.x/src/main/resources/OH-INF/addon/addon.xml [new file with mode: 0644]
bundles/org.openhab.binding.x/src/main/resources/OH-INF/i18n/x.properties [new file with mode: 0644]
bundles/org.openhab.binding.x/src/main/resources/OH-INF/thing/thing-types.xml [new file with mode: 0644]
bundles/pom.xml

index 6d8ddc9c790abbc1341a9f20b667fa91add55981..4b4e4a859c7908eac1bc9a1170cea6727eaee586 100644 (file)
 /bundles/org.openhab.binding.tplinksmarthome/ @Hilbrand
 /bundles/org.openhab.binding.tr064/ @J-N-K
 /bundles/org.openhab.binding.tradfri/ @cweitkamp @kaikreuzer
-/bundles/org.openhab.binding.twitter/ @computergeek1507
 /bundles/org.openhab.binding.unifi/ @mgbowman @Hilbrand
 /bundles/org.openhab.binding.unifiedremote/ @GiviMAD
 /bundles/org.openhab.binding.upb/ @marcusb
 /bundles/org.openhab.binding.wled/ @Skinah
 /bundles/org.openhab.binding.wolfsmartset/ @BoBiene
 /bundles/org.openhab.binding.wundergroundupdatereceiver/ @danieldemus
+/bundles/org.openhab.binding.x/ @computergeek1507
 /bundles/org.openhab.binding.xmltv/ @clinique
 /bundles/org.openhab.binding.xmppclient/ @pavel-gololobov
 /bundles/org.openhab.binding.yamahamusiccast/ @coop-git
index 9f322e518bc05dd0930dd2e2657582b8bd571997..bfee3215ad361a62f4a360596bf5a5aee8195710 100644 (file)
       <artifactId>org.openhab.binding.tradfri</artifactId>
       <version>${project.version}</version>
     </dependency>
-    <dependency>
-      <groupId>org.openhab.addons.bundles</groupId>
-      <artifactId>org.openhab.binding.twitter</artifactId>
-      <version>${project.version}</version>
-    </dependency>
     <dependency>
       <groupId>org.openhab.addons.bundles</groupId>
       <artifactId>org.openhab.binding.unifi</artifactId>
       <artifactId>org.openhab.binding.wundergroundupdatereceiver</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.openhab.addons.bundles</groupId>
+      <artifactId>org.openhab.binding.x</artifactId>
+      <version>${project.version}</version>
+    </dependency>
     <dependency>
       <groupId>org.openhab.addons.bundles</groupId>
       <artifactId>org.openhab.binding.xmltv</artifactId>
diff --git a/bundles/org.openhab.binding.twitter/NOTICE b/bundles/org.openhab.binding.twitter/NOTICE
deleted file mode 100644 (file)
index 9b243a5..0000000
+++ /dev/null
@@ -1,22 +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
-
-twitter4j
-* License: Apache License 2.0
-* Project: https://twitter4j.org/
-* Source:  https://github.com/Twitter4J/Twitter4J
-
-
diff --git a/bundles/org.openhab.binding.twitter/README.md b/bundles/org.openhab.binding.twitter/README.md
deleted file mode 100644 (file)
index b0ed2e6..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-# Twitter Binding
-
-The Twitter binding allows your home to Tweet 280 characters at a time. It also supports direct messages and tweeting with media.
-
-## Supported Things
-
-```text
-account - Twitter Account.
-```
-
-## Thing Configuration
-
-The Twitter Account Thing requires you to create a Twitter App in the Twitter Developer Page.
-
-|   Property        | Default | Required | Description                       |
-|-------------------|---------|:--------:|-----------------------------------|
-| consumerKey       |         |   Yes    | Consumer API Key                  |
-| consumerSecret    |         |   Yes    | Consumer API Secret               |
-| accessToken       |         |   Yes    | Access Token                      |
-| accessTokenSecret |         |   Yes    | Access Token Secret               |
-| refresh           | 30      |   No     | Tweet refresh interval in minutes |
-
-## Channels
-
-| channel    | type   | description                                    |
-|------------|--------|------------------------------------------------|
-| lasttweet  | String | This channel provides the Latest Tweet message |
-
-## Full Example
-
-twitter.things:
-
-```java
-Thing twitter:account:sampleaccount [ consumerKey="11111", consumerSecret="22222", accessToken="33333", accessTokenSecret="444444" ]
-
-```
-
-twitter.items:
-
-```java
-String sample_tweet   "Latest Tweet: [%s]" { channel="twitter:account:sampleaccount:lasttweet" }
-
-```
-
-## Rule Action
-
-This binding includes rule actions for sending tweets and direct messages.
-
-- `boolean success = sendTweet(String text)`
-- `boolean success = sendTweetWithAttachment(String text, String URL)`
-- `boolean success = sendDirectMessage(String recipientID, String text)`
-
-Examples:
-
-```java
-val tweetActions = getActions("twitter","twitter:account:sampleaccount")
-val success  = tweetActions.sendTweet("This is A Tweet")
-val success2 = tweetActions.sendTweetWithAttachment("This is A Tweet with a Pic", file:///tmp/201601011031.jpg)
-val success3 = tweetActions.sendTweetWithAttachment("Windows Picture", "D:\\Test.png" )
-val success4 = tweetActions.sendTweetWithAttachment("HTTP Picture", "http://www.mywebsite.com/Test.png" )
-val success5 = tweetActions.sendDirectMessage("1234567", "Wake Up" )
-
-```
diff --git a/bundles/org.openhab.binding.twitter/pom.xml b/bundles/org.openhab.binding.twitter/pom.xml
deleted file mode 100644 (file)
index 00e42c8..0000000
+++ /dev/null
@@ -1,29 +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.twitter</artifactId>
-
-  <name>openHAB Add-ons :: Bundles :: Twitter Binding</name>
-
-  <properties>
-    <bnd.importpackage>!android.*,!com.android.org.*,!dalvik.*,!javax.annotation.meta.*,!org.apache.harmony.*,!org.conscrypt.*,!sun.*,!com.google.appengine.api.*</bnd.importpackage>
-  </properties>
-
-  <dependencies>
-    <dependency>
-      <groupId>org.twitter4j</groupId>
-      <artifactId>twitter4j-core</artifactId>
-      <version>4.0.7</version>
-      <scope>compile</scope>
-    </dependency>
-  </dependencies>
-</project>
diff --git a/bundles/org.openhab.binding.twitter/src/main/feature/feature.xml b/bundles/org.openhab.binding.twitter/src/main/feature/feature.xml
deleted file mode 100644 (file)
index 86048f2..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<features name="org.openhab.binding.twitter-${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-twitter" description="Twitter Binding" version="${project.version}">
-               <feature>openhab-runtime-base</feature>
-               <bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.twitter/${project.version}</bundle>
-       </feature>
-</features>
diff --git a/bundles/org.openhab.binding.twitter/src/main/java/org/openhab/binding/twitter/internal/TwitterBindingConstants.java b/bundles/org.openhab.binding.twitter/src/main/java/org/openhab/binding/twitter/internal/TwitterBindingConstants.java
deleted file mode 100644 (file)
index c42baae..0000000
+++ /dev/null
@@ -1,34 +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.twitter.internal;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.core.thing.ThingTypeUID;
-
-/**
- * The {@link TwitterBindingConstants} class defines common constants, which are
- * used across the whole binding.
- *
- * @author Scott Hanson - Initial contribution
- */
-@NonNullByDefault
-public class TwitterBindingConstants {
-
-    private static final String BINDING_ID = "twitter";
-
-    // List of all Thing Type UIDs
-    public static final ThingTypeUID THING_TYPE_ACCOUNT = new ThingTypeUID(BINDING_ID, "account");
-
-    // List of all Channel ids
-    public static final String CHANNEL_LASTTWEET = "lasttweet";
-}
diff --git a/bundles/org.openhab.binding.twitter/src/main/java/org/openhab/binding/twitter/internal/TwitterHandler.java b/bundles/org.openhab.binding.twitter/src/main/java/org/openhab/binding/twitter/internal/TwitterHandler.java
deleted file mode 100644 (file)
index 8b962d7..0000000
+++ /dev/null
@@ -1,333 +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.twitter.internal;
-
-import static org.openhab.binding.twitter.internal.TwitterBindingConstants.CHANNEL_LASTTWEET;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Collection;
-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.twitter.internal.action.TwitterActions;
-import org.openhab.binding.twitter.internal.config.TwitterConfig;
-import org.openhab.core.io.net.http.HttpUtil;
-import org.openhab.core.library.types.RawType;
-import org.openhab.core.library.types.StringType;
-import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.binding.BaseThingHandler;
-import org.openhab.core.thing.binding.ThingHandlerService;
-import org.openhab.core.types.Command;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import twitter4j.DirectMessage;
-import twitter4j.ResponseList;
-import twitter4j.Status;
-import twitter4j.StatusUpdate;
-import twitter4j.Twitter;
-import twitter4j.TwitterException;
-import twitter4j.TwitterFactory;
-import twitter4j.auth.AccessToken;
-
-/**
- * The {@link TwitterHandler} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Scott Hanson - Initial contribution
- */
-
-@NonNullByDefault
-public class TwitterHandler extends BaseThingHandler {
-
-    private final Logger logger = LoggerFactory.getLogger(TwitterHandler.class);
-
-    private TwitterConfig config = new TwitterConfig();
-
-    private @Nullable ScheduledFuture<?> refreshTask;
-
-    private static final int CHARACTER_LIMIT = 280;
-
-    private static @Nullable Twitter client = null;
-    boolean isProperlyConfigured = false;
-
-    public TwitterHandler(Thing thing) {
-        super(thing);
-    }
-
-    @Override
-    public void handleCommand(ChannelUID channelUID, Command command) {
-    }
-
-    // creates list of available Actions
-    @Override
-    public Collection<Class<? extends ThingHandlerService>> getServices() {
-        return List.of(TwitterActions.class);
-    }
-
-    @Override
-    public void initialize() {
-        config = getConfigAs(TwitterConfig.class);
-
-        // create a New Twitter Client
-        Twitter localClient = createClient();
-        client = localClient;
-        refresh();// Get latest status
-        isProperlyConfigured = true;
-        refreshTask = scheduler.scheduleWithFixedDelay(this::refresh, 0, config.refresh, TimeUnit.MINUTES);
-        updateStatus(ThingStatus.ONLINE);
-    }
-
-    @Override
-    public void dispose() {
-        ScheduledFuture<?> localRefreshTask = refreshTask;
-        if (localRefreshTask != null) {
-            localRefreshTask.cancel(true);
-        }
-    }
-
-    /**
-     * Internal method for Getting Twitter Status
-     *
-     */
-    private void refresh() {
-        try {
-            if (!checkPrerequisites()) {
-                return;
-            }
-            Twitter localClient = client;
-            if (localClient != null) {
-                ResponseList<Status> statuses = localClient.getUserTimeline();
-                if (!statuses.isEmpty()) {
-                    updateState(CHANNEL_LASTTWEET, StringType.valueOf(statuses.get(0).getText()));
-                } else {
-                    logger.debug("No Statuses Found");
-                }
-            }
-        } catch (TwitterException e) {
-            logger.debug("Error when trying to refresh Twitter Account: {}", e.getMessage());
-        }
-    }
-
-    /**
-     * Internal method for sending a tweet, with or without image
-     *
-     * @param tweetTxt
-     *            text string to be sent as a Tweet
-     * @param fileToAttach
-     *            the file to attach. May be null if no attached file.
-     *
-     * @return <code>true</code>, if sending the tweet has been successful and
-     *         <code>false</code> in all other cases.
-     */
-    private boolean sendTweet(final String tweetTxt, final @Nullable File fileToAttach) {
-        if (!checkPrerequisites()) {
-            return false;
-        }
-        // abbreviate the Tweet to meet the 280 character limit ...
-        String abbreviatedTweetTxt = abbreviateString(tweetTxt, CHARACTER_LIMIT);
-        try {
-            Twitter localClient = client;
-            if (localClient != null) {
-                // send the Tweet
-                StatusUpdate status = new StatusUpdate(abbreviatedTweetTxt);
-                if (fileToAttach != null && fileToAttach.isFile()) {
-                    status.setMedia(fileToAttach);
-                }
-                Status updatedStatus = localClient.updateStatus(status);
-                logger.debug("Successfully sent Tweet '{}'", updatedStatus.getText());
-                updateState(CHANNEL_LASTTWEET, StringType.valueOf(updatedStatus.getText()));
-                return true;
-            }
-        } catch (TwitterException e) {
-            logger.warn("Failed to send Tweet '{}' because of : {}", abbreviatedTweetTxt, e.getLocalizedMessage());
-        }
-        return false;
-    }
-
-    /**
-     * Sends a standard Tweet.
-     *
-     * @param tweetTxt
-     *            text string to be sent as a Tweet
-     *
-     * @return <code>true</code>, if sending the tweet has been successful and
-     *         <code>false</code> in all other cases.
-     */
-    public boolean sendTweet(String tweetTxt) {
-        if (!checkPrerequisites()) {
-            return false;
-        }
-        return sendTweet(tweetTxt, (File) null);
-    }
-
-    /**
-     * Sends a Tweet with an image
-     *
-     * @param tweetTxt
-     *            text string to be sent as a Tweet
-     * @param tweetPicture
-     *            the path of the picture that needs to be attached (either an url,
-     *            either a path pointing to a local file)
-     *
-     * @return <code>true</code>, if sending the tweet has been successful and
-     *         <code>false</code> in all other cases.
-     */
-    public boolean sendTweet(String tweetTxt, String tweetPicture) {
-        if (!checkPrerequisites()) {
-            return false;
-        }
-
-        // prepare the image attachment
-        File fileToAttach = null;
-        boolean deleteTemporaryFile = false;
-        if (tweetPicture.startsWith("http://") || tweetPicture.startsWith("https://")) {
-            try {
-                // we have a remote url and need to download the remote file to a temporary location
-                Path tDir = Files.createTempDirectory("TempDirectory");
-                String path = tDir + File.separator + "openhab-twitter-remote_attached_file" + "."
-                        + getExtension(tweetPicture);
-
-                // URL url = new URL(tweetPicture);
-                fileToAttach = new File(path);
-                deleteTemporaryFile = true;
-
-                RawType rawPicture = HttpUtil.downloadImage(tweetPicture);
-                if (rawPicture != null) {
-                    try (FileOutputStream fos = new FileOutputStream(path)) {
-                        fos.write(rawPicture.getBytes(), 0, rawPicture.getBytes().length);
-                    } catch (FileNotFoundException ex) {
-                        logger.debug("Could not create {} in temp dir. {}", path, ex.getMessage());
-                    } catch (IOException ex) {
-                        logger.debug("Could not write {} to temp dir. {}", path, ex.getMessage());
-                    }
-                } else {
-                    logger.debug("Could not download tweet file from {}", tweetPicture);
-                }
-            } catch (IOException ex) {
-                logger.debug("Could not write {} to temp dir. {}", tweetPicture, ex.getMessage());
-            }
-        } else {
-            // we have a local file and can just use it directly
-            fileToAttach = new File(tweetPicture);
-        }
-
-        if (fileToAttach != null && fileToAttach.isFile()) {
-            logger.debug("Image '{}' correctly found, will be included in tweet", tweetPicture);
-        } else {
-            logger.warn("Image '{}' not found, will only tweet text", tweetPicture);
-        }
-
-        // send the Tweet
-        boolean result = sendTweet(tweetTxt, fileToAttach);
-        // delete temp file (if needed)
-        if (deleteTemporaryFile) {
-            if (fileToAttach != null) {
-                try {
-                    fileToAttach.delete();
-                } catch (final Exception ignored) {
-                    return false;
-                }
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Sends a DirectMessage
-     *
-     * @param recipientId
-     *            recipient ID of the twitter user
-     * @param messageTxt
-     *            text string to be sent as a Direct Message
-     *
-     * @return <code>true</code>, if sending the direct message has been successful and
-     *         <code>false</code> in all other cases.
-     */
-    public boolean sendDirectMessage(String recipientId, String messageTxt) {
-        if (!checkPrerequisites()) {
-            return false;
-        }
-
-        try {
-            Twitter localClient = client;
-            if (localClient != null) {
-                // abbreviate the Tweet to meet the allowed character limit ...
-                String abbreviatedMessageTxt = abbreviateString(messageTxt, CHARACTER_LIMIT);
-                // send the direct message
-                DirectMessage message = localClient.sendDirectMessage(recipientId, abbreviatedMessageTxt);
-                logger.debug("Successfully sent direct message '{}' to @'{}'", message.getText(),
-                        message.getRecipientId());
-                return true;
-            }
-        } catch (TwitterException e) {
-            logger.warn("Failed to send Direct Message '{}' because of :'{}'", messageTxt, e.getLocalizedMessage());
-        }
-        return false;
-    }
-
-    /**
-     * check if twitter account was created with prerequisites
-     *
-     * @return <code>true</code>, if twitter account was initialized
-     *         <code>false</code> in all other cases.
-     */
-    private boolean checkPrerequisites() {
-        if (client == null) {
-            logger.debug("Twitter client is not yet configured > execution aborted!");
-            return false;
-        }
-        if (!isProperlyConfigured) {
-            logger.debug("Twitter client is not yet configured > execution aborted!");
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Creates and returns a Twitter4J Twitter client.
-     *
-     * @return a new instance of a Twitter4J Twitter client.
-     */
-    private twitter4j.Twitter createClient() {
-        twitter4j.Twitter client = TwitterFactory.getSingleton();
-        client.setOAuthConsumer(config.consumerKey, config.consumerSecret);
-        client.setOAuthAccessToken(new AccessToken(config.accessToken, config.accessTokenSecret));
-        return client;
-    }
-
-    public static String abbreviateString(String input, int maxLength) {
-        if (input.length() <= maxLength) {
-            return input;
-        } else {
-            return input.substring(0, maxLength);
-        }
-    }
-
-    public static String getExtension(String filename) {
-        if (filename.contains(".")) {
-            return filename.substring(filename.lastIndexOf(".") + 1);
-        }
-        return new String();
-    }
-}
diff --git a/bundles/org.openhab.binding.twitter/src/main/java/org/openhab/binding/twitter/internal/TwitterHandlerFactory.java b/bundles/org.openhab.binding.twitter/src/main/java/org/openhab/binding/twitter/internal/TwitterHandlerFactory.java
deleted file mode 100644 (file)
index d27f76f..0000000
+++ /dev/null
@@ -1,55 +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.twitter.internal;
-
-import static org.openhab.binding.twitter.internal.TwitterBindingConstants.THING_TYPE_ACCOUNT;
-
-import java.util.Set;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-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.Component;
-
-/**
- * The {@link TwitterHandlerFactory} is responsible for creating things and thing
- * handlers.
- *
- * @author Scott Hanson - Initial contribution
- */
-@NonNullByDefault
-@Component(configurationPid = "binding.twitter", service = ThingHandlerFactory.class)
-public class TwitterHandlerFactory extends BaseThingHandlerFactory {
-
-    private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_ACCOUNT);
-
-    @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 TwitterHandler(thing);
-        }
-
-        return null;
-    }
-}
diff --git a/bundles/org.openhab.binding.twitter/src/main/java/org/openhab/binding/twitter/internal/action/TwitterActions.java b/bundles/org.openhab.binding.twitter/src/main/java/org/openhab/binding/twitter/internal/action/TwitterActions.java
deleted file mode 100644 (file)
index 12a56c3..0000000
+++ /dev/null
@@ -1,124 +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.twitter.internal.action;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.twitter.internal.TwitterHandler;
-import org.openhab.core.automation.annotation.ActionInput;
-import org.openhab.core.automation.annotation.ActionOutput;
-import org.openhab.core.automation.annotation.RuleAction;
-import org.openhab.core.thing.binding.ThingActions;
-import org.openhab.core.thing.binding.ThingActionsScope;
-import org.openhab.core.thing.binding.ThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link TwitterActions} class defines rule actions for sending tweet
- *
- * @author Scott Hanson - Initial contribution
- */
-@ThingActionsScope(name = "twitter")
-@NonNullByDefault
-public class TwitterActions implements ThingActions {
-
-    private final Logger logger = LoggerFactory.getLogger(TwitterActions.class);
-
-    private @Nullable TwitterHandler handler;
-
-    @RuleAction(label = "@text/sendTweetActionLabel", description = "@text/sendTweetActionDescription")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendTweet(
-            @ActionInput(name = "text") @Nullable String text) {
-        if (text == null) {
-            logger.warn("Cannot send Tweet as text is missing.");
-            return false;
-        }
-
-        final TwitterHandler handler = this.handler;
-        if (handler == null) {
-            logger.debug("Handler is null, cannot tweet.");
-            return false;
-        } else {
-            return handler.sendTweet(text);
-        }
-    }
-
-    @RuleAction(label = "@text/sendAttachmentTweetActionLabel", description = "@text/sendAttachmentTweetActionDescription")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendTweetWithAttachment(
-            @ActionInput(name = "text") @Nullable String text, @ActionInput(name = "url") @Nullable String urlString) {
-        if (text == null) {
-            logger.warn("Cannot send Tweet as text is missing.");
-            return false;
-        }
-        if (urlString == null) {
-            logger.warn("Cannot send Tweet as urlString is missing.");
-            return false;
-        }
-
-        final TwitterHandler handler = this.handler;
-        if (handler == null) {
-            logger.debug("Handler is null, cannot tweet.");
-            return false;
-        } else {
-            return handler.sendTweet(text, urlString);
-        }
-    }
-
-    @RuleAction(label = "@text/sendDirectMessageActionLabel", description = "@text/sendDirectMessageActionDescription")
-    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendDirectMessage(
-            @ActionInput(name = "recipient") @Nullable String recipient,
-            @ActionInput(name = "text") @Nullable String text) {
-        if (recipient == null) {
-            logger.warn("Cannot send Direct Message as recipient is missing.");
-            return false;
-        }
-        if (text == null) {
-            logger.warn("Cannot send Direct Message as text is missing.");
-            return false;
-        }
-
-        final TwitterHandler handler = this.handler;
-        if (handler == null) {
-            logger.debug("Handler is null, cannot tweet.");
-            return false;
-        } else {
-            return handler.sendDirectMessage(recipient, text);
-        }
-    }
-
-    public static boolean sendTweet(ThingActions actions, @Nullable String text) {
-        return ((TwitterActions) actions).sendTweet(text);
-    }
-
-    public static boolean sendTweetWithAttachment(ThingActions actions, @Nullable String text,
-            @Nullable String urlString) {
-        return ((TwitterActions) actions).sendTweetWithAttachment(text, urlString);
-    }
-
-    public static boolean sendDirectMessage(ThingActions actions, @Nullable String recipient, @Nullable String text) {
-        return ((TwitterActions) actions).sendDirectMessage(recipient, text);
-    }
-
-    @Override
-    public void setThingHandler(@Nullable ThingHandler handler) {
-        if (handler instanceof TwitterHandler twitterHandler) {
-            this.handler = twitterHandler;
-        }
-    }
-
-    @Override
-    public @Nullable ThingHandler getThingHandler() {
-        return handler;
-    }
-}
diff --git a/bundles/org.openhab.binding.twitter/src/main/java/org/openhab/binding/twitter/internal/config/TwitterConfig.java b/bundles/org.openhab.binding.twitter/src/main/java/org/openhab/binding/twitter/internal/config/TwitterConfig.java
deleted file mode 100644 (file)
index 529bee3..0000000
+++ /dev/null
@@ -1,30 +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.twitter.internal.config;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * The {@link TwitterConfig} class contains fields mapping thing configuration parameters.
- *
- * @author Scott Hanson - Initial contribution
- */
-
-@NonNullByDefault
-public class TwitterConfig {
-    public String consumerKey = "";
-    public String consumerSecret = "";
-    public String accessToken = "";
-    public String accessTokenSecret = "";
-    public int refresh = 30;
-}
diff --git a/bundles/org.openhab.binding.twitter/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.twitter/src/main/resources/OH-INF/addon/addon.xml
deleted file mode 100644 (file)
index 8c90697..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<addon:addon id="twitter" 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>Twitter Binding</name>
-       <description>Supports adding Thing for getting the Last Tweet. Send Tweets and Pictures with Actions.</description>
-       <connection>cloud</connection>
-
-</addon:addon>
diff --git a/bundles/org.openhab.binding.twitter/src/main/resources/OH-INF/i18n/twitter.properties b/bundles/org.openhab.binding.twitter/src/main/resources/OH-INF/i18n/twitter.properties
deleted file mode 100644 (file)
index 161cccf..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-# add-on
-
-addon.twitter.name = Twitter Binding
-addon.twitter.description = Supports adding Thing for getting the Last Tweet. Send Tweets and Pictures with Actions.
-
-# thing types
-
-thing-type.twitter.account.label = Twitter Account
-thing-type.twitter.account.description = Account uses for sending Tweets
-
-# thing types config
-
-thing-type.config.twitter.account.accessToken.label = Access Token
-thing-type.config.twitter.account.accessTokenSecret.label = Access Token Secret
-thing-type.config.twitter.account.consumerKey.label = Consumer API Key
-thing-type.config.twitter.account.consumerSecret.label = Consumer API Secret
-thing-type.config.twitter.account.refresh.label = Refresh Time
-thing-type.config.twitter.account.refresh.description = Refresh Time for This Account in Mins
-
-# channel types
-
-channel-type.twitter.lasttweet.label = Last Tweet
-channel-type.twitter.lasttweet.description = Users Last Tweet
-
-# actions
-
-sendAttachmentTweetActionLabel = send a Tweet with attachment
-sendAttachmentTweetActionDescription = Sends a Tweet with an attachment.
-sendDirectMessageActionLabel = send a DirectMessage
-sendDirectMessageActionDescription = Sends a DirectMessage.
-sendTweetActionLabel = send a Tweet
-sendTweetActionDescription = Sends a Tweet.
diff --git a/bundles/org.openhab.binding.twitter/src/main/resources/OH-INF/i18n/twitter_fr.properties b/bundles/org.openhab.binding.twitter/src/main/resources/OH-INF/i18n/twitter_fr.properties
deleted file mode 100644 (file)
index 06d8269..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-# add-on
-
-addon.twitter.name = Extension Twitter
-addon.twitter.description = Cette extension permet d'obtenir le dernier Tweet ou d'envoyer des Tweets et des images depuis vos règles d'automatisation.
-
-# thing types
-
-thing-type.twitter.account.label = Compte Twitter
-thing-type.twitter.account.description = Compte pour envoyer des Tweets
-
-# thing types config
-
-thing-type.config.twitter.account.accessToken.label = Jeton d'authentification
-thing-type.config.twitter.account.accessTokenSecret.label = Code secret du jeton d'authentification
-thing-type.config.twitter.account.consumerKey.label = Clé d'accès à l'API
-thing-type.config.twitter.account.consumerSecret.label = Code secret pour l'accès à l'API
-thing-type.config.twitter.account.refresh.label = Fréquence de rafraîchissement
-thing-type.config.twitter.account.refresh.description = Fréquence de rafraîchissement de ce compte en minutes
-
-# channel types
-
-channel-type.twitter.lasttweet.label = Dernier Tweet
-channel-type.twitter.lasttweet.description = Dernier Tweet des utilisateurs
-
-# actions
-
-sendAttachmentTweetActionLabel = envoyer un Tweet avec pièce jointe
-sendAttachmentTweetActionDescription = Envoie un Tweet avec pièce jointe.
-sendDirectMessageActionLabel = envoyer un message privé
-sendDirectMessageActionDescription = Envoie un message privé.
-sendTweetActionLabel = envoyer un Tweet
-sendTweetActionDescription = Envoie un Tweet.
diff --git a/bundles/org.openhab.binding.twitter/src/main/resources/OH-INF/i18n/twitter_ru.properties b/bundles/org.openhab.binding.twitter/src/main/resources/OH-INF/i18n/twitter_ru.properties
deleted file mode 100644 (file)
index 6991945..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# actions
-sendTweetActionLabel = отправить твит
-sendTweetActionDescription = Отправляет твит.
-
-sendAttachmentTweetActionLabel = отправить твит с вложением
-sendAttachmentTweetActionDescription = Отправляет твит с вложением.
-
-sendDirectMessageActionLabel = отправить личное сообщение
-sendDirectMessageActionDescription = Отправляет личное сообщение.
diff --git a/bundles/org.openhab.binding.twitter/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.twitter/src/main/resources/OH-INF/thing/thing-types.xml
deleted file mode 100644 (file)
index 1ec1355..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="twitter"
-       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="account">
-               <label>Twitter Account</label>
-               <description>Account uses for sending Tweets</description>
-
-               <channels>
-                       <channel id="lasttweet" typeId="lasttweet"/>
-               </channels>
-
-               <config-description>
-                       <parameter name="consumerKey" type="text" required="true">
-                               <label>Consumer API Key</label>
-                               <context>password</context>
-                       </parameter>
-                       <parameter name="consumerSecret" type="text" required="true">
-                               <label>Consumer API Secret</label>
-                               <context>password</context>
-                       </parameter>
-                       <parameter name="accessToken" type="text" required="true">
-                               <label>Access Token</label>
-                               <context>password</context>
-                       </parameter>
-                       <parameter name="accessTokenSecret" type="text" required="true">
-                               <label>Access Token Secret</label>
-                               <context>password</context>
-                       </parameter>
-                       <parameter name="refresh" type="integer" required="false" unit="min" min="1">
-                               <label>Refresh Time</label>
-                               <description>Refresh Time for This Account in Mins</description>
-                               <default>30</default>
-                       </parameter>
-               </config-description>
-
-       </thing-type>
-
-       <channel-type id="lasttweet">
-               <item-type>String</item-type>
-               <label>Last Tweet</label>
-               <description>Users Last Tweet</description>
-               <state readOnly="true"/>
-       </channel-type>
-</thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.x/NOTICE b/bundles/org.openhab.binding.x/NOTICE
new file mode 100644 (file)
index 0000000..9b243a5
--- /dev/null
@@ -0,0 +1,22 @@
+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
+
+twitter4j
+* License: Apache License 2.0
+* Project: https://twitter4j.org/
+* Source:  https://github.com/Twitter4J/Twitter4J
+
+
diff --git a/bundles/org.openhab.binding.x/README.md b/bundles/org.openhab.binding.x/README.md
new file mode 100644 (file)
index 0000000..846ee86
--- /dev/null
@@ -0,0 +1,60 @@
+# X Binding
+
+The X (formerly known as Twitter) binding allows your home to post 280 characters at a time. It also supports direct messages and posting with media.
+
+## Supported Things
+
+```text
+account - X Account.
+```
+
+## Thing Configuration
+
+The X Account Thing requires you to create a X App in the X Developer Page.
+
+|   Property        | Default | Required | Description                       |
+|-------------------|---------|:--------:|-----------------------------------|
+| consumerKey       |         |   Yes    | Consumer API Key                  |
+| consumerSecret    |         |   Yes    | Consumer API Secret               |
+| accessToken       |         |   Yes    | Access Token                      |
+| accessTokenSecret |         |   Yes    | Access Token Secret               |
+| refresh           | 30      |   No     | Post refresh interval in minutes |
+
+## Channels
+
+| channel  | type   | description                                   |
+|----------|--------|-----------------------------------------------|
+| lastpost | String | This channel provides the Latest post message |
+
+## Full Example
+
+x.things:
+
+```java
+Thing x:account:sampleaccount [ consumerKey="11111", consumerSecret="22222", accessToken="33333", accessTokenSecret="444444" ]
+```
+
+x.items:
+
+```java
+String sample_post   "Latest post: [%s]" { channel="x:account:sampleaccount:lastpost" }
+```
+
+## Rule Action
+
+This binding includes rule actions for sending posts and direct messages.
+
+- `boolean success = sendPost(String text)`
+- `boolean success = sendPostWithAttachment(String text, String URL)`
+- `boolean success = sendDirectMessage(String recipientID, String text)`
+
+Examples:
+
+```java
+val postActions = getActions("x","x:account:sampleaccount")
+val success  = postActions.sendPost("This is A Post")
+val success2 = postActions.sendPostWithAttachment("This is A Post with a Pic", file:///tmp/201601011031.jpg)
+val success3 = postActions.sendPostWithAttachment("Windows Picture", "D:\\Test.png" )
+val success4 = postActions.sendPostWithAttachment("HTTP Picture", "http://www.mywebsite.com/Test.png" )
+val success5 = postActions.sendDirectMessage("1234567", "Wake Up" )
+```
diff --git a/bundles/org.openhab.binding.x/pom.xml b/bundles/org.openhab.binding.x/pom.xml
new file mode 100644 (file)
index 0000000..f7020d2
--- /dev/null
@@ -0,0 +1,29 @@
+<?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.x</artifactId>
+
+  <name>openHAB Add-ons :: Bundles :: X Binding</name>
+
+  <properties>
+    <bnd.importpackage>!org.slf4j.impl.*,!android.*,!com.android.org.*,!dalvik.*,!javax.annotation.meta.*,!org.apache.harmony.*,!org.conscrypt.*,!sun.*,!com.google.appengine.api.*</bnd.importpackage>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.twitter4j</groupId>
+      <artifactId>twitter4j-core</artifactId>
+      <version>4.1.2</version>
+      <scope>compile</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/bundles/org.openhab.binding.x/src/main/feature/feature.xml b/bundles/org.openhab.binding.x/src/main/feature/feature.xml
new file mode 100644 (file)
index 0000000..ffb9548
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<features name="org.openhab.binding.x-${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-x" description="X Binding" version="${project.version}">
+               <feature>openhab-runtime-base</feature>
+               <bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.x/${project.version}</bundle>
+       </feature>
+</features>
diff --git a/bundles/org.openhab.binding.x/src/main/java/org/openhab/binding/x/internal/XBindingConstants.java b/bundles/org.openhab.binding.x/src/main/java/org/openhab/binding/x/internal/XBindingConstants.java
new file mode 100644 (file)
index 0000000..d35f524
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * 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.x.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * The {@link XBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Scott Hanson - Initial contribution
+ */
+@NonNullByDefault
+public class XBindingConstants {
+
+    private static final String BINDING_ID = "x";
+
+    // List of all Thing Type UIDs
+    public static final ThingTypeUID THING_TYPE_ACCOUNT = new ThingTypeUID(BINDING_ID, "account");
+
+    // List of all Channel ids
+    public static final String CHANNEL_LASTPOST = "lastpost";
+}
diff --git a/bundles/org.openhab.binding.x/src/main/java/org/openhab/binding/x/internal/XHandler.java b/bundles/org.openhab.binding.x/src/main/java/org/openhab/binding/x/internal/XHandler.java
new file mode 100644 (file)
index 0000000..b262840
--- /dev/null
@@ -0,0 +1,332 @@
+/**
+ * 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.x.internal;
+
+import static org.openhab.binding.x.internal.XBindingConstants.CHANNEL_LASTPOST;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+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.x.internal.action.XActions;
+import org.openhab.binding.x.internal.config.XConfig;
+import org.openhab.core.io.net.http.HttpUtil;
+import org.openhab.core.library.types.RawType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.openhab.core.types.Command;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import twitter4j.Twitter;
+import twitter4j.TwitterException;
+import twitter4j.v1.DirectMessage;
+import twitter4j.v1.ResponseList;
+import twitter4j.v1.Status;
+import twitter4j.v1.StatusUpdate;
+
+/**
+ * The {@link XHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Scott Hanson - Initial contribution
+ */
+
+@NonNullByDefault
+public class XHandler extends BaseThingHandler {
+
+    private final Logger logger = LoggerFactory.getLogger(XHandler.class);
+
+    private XConfig config = new XConfig();
+
+    private @Nullable ScheduledFuture<?> refreshTask;
+
+    private static final int CHARACTER_LIMIT = 280;
+
+    private static @Nullable Twitter client = null;
+    boolean isProperlyConfigured = false;
+
+    public XHandler(Thing thing) {
+        super(thing);
+    }
+
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+    }
+
+    // creates list of available Actions
+    @Override
+    public Collection<Class<? extends ThingHandlerService>> getServices() {
+        return List.of(XActions.class);
+    }
+
+    @Override
+    public void initialize() {
+        config = getConfigAs(XConfig.class);
+
+        // create a New X/Twitter Client
+        Twitter localClient = createClient();
+        client = localClient;
+        refresh();// Get latest status
+        isProperlyConfigured = true;
+        refreshTask = scheduler.scheduleWithFixedDelay(this::refresh, 0, config.refresh, TimeUnit.MINUTES);
+        updateStatus(ThingStatus.ONLINE);
+    }
+
+    @Override
+    public void dispose() {
+        ScheduledFuture<?> localRefreshTask = refreshTask;
+        if (localRefreshTask != null) {
+            localRefreshTask.cancel(true);
+        }
+    }
+
+    /**
+     * Internal method for Getting X Status
+     *
+     */
+    private void refresh() {
+        try {
+            if (!checkPrerequisites()) {
+                return;
+            }
+            Twitter localClient = client;
+            if (localClient != null) {
+                ResponseList<Status> statuses = localClient.v1().timelines().getUserTimeline();
+                if (!statuses.isEmpty()) {
+                    updateState(CHANNEL_LASTPOST, StringType.valueOf(statuses.get(0).getText()));
+                } else {
+                    logger.debug("No Statuses Found");
+                }
+            }
+        } catch (TwitterException e) {
+            logger.debug("Error when trying to refresh X Account: {}", e.getMessage());
+        }
+    }
+
+    /**
+     * Internal method for sending a post, with or without image
+     *
+     * @param postTxt
+     *            text string to be sent as a Post
+     * @param fileToAttach
+     *            the file to attach. May be null if no attached file.
+     *
+     * @return <code>true</code>, if sending the post has been successful and
+     *         <code>false</code> in all other cases.
+     */
+    private boolean sendPost(final String postTxt, final @Nullable File fileToAttach) {
+        if (!checkPrerequisites()) {
+            return false;
+        }
+        // abbreviate the Post to meet the 280 character limit ...
+        String abbreviatedPostTxt = abbreviateString(postTxt, CHARACTER_LIMIT);
+        try {
+            Twitter localClient = client;
+            if (localClient != null) {
+                // send the Post
+                StatusUpdate status = StatusUpdate.of(abbreviatedPostTxt);
+                if (fileToAttach != null && fileToAttach.isFile()) {
+                    status = status.media(fileToAttach);
+                }
+                Status updatedStatus = localClient.v1().tweets().updateStatus(status);
+                logger.debug("Successfully sent Post '{}'", updatedStatus.getText());
+                updateState(CHANNEL_LASTPOST, StringType.valueOf(updatedStatus.getText()));
+                return true;
+            }
+        } catch (TwitterException e) {
+            logger.warn("Failed to send Post '{}' because of : {}", abbreviatedPostTxt, e.getLocalizedMessage());
+        }
+        return false;
+    }
+
+    /**
+     * Sends a standard Post.
+     *
+     * @param postTxt
+     *            text string to be sent as a Post
+     *
+     * @return <code>true</code>, if sending the post has been successful and
+     *         <code>false</code> in all other cases.
+     */
+    public boolean sendPost(String postTxt) {
+        if (!checkPrerequisites()) {
+            return false;
+        }
+        return sendPost(postTxt, (File) null);
+    }
+
+    /**
+     * Sends a Post with an image
+     *
+     * @param postTxt
+     *            text string to be sent as a Post
+     * @param postPicture
+     *            the path of the picture that needs to be attached (either an url,
+     *            either a path pointing to a local file)
+     *
+     * @return <code>true</code>, if sending the post has been successful and
+     *         <code>false</code> in all other cases.
+     */
+    public boolean sendPost(String postTxt, String postPicture) {
+        if (!checkPrerequisites()) {
+            return false;
+        }
+
+        // prepare the image attachment
+        File fileToAttach = null;
+        boolean deleteTemporaryFile = false;
+        if (postPicture.startsWith("http://") || postPicture.startsWith("https://")) {
+            try {
+                // we have a remote url and need to download the remote file to a temporary location
+                Path tDir = Files.createTempDirectory("TempDirectory");
+                String path = tDir + File.separator + "openhab-x-remote_attached_file" + "."
+                        + getExtension(postPicture);
+
+                // URL url = new URL(postPicture);
+                fileToAttach = new File(path);
+                deleteTemporaryFile = true;
+
+                RawType rawPicture = HttpUtil.downloadImage(postPicture);
+                if (rawPicture != null) {
+                    try (FileOutputStream fos = new FileOutputStream(path)) {
+                        fos.write(rawPicture.getBytes(), 0, rawPicture.getBytes().length);
+                    } catch (FileNotFoundException ex) {
+                        logger.debug("Could not create {} in temp dir. {}", path, ex.getMessage());
+                    } catch (IOException ex) {
+                        logger.debug("Could not write {} to temp dir. {}", path, ex.getMessage());
+                    }
+                } else {
+                    logger.debug("Could not download post file from {}", postPicture);
+                }
+            } catch (IOException ex) {
+                logger.debug("Could not write {} to temp dir. {}", postPicture, ex.getMessage());
+            }
+        } else {
+            // we have a local file and can just use it directly
+            fileToAttach = new File(postPicture);
+        }
+
+        if (fileToAttach != null && fileToAttach.isFile()) {
+            logger.debug("Image '{}' correctly found, will be included in post", postPicture);
+        } else {
+            logger.warn("Image '{}' not found, will only post text", postPicture);
+        }
+
+        // send the Post
+        boolean result = sendPost(postTxt, fileToAttach);
+        // delete temp file (if needed)
+        if (deleteTemporaryFile) {
+            if (fileToAttach != null) {
+                try {
+                    fileToAttach.delete();
+                } catch (final Exception ignored) {
+                    return false;
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Sends a DirectMessage
+     *
+     * @param recipientId
+     *            recipient ID of the twitter user
+     * @param messageTxt
+     *            text string to be sent as a Direct Message
+     *
+     * @return <code>true</code>, if sending the direct message has been successful and
+     *         <code>false</code> in all other cases.
+     */
+    public boolean sendDirectMessage(String recipientId, String messageTxt) {
+        if (!checkPrerequisites()) {
+            return false;
+        }
+
+        try {
+            Twitter localClient = client;
+            if (localClient != null) {
+                // abbreviate the Post to meet the allowed character limit ...
+                String abbreviatedMessageTxt = abbreviateString(messageTxt, CHARACTER_LIMIT);
+                // send the direct message
+                DirectMessage message = localClient.v1().directMessages().sendDirectMessage(recipientId,
+                        abbreviatedMessageTxt);
+                logger.debug("Successfully sent direct message '{}' to @'{}'", message.getText(),
+                        message.getRecipientId());
+                return true;
+            }
+        } catch (TwitterException e) {
+            logger.warn("Failed to send Direct Message '{}' because of :'{}'", messageTxt, e.getLocalizedMessage());
+        }
+        return false;
+    }
+
+    /**
+     * check if X account was created with prerequisites
+     * 
+     * @return <code>true</code>, if X account was initialized
+     *         <code>false</code> in all other cases.
+     */
+    private boolean checkPrerequisites() {
+        if (client == null) {
+            logger.debug("X client is not yet configured > execution aborted!");
+            return false;
+        }
+        if (!isProperlyConfigured) {
+            logger.debug("X client is not yet configured > execution aborted!");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Creates and returns a Twitter4J Twitter client.
+     *
+     * @return a new instance of a Twitter4J Twitter client.
+     */
+    private twitter4j.Twitter createClient() {
+        Twitter client = Twitter.newBuilder().oAuthConsumer(config.consumerKey, config.consumerSecret)
+                .oAuthAccessToken(config.accessToken, config.accessTokenSecret).build();
+
+        return client;
+    }
+
+    public static String abbreviateString(String input, int maxLength) {
+        if (input.length() <= maxLength) {
+            return input;
+        } else {
+            return input.substring(0, maxLength);
+        }
+    }
+
+    public static String getExtension(String filename) {
+        if (filename.contains(".")) {
+            return filename.substring(filename.lastIndexOf(".") + 1);
+        }
+        return new String();
+    }
+}
diff --git a/bundles/org.openhab.binding.x/src/main/java/org/openhab/binding/x/internal/XHandlerFactory.java b/bundles/org.openhab.binding.x/src/main/java/org/openhab/binding/x/internal/XHandlerFactory.java
new file mode 100644 (file)
index 0000000..50a9341
--- /dev/null
@@ -0,0 +1,55 @@
+/**
+ * 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.x.internal;
+
+import static org.openhab.binding.x.internal.XBindingConstants.THING_TYPE_ACCOUNT;
+
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+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.Component;
+
+/**
+ * The {@link XHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Scott Hanson - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.x", service = ThingHandlerFactory.class)
+public class XHandlerFactory extends BaseThingHandlerFactory {
+
+    private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_ACCOUNT);
+
+    @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 XHandler(thing);
+        }
+
+        return null;
+    }
+}
diff --git a/bundles/org.openhab.binding.x/src/main/java/org/openhab/binding/x/internal/action/XActions.java b/bundles/org.openhab.binding.x/src/main/java/org/openhab/binding/x/internal/action/XActions.java
new file mode 100644 (file)
index 0000000..8133645
--- /dev/null
@@ -0,0 +1,124 @@
+/**
+ * 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.x.internal.action;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.x.internal.XHandler;
+import org.openhab.core.automation.annotation.ActionInput;
+import org.openhab.core.automation.annotation.ActionOutput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link XActions} class defines rule actions for sending post
+ *
+ * @author Scott Hanson - Initial contribution
+ */
+@ThingActionsScope(name = "x")
+@NonNullByDefault
+public class XActions implements ThingActions {
+
+    private final Logger logger = LoggerFactory.getLogger(XActions.class);
+
+    private @Nullable XHandler handler;
+
+    @RuleAction(label = "@text/sendPostActionLabel", description = "@text/sendPostActionDescription")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendPost(
+            @ActionInput(name = "text") @Nullable String text) {
+        if (text == null) {
+            logger.warn("Cannot send Post as text is missing.");
+            return false;
+        }
+
+        final XHandler handler = this.handler;
+        if (handler == null) {
+            logger.debug("Handler is null, cannot post.");
+            return false;
+        } else {
+            return handler.sendPost(text);
+        }
+    }
+
+    @RuleAction(label = "@text/sendAttachmentPostActionLabel", description = "@text/sendAttachmentPostActionDescription")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendPostWithAttachment(
+            @ActionInput(name = "text") @Nullable String text, @ActionInput(name = "url") @Nullable String urlString) {
+        if (text == null) {
+            logger.warn("Cannot send Post as text is missing.");
+            return false;
+        }
+        if (urlString == null) {
+            logger.warn("Cannot send Post as urlString is missing.");
+            return false;
+        }
+
+        final XHandler handler = this.handler;
+        if (handler == null) {
+            logger.debug("Handler is null, cannot post.");
+            return false;
+        } else {
+            return handler.sendPost(text, urlString);
+        }
+    }
+
+    @RuleAction(label = "@text/sendDirectMessageActionLabel", description = "@text/sendDirectMessageActionDescription")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendDirectMessage(
+            @ActionInput(name = "recipient") @Nullable String recipient,
+            @ActionInput(name = "text") @Nullable String text) {
+        if (recipient == null) {
+            logger.warn("Cannot send Direct Message as recipient is missing.");
+            return false;
+        }
+        if (text == null) {
+            logger.warn("Cannot send Direct Message as text is missing.");
+            return false;
+        }
+
+        final XHandler handler = this.handler;
+        if (handler == null) {
+            logger.debug("Handler is null, cannot post.");
+            return false;
+        } else {
+            return handler.sendDirectMessage(recipient, text);
+        }
+    }
+
+    public static boolean sendPost(ThingActions actions, @Nullable String text) {
+        return ((XActions) actions).sendPost(text);
+    }
+
+    public static boolean sendPostWithAttachment(ThingActions actions, @Nullable String text,
+            @Nullable String urlString) {
+        return ((XActions) actions).sendPostWithAttachment(text, urlString);
+    }
+
+    public static boolean sendDirectMessage(ThingActions actions, @Nullable String recipient, @Nullable String text) {
+        return ((XActions) actions).sendDirectMessage(recipient, text);
+    }
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        if (handler instanceof XHandler xHandler) {
+            this.handler = xHandler;
+        }
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
+    }
+}
diff --git a/bundles/org.openhab.binding.x/src/main/java/org/openhab/binding/x/internal/config/XConfig.java b/bundles/org.openhab.binding.x/src/main/java/org/openhab/binding/x/internal/config/XConfig.java
new file mode 100644 (file)
index 0000000..73e11d9
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * 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.x.internal.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link XConfig} class contains fields mapping thing configuration parameters.
+ *
+ * @author Scott Hanson - Initial contribution
+ */
+
+@NonNullByDefault
+public class XConfig {
+    public String consumerKey = "";
+    public String consumerSecret = "";
+    public String accessToken = "";
+    public String accessTokenSecret = "";
+    public int refresh = 30;
+}
diff --git a/bundles/org.openhab.binding.x/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.x/src/main/resources/OH-INF/addon/addon.xml
new file mode 100644 (file)
index 0000000..acb2764
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<addon:addon id="x" 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>X Binding</name>
+       <description>Supports adding Thing for getting the Last Post. Send Posts and Pictures with Actions.</description>
+       <connection>cloud</connection>
+
+</addon:addon>
diff --git a/bundles/org.openhab.binding.x/src/main/resources/OH-INF/i18n/x.properties b/bundles/org.openhab.binding.x/src/main/resources/OH-INF/i18n/x.properties
new file mode 100644 (file)
index 0000000..66bc692
--- /dev/null
@@ -0,0 +1,32 @@
+# add-on
+
+addon.x.name = X Binding
+addon.x.description = Supports adding Thing for getting the Last Post. Send Posts and Pictures with Actions.
+
+# thing types
+
+thing-type.x.account.label = X Account
+thing-type.x.account.description = Account uses for sending posts
+
+# thing types config
+
+thing-type.config.x.account.accessToken.label = Access Token
+thing-type.config.x.account.accessTokenSecret.label = Access Token Secret
+thing-type.config.x.account.consumerKey.label = Consumer API Key
+thing-type.config.x.account.consumerSecret.label = Consumer API Secret
+thing-type.config.x.account.refresh.label = Refresh Time
+thing-type.config.x.account.refresh.description = Refresh Time for This Account in Mins
+
+# channel types
+
+channel-type.x.lastpost.label = Last Post
+channel-type.x.lastpost.description = Users Last Post
+
+# actions
+
+sendAttachmentPostActionLabel = send a Post with attachment
+sendAttachmentPostActionDescription = Sends a Post with an attachment.
+sendDirectMessageActionLabel = send a DirectMessage
+sendDirectMessageActionDescription = Sends a DirectMessage.
+sendPostActionLabel = send a Post
+sendPostActionDescription = Sends a Post.
diff --git a/bundles/org.openhab.binding.x/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.x/src/main/resources/OH-INF/thing/thing-types.xml
new file mode 100644 (file)
index 0000000..d18df8f
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="x" 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="account">
+               <label>X Account</label>
+               <description>Account uses for sending posts</description>
+
+               <channels>
+                       <channel id="lastpost" typeId="lastpost"/>
+               </channels>
+
+               <config-description>
+                       <parameter name="consumerKey" type="text" required="true">
+                               <label>Consumer API Key</label>
+                               <context>password</context>
+                       </parameter>
+                       <parameter name="consumerSecret" type="text" required="true">
+                               <label>Consumer API Secret</label>
+                               <context>password</context>
+                       </parameter>
+                       <parameter name="accessToken" type="text" required="true">
+                               <label>Access Token</label>
+                               <context>password</context>
+                       </parameter>
+                       <parameter name="accessTokenSecret" type="text" required="true">
+                               <label>Access Token Secret</label>
+                               <context>password</context>
+                       </parameter>
+                       <parameter name="refresh" type="integer" required="false" unit="min" min="1">
+                               <label>Refresh Time</label>
+                               <description>Refresh Time for This Account in Mins</description>
+                               <default>30</default>
+                       </parameter>
+               </config-description>
+
+       </thing-type>
+
+       <channel-type id="lastpost">
+               <item-type>String</item-type>
+               <label>Last Post</label>
+               <description>Users Last Post</description>
+               <state readOnly="true"/>
+       </channel-type>
+</thing:thing-descriptions>
index e992b98ac4dfc46fb1b357b46372bdb52549043b..24ba021ef0c0a69530eddd1a8a76a56086dca57b 100644 (file)
     <module>org.openhab.binding.tplinksmarthome</module>
     <module>org.openhab.binding.tr064</module>
     <module>org.openhab.binding.tradfri</module>
-    <module>org.openhab.binding.twitter</module>
     <module>org.openhab.binding.unifi</module>
     <module>org.openhab.binding.unifiedremote</module>
     <module>org.openhab.binding.upnpcontrol</module>
     <module>org.openhab.binding.wled</module>
     <module>org.openhab.binding.wolfsmartset</module>
     <module>org.openhab.binding.wundergroundupdatereceiver</module>
+    <module>org.openhab.binding.x</module>
     <module>org.openhab.binding.xmltv</module>
     <module>org.openhab.binding.xmppclient</module>
     <module>org.openhab.binding.yamahamusiccast</module>