diff --git a/pom.xml b/pom.xml index 8c3cafd82..bd96a4976 100644 --- a/pom.xml +++ b/pom.xml @@ -116,6 +116,18 @@ + + com.coveo + fmt-maven-plugin + 2.6.0 + + + + format + + + + org.apache.maven.plugins maven-compiler-plugin @@ -272,19 +284,19 @@ ossrh - gpg - ${env.GPG_KEYNAME} - ${env.GPG_PASSPHRASE} - false - deploy/pubring.gpg - deploy/secring.gpg - + gpg + ${env.GPG_KEYNAME} + ${env.GPG_PASSPHRASE} + false + deploy/pubring.gpg + deploy/secring.gpg + - - performRelease - true - - + + performRelease + true + + diff --git a/src/main/java/com/beowulfe/hap/HomekitAccessory.java b/src/main/java/com/beowulfe/hap/HomekitAccessory.java index af0a43e8d..0c4d57821 100644 --- a/src/main/java/com/beowulfe/hap/HomekitAccessory.java +++ b/src/main/java/com/beowulfe/hap/HomekitAccessory.java @@ -3,64 +3,67 @@ import java.util.Collection; /** - * Base interface for all Homekit Accessories. You can implement this interface directly, but most users will - * prefer to use the more full featured interfaces in {@link com.beowulfe.hap.accessories} which include a - * default implementation of {@link #getServices()}. - * + * Base interface for all Homekit Accessories. You can implement this interface directly, but most + * users will prefer to use the more full featured interfaces in {@link + * com.beowulfe.hap.accessories} which include a default implementation of {@link #getServices()}. + * * @author Andy Lintner */ public interface HomekitAccessory { - - /** - * A unique identifier that must remain static across invocations to prevent errors with paired iOS devices. When used - * as a child of a Bridge, this value must be greater than 1, as that ID is reserved for the Bridge itself. - * - * @return the unique identifier. - */ - int getId(); - /** - * Returns a label to display in iOS. - * - * @return the label. - */ - String getLabel(); - - /** - * Performs an operation that can be used to identify the accessory. This action can be performed without - * pairing. - */ - void identify(); + /** + * A unique identifier that must remain static across invocations to prevent errors with paired + * iOS devices. When used as a child of a Bridge, this value must be greater than 1, as that ID is + * reserved for the Bridge itself. + * + * @return the unique identifier. + */ + int getId(); - /** - * Returns a serial number to display in iOS. - * - * @return the serial number, or null. - */ - String getSerialNumber(); + /** + * Returns a label to display in iOS. + * + * @return the label. + */ + String getLabel(); - /** - * Returns a model name to display in iOS. - * - * @return the model name, or null. - */ - String getModel(); + /** + * Performs an operation that can be used to identify the accessory. This action can be performed + * without pairing. + */ + void identify(); - /** - * Returns a manufacturer to display in iOS. - * - * @return the manufacturer, or null. - */ - String getManufacturer(); - - /** - * The collection of Services this accessory supports. Services are the primary way to interact with the accessory via - * Homekit. Besides the Services offered here, the accessory will automatically include the required information service. - * - * This method will only be useful if you're implementing your own accessory type. For the standard accessories, use - * the default implementation provided by the interfaces in {@link com.beowulfe.hap.accessories}. - * - * @return the collection of services. - */ - Collection getServices(); + /** + * Returns a serial number to display in iOS. + * + * @return the serial number, or null. + */ + String getSerialNumber(); + + /** + * Returns a model name to display in iOS. + * + * @return the model name, or null. + */ + String getModel(); + + /** + * Returns a manufacturer to display in iOS. + * + * @return the manufacturer, or null. + */ + String getManufacturer(); + + /** + * The collection of Services this accessory supports. Services are the primary way to interact + * with the accessory via Homekit. Besides the Services offered here, the accessory will + * automatically include the required information service. + * + *

This method will only be useful if you're implementing your own accessory type. For the + * standard accessories, use the default implementation provided by the interfaces in {@link + * com.beowulfe.hap.accessories}. + * + * @return the collection of services. + */ + Collection getServices(); } diff --git a/src/main/java/com/beowulfe/hap/HomekitAuthInfo.java b/src/main/java/com/beowulfe/hap/HomekitAuthInfo.java index 0b91897e9..d43a57fc4 100644 --- a/src/main/java/com/beowulfe/hap/HomekitAuthInfo.java +++ b/src/main/java/com/beowulfe/hap/HomekitAuthInfo.java @@ -3,79 +3,86 @@ import java.math.BigInteger; /** - * Authentication info that must be provided when constructing a new {@link HomekitServer}. You will need to implement - * this interface yourself to provide the necessary callbacks to a persistent storage mechanism. All values provided - * must be constant across invocations or the accessories will require re-pairing within iOS. + * Authentication info that must be provided when constructing a new {@link HomekitServer}. You will + * need to implement this interface yourself to provide the necessary callbacks to a persistent + * storage mechanism. All values provided must be constant across invocations or the accessories + * will require re-pairing within iOS. * * @author Andy Lintner */ public interface HomekitAuthInfo { - /** - * A pin code used for pairing the device. This pin will be required within iOS in order to complete pairing. The numbers - * cannot be sequential and should not have a repeating pattern. - * - * @return the pin code, in the form ###-##-### - */ - String getPin(); - - /** - * A unique MAC address to be advertised with the Homekit information. This does not have to be the MAC address of the - * network interface. You can generate this using {@link HomekitServer#generateMac()}. - * - * @return the unique MAC address. - */ - String getMac(); + /** + * A pin code used for pairing the device. This pin will be required within iOS in order to + * complete pairing. The numbers cannot be sequential and should not have a repeating pattern. + * + * @return the pin code, in the form ###-##-### + */ + String getPin(); - /** - * The Salt that will be used when hashing the pin code to send to the client. You should generate this using - * {@link HomekitServer#generateSalt()}. - * - * @return the Salt. - */ - BigInteger getSalt(); + /** + * A unique MAC address to be advertised with the Homekit information. This does not have to be + * the MAC address of the network interface. You can generate this using {@link + * HomekitServer#generateMac()}. + * + * @return the unique MAC address. + */ + String getMac(); - /** - * The private key used by the server during pairing and message encryption. You should generate this using - * {@link HomekitServer#generateKey()} - * - * @return the private key. - */ - byte[] getPrivateKey(); - - /** - * Called during the pairing process, you should store the user and public key in a manner that the public key can later - * be retrieved using {@link #getUserPublicKey(String)}. This must be stored in a persistent store as pairing will - * need to be reset if the information is lost. - * - * @param username the iOS device's username. The value will not be meaningful to anything but iOS. - * @param publicKey the iOS device's public key. - */ - void createUser(String username, byte[] publicKey); - - /** - * Called when an iOS device needs to remove an existing pairing. Subsequent calls to {@link #getUserPublicKey(String)} for this - * username return null. - * - * @param username the username to delete from the persistent store. - */ - void removeUser(String username); + /** + * The Salt that will be used when hashing the pin code to send to the client. You should generate + * this using {@link HomekitServer#generateSalt()}. + * + * @return the Salt. + */ + BigInteger getSalt(); - /** - * Called when an already paired iOS device is re-connecting. The public key returned by this method will be compared - * with the signature of the pair verification request to validate the device. - * - * @param username the username of the iOS device to retrieve the public key for. - * @return the previously stored public key for this user. - */ - byte[] getUserPublicKey(String username); - - /** - * Called to check if a user has been created. The homekit accessory advertises whether the accessory has already been paired. - * At this time, it's unclear whether multiple users can be created, however it is known that advertising as unpaired - * will break in iOS 9. The default value has been provided to maintain API compatibility for implementations targeting iOS 8. - * - * @return whether a user has been created and stored - */ - default boolean hasUser() { return false; }; + /** + * The private key used by the server during pairing and message encryption. You should generate + * this using {@link HomekitServer#generateKey()} + * + * @return the private key. + */ + byte[] getPrivateKey(); + + /** + * Called during the pairing process, you should store the user and public key in a manner that + * the public key can later be retrieved using {@link #getUserPublicKey(String)}. This must be + * stored in a persistent store as pairing will need to be reset if the information is lost. + * + * @param username the iOS device's username. The value will not be meaningful to anything but + * iOS. + * @param publicKey the iOS device's public key. + */ + void createUser(String username, byte[] publicKey); + + /** + * Called when an iOS device needs to remove an existing pairing. Subsequent calls to {@link + * #getUserPublicKey(String)} for this username return null. + * + * @param username the username to delete from the persistent store. + */ + void removeUser(String username); + + /** + * Called when an already paired iOS device is re-connecting. The public key returned by this + * method will be compared with the signature of the pair verification request to validate the + * device. + * + * @param username the username of the iOS device to retrieve the public key for. + * @return the previously stored public key for this user. + */ + byte[] getUserPublicKey(String username); + + /** + * Called to check if a user has been created. The homekit accessory advertises whether the + * accessory has already been paired. At this time, it's unclear whether multiple users can be + * created, however it is known that advertising as unpaired will break in iOS 9. The default + * value has been provided to maintain API compatibility for implementations targeting iOS 8. + * + * @return whether a user has been created and stored + */ + default boolean hasUser() { + return false; + }; } diff --git a/src/main/java/com/beowulfe/hap/HomekitCharacteristicChangeCallback.java b/src/main/java/com/beowulfe/hap/HomekitCharacteristicChangeCallback.java index a02e88100..fbc160251 100644 --- a/src/main/java/com/beowulfe/hap/HomekitCharacteristicChangeCallback.java +++ b/src/main/java/com/beowulfe/hap/HomekitCharacteristicChangeCallback.java @@ -3,18 +3,20 @@ import com.beowulfe.hap.characteristics.EventableCharacteristic; /** - * A callback interface for notifying subscribers that a characteristic value has changed. - * - * {@link EventableCharacteristic}s can be subscribed to, and in doing so, are supplied an instance of this class. Implementors - * should call the {@link #changed()} method on the passed object when a subscribed characteristic changes. + * A callback interface for notifying subscribers that a characteristic value has changed. + * + *

{@link EventableCharacteristic}s can be subscribed to, and in doing so, are supplied an + * instance of this class. Implementors should call the {@link #changed()} method on the passed + * object when a subscribed characteristic changes. * * @author Andy Lintner */ @FunctionalInterface public interface HomekitCharacteristicChangeCallback { - /** - * Call when the value of the characteristic that was subscribed to when this object was passed changes. - */ - void changed(); + /** + * Call when the value of the characteristic that was subscribed to when this object was passed + * changes. + */ + void changed(); } diff --git a/src/main/java/com/beowulfe/hap/HomekitRoot.java b/src/main/java/com/beowulfe/hap/HomekitRoot.java index b9d954d0d..0d0ba6462 100644 --- a/src/main/java/com/beowulfe/hap/HomekitRoot.java +++ b/src/main/java/com/beowulfe/hap/HomekitRoot.java @@ -6,160 +6,170 @@ import com.beowulfe.hap.impl.connections.HomekitClientConnectionFactoryImpl; import com.beowulfe.hap.impl.connections.SubscriptionManager; import com.beowulfe.hap.impl.jmdns.JmdnsHomekitAdvertiser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.net.InetAddress; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * Provides advertising and handling for Homekit accessories. This class handles the advertising of Homekit accessories and - * contains one or more accessories. When implementing a bridge accessory, you will interact with this class directly. Instantiate - * it via {@link HomekitServer#createBridge(HomekitAuthInfo, String, String, String, String)}. For single accessories, this is composed - * by {@link HomekitStandaloneAccessoryServer}. + * Provides advertising and handling for Homekit accessories. This class handles the advertising of + * Homekit accessories and contains one or more accessories. When implementing a bridge accessory, + * you will interact with this class directly. Instantiate it via {@link + * HomekitServer#createBridge(HomekitAuthInfo, String, String, String, String)}. For single + * accessories, this is composed by {@link HomekitStandaloneAccessoryServer}. * * @author Andy Lintner */ public class HomekitRoot { - - private final static Logger logger = LoggerFactory.getLogger(HomekitRoot.class); - - private final JmdnsHomekitAdvertiser advertiser; - private final HomekitWebHandler webHandler; - private final HomekitAuthInfo authInfo; - private final String label; - private final HomekitRegistry registry; - private final SubscriptionManager subscriptions = new SubscriptionManager(); - private boolean started = false; - private int configurationIndex = 1; - HomekitRoot(String label, HomekitWebHandler webHandler, InetAddress localhost, - HomekitAuthInfo authInfo) throws IOException { - this(label, webHandler, authInfo, new JmdnsHomekitAdvertiser(localhost)); - } - - HomekitRoot(String label, HomekitWebHandler webHandler, HomekitAuthInfo authInfo, - JmdnsHomekitAdvertiser advertiser) throws IOException { - this.advertiser = advertiser; - this.webHandler = webHandler; - this.authInfo = authInfo; - this.label = label; - this.registry = new HomekitRegistry(label); - } - - /** - * Add an accessory to be handled and advertised by this root. Any existing Homekit connections will be terminated to allow - * the clients to reconnect and see the updated accessory list. When using this for a bridge, the ID of the accessory must be - * greater than 1, as that ID is reserved for the Bridge itself. - * - * @param accessory to advertise and handle. - */ - public void addAccessory(HomekitAccessory accessory) { - if (accessory.getId() <= 1 && !(accessory instanceof Bridge)) { - throw new IndexOutOfBoundsException("The ID of an accessory used in a bridge must be greater than 1"); - } - addAccessorySkipRangeCheck(accessory); - } - - /** - * Skips the range check. Used by {@link HomekitStandaloneAccessoryServer} as well as {@link #addAccessory(HomekitAccessory)}; - * - * @param accessory to advertise and handle. - */ - void addAccessorySkipRangeCheck(HomekitAccessory accessory) { - this.registry.add(accessory); - logger.info("Added accessory " + accessory.getLabel()); - if (started) { - registry.reset(); - webHandler.resetConnections(); - } - } - - /** - * Removes an accessory from being handled or advertised by this root. Any existing Homekit connections will be terminated to allow - * the clients to reconnect and see the updated accessory list. - * - * @param accessory accessory to cease advertising and handling - */ - public void removeAccessory(HomekitAccessory accessory) { - this.registry.remove(accessory); - logger.info("Removed accessory " + accessory.getLabel()); - if (started) { - registry.reset(); - webHandler.resetConnections(); - } - } - - /** - * Starts advertising and handling the previously added Homekit accessories. You should try to call this after you have used the - * {@link #addAccessory(HomekitAccessory)} method to add all the initial accessories you plan on advertising, as any later additions - * will cause the Homekit clients to reconnect. - */ - public void start() { - started = true; - registry.reset(); - webHandler.start(new HomekitClientConnectionFactoryImpl(authInfo, - registry, subscriptions, advertiser)).thenAccept(port -> { - try { - refreshAuthInfo(); - advertiser.advertise(label, authInfo.getMac(), port, configurationIndex); - } catch (Exception e) { - throw new RuntimeException(e); - } - }); - } - - /** - * Stops advertising and handling the Homekit accessories. - */ - public void stop() { - advertiser.stop(); - webHandler.stop(); - started = false; - } - - /** - * Refreshes auth info after it has been changed outside this library - * - * @throws IOException if there is an error in the underlying protocol, such as a TCP error - */ - public void refreshAuthInfo() throws IOException { - advertiser.setDiscoverable(!authInfo.hasUser()); - } - - /** - * By default, most homekit requests require that the client be paired. Allowing unauthenticated requests - * can be useful for debugging, but should not be used in production. - * - * @param allow whether to allow unauthenticated requests - */ - public void allowUnauthenticatedRequests(boolean allow) { - registry.setAllowUnauthenticatedRequests(allow); - } - - - /** - * By default, the bridge advertises itself at revision 1. If you make changes to the accessories you're - * including in the bridge after your first call to {@link start()}, you should increment this number. The - * behavior of the client if the configuration index were to decrement is undefined, so this implementation will - * not manage the configuration index by automatically incrementing - preserving this state across invocations should - * be handled externally. - * - * @param revision an integer, greater than or equal to one, indicating the revision of the accessory information - * @throws IOException if there is an error in the underlying protocol, such as a TCP error - */ - public void setConfigurationIndex(int revision) throws IOException { - if (revision < 1) { - throw new IllegalArgumentException("revision must be greater than or equal to 1"); - } - this.configurationIndex = revision; - if (this.started) { - advertiser.setConfigurationIndex(revision); - } - } - - HomekitRegistry getRegistry() { - return registry; - } + private static final Logger logger = LoggerFactory.getLogger(HomekitRoot.class); + + private final JmdnsHomekitAdvertiser advertiser; + private final HomekitWebHandler webHandler; + private final HomekitAuthInfo authInfo; + private final String label; + private final HomekitRegistry registry; + private final SubscriptionManager subscriptions = new SubscriptionManager(); + private boolean started = false; + private int configurationIndex = 1; + + HomekitRoot( + String label, HomekitWebHandler webHandler, InetAddress localhost, HomekitAuthInfo authInfo) + throws IOException { + this(label, webHandler, authInfo, new JmdnsHomekitAdvertiser(localhost)); + } + + HomekitRoot( + String label, + HomekitWebHandler webHandler, + HomekitAuthInfo authInfo, + JmdnsHomekitAdvertiser advertiser) + throws IOException { + this.advertiser = advertiser; + this.webHandler = webHandler; + this.authInfo = authInfo; + this.label = label; + this.registry = new HomekitRegistry(label); + } + + /** + * Add an accessory to be handled and advertised by this root. Any existing Homekit connections + * will be terminated to allow the clients to reconnect and see the updated accessory list. When + * using this for a bridge, the ID of the accessory must be greater than 1, as that ID is reserved + * for the Bridge itself. + * + * @param accessory to advertise and handle. + */ + public void addAccessory(HomekitAccessory accessory) { + if (accessory.getId() <= 1 && !(accessory instanceof Bridge)) { + throw new IndexOutOfBoundsException( + "The ID of an accessory used in a bridge must be greater than 1"); + } + addAccessorySkipRangeCheck(accessory); + } + + /** + * Skips the range check. Used by {@link HomekitStandaloneAccessoryServer} as well as {@link + * #addAccessory(HomekitAccessory)}; + * + * @param accessory to advertise and handle. + */ + void addAccessorySkipRangeCheck(HomekitAccessory accessory) { + this.registry.add(accessory); + logger.info("Added accessory " + accessory.getLabel()); + if (started) { + registry.reset(); + webHandler.resetConnections(); + } + } + + /** + * Removes an accessory from being handled or advertised by this root. Any existing Homekit + * connections will be terminated to allow the clients to reconnect and see the updated accessory + * list. + * + * @param accessory accessory to cease advertising and handling + */ + public void removeAccessory(HomekitAccessory accessory) { + this.registry.remove(accessory); + logger.info("Removed accessory " + accessory.getLabel()); + if (started) { + registry.reset(); + webHandler.resetConnections(); + } + } + + /** + * Starts advertising and handling the previously added Homekit accessories. You should try to + * call this after you have used the {@link #addAccessory(HomekitAccessory)} method to add all the + * initial accessories you plan on advertising, as any later additions will cause the Homekit + * clients to reconnect. + */ + public void start() { + started = true; + registry.reset(); + webHandler + .start( + new HomekitClientConnectionFactoryImpl(authInfo, registry, subscriptions, advertiser)) + .thenAccept( + port -> { + try { + refreshAuthInfo(); + advertiser.advertise(label, authInfo.getMac(), port, configurationIndex); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + + /** Stops advertising and handling the Homekit accessories. */ + public void stop() { + advertiser.stop(); + webHandler.stop(); + started = false; + } + + /** + * Refreshes auth info after it has been changed outside this library + * + * @throws IOException if there is an error in the underlying protocol, such as a TCP error + */ + public void refreshAuthInfo() throws IOException { + advertiser.setDiscoverable(!authInfo.hasUser()); + } + + /** + * By default, most homekit requests require that the client be paired. Allowing unauthenticated + * requests can be useful for debugging, but should not be used in production. + * + * @param allow whether to allow unauthenticated requests + */ + public void allowUnauthenticatedRequests(boolean allow) { + registry.setAllowUnauthenticatedRequests(allow); + } + + /** + * By default, the bridge advertises itself at revision 1. If you make changes to the accessories + * you're including in the bridge after your first call to {@link start()}, you should increment + * this number. The behavior of the client if the configuration index were to decrement is + * undefined, so this implementation will not manage the configuration index by automatically + * incrementing - preserving this state across invocations should be handled externally. + * + * @param revision an integer, greater than or equal to one, indicating the revision of the + * accessory information + * @throws IOException if there is an error in the underlying protocol, such as a TCP error + */ + public void setConfigurationIndex(int revision) throws IOException { + if (revision < 1) { + throw new IllegalArgumentException("revision must be greater than or equal to 1"); + } + this.configurationIndex = revision; + if (this.started) { + advertiser.setConfigurationIndex(revision); + } + } + HomekitRegistry getRegistry() { + return registry; + } } diff --git a/src/main/java/com/beowulfe/hap/HomekitServer.java b/src/main/java/com/beowulfe/hap/HomekitServer.java index 8653ab043..47e7afb2a 100644 --- a/src/main/java/com/beowulfe/hap/HomekitServer.java +++ b/src/main/java/com/beowulfe/hap/HomekitServer.java @@ -1,143 +1,152 @@ package com.beowulfe.hap; +import com.beowulfe.hap.impl.HomekitBridge; +import com.beowulfe.hap.impl.HomekitUtils; +import com.beowulfe.hap.impl.http.impl.HomekitHttpServer; import java.io.IOException; import java.math.BigInteger; import java.net.InetAddress; import java.security.InvalidAlgorithmParameterException; -import com.beowulfe.hap.impl.HomekitBridge; -import com.beowulfe.hap.impl.HomekitUtils; -import com.beowulfe.hap.impl.http.impl.HomekitHttpServer; - /** - * The main entry point for hap-java. Creating an instance of this class will listen for Homekit connections - * on the supplied port. Only a single root accessory can be added for each unique instance and port, however, - * that accessory may be a {@link #createBridge(HomekitAuthInfo, String, String, String, String) bridge accessory} - * containing child accessories. - * - * The {@link HomekitAuthInfo HomekitAuthInfo} argument when creating accessories should be an implementation supplied - * by your application. Several of the values needed for your implementation are provided by this class, specifically - * {@link #generateKey() generateKey}, {@link #generateMac() generateMac}, and {@link #generateSalt()}. It is important - * that you provide these same values on each start of your application or Homekit will fail to recognize your device as - * being the same. - * + * The main entry point for hap-java. Creating an instance of this class will listen for Homekit + * connections on the supplied port. Only a single root accessory can be added for each unique + * instance and port, however, that accessory may be a {@link #createBridge(HomekitAuthInfo, String, + * String, String, String) bridge accessory} containing child accessories. + * + *

The {@link HomekitAuthInfo HomekitAuthInfo} argument when creating accessories should be an + * implementation supplied by your application. Several of the values needed for your implementation + * are provided by this class, specifically {@link #generateKey() generateKey}, {@link + * #generateMac() generateMac}, and {@link #generateSalt()}. It is important that you provide these + * same values on each start of your application or Homekit will fail to recognize your device as + * being the same. + * * @author Andy Lintner */ public class HomekitServer { - private final HomekitHttpServer http; - private final InetAddress localAddress; - - /** - * Constructor. Contains an argument indicating the number of threads to use in the http server. The other constructors - * default this to the number of available processors, however you may increase this in an environment with many users - * and/or blocking accessory implementations. - * - * @param localAddress local address to bind to. - * @param port local port to bind to. - * @param nThreads number of threads to use in the http server - * @throws IOException when the server cannot bind to the supplied port - */ - public HomekitServer(InetAddress localAddress, int port, int nThreads) throws IOException { - this.localAddress = localAddress; - http = new HomekitHttpServer(port, nThreads); - } - - /** - * Constructor - * - * @param localAddress local address to bind to - * @param port local port to bind to - * @throws IOException when the server cannot bind to the supplied port - */ - public HomekitServer(InetAddress localAddress, int port) throws IOException { - this(localAddress, port, Runtime.getRuntime().availableProcessors()); - } - - /** - * Constructor - * - * @param port local port to bind to. - * @throws IOException when the server cannot bind to the supplied port - */ - public HomekitServer(int port) throws IOException { - this(InetAddress.getLocalHost(), port); - } - - /** - * Stops the service, closing down existing connections and preventing new ones. - */ - public void stop() { - http.stop(); - } - - /** - * Creates a single (non-bridge) accessory - * - * @param authInfo authentication information for this accessory. These values should be persisted and re-supplied on re-start - * of your application. - * @param accessory accessory implementation. This will usually be an implementation of an interface in - * {#link com.beowulfe.hap.accessories com.beowulfe.hap.accessories}. - * @return the newly created server. Call {@link HomekitStandaloneAccessoryServer#start start} on this to begin. - * @throws IOException when mDNS cannot connect to the network - */ - public HomekitStandaloneAccessoryServer createStandaloneAccessory(HomekitAuthInfo authInfo, - HomekitAccessory accessory) throws IOException { - return new HomekitStandaloneAccessoryServer(accessory, http, - localAddress, authInfo); - } - - /** - * Creates a bridge accessory, capable of holding multiple child accessories. This has the advantage over multiple standalone - * accessories of only requiring a single pairing from iOS for the bridge. - * - * @param authInfo authentication information for this accessory. These values should be persisted and re-supplied on re-start - * of your application. - * @param label label for the bridge. This will show in iOS during pairing. - * @param manufacturer manufacturer of the bridge. This information is exposed to iOS for unknown purposes. - * @param model model of the bridge. This is also exposed to iOS for unknown purposes. - * @param serialNumber serial number of the bridge. Also exposed. Purposes also unknown. - * @return the bridge, from which you can {@link HomekitRoot#addAccessory add accessories} and then {@link HomekitRoot#start start} handling requests. - * @throws IOException when mDNS cannot connect to the network - */ - public HomekitRoot createBridge(HomekitAuthInfo authInfo, String label, String manufacturer, - String model, String serialNumber) throws IOException { - HomekitRoot root = new HomekitRoot(label, http, localAddress, authInfo); - root.addAccessory(new HomekitBridge(label, serialNumber, model, manufacturer)); - return root; - } - - /** - * Generates a value to supply in {@link HomekitAuthInfo#getSalt() HomekitAuthInfo.getSalt()}. This is used to salt - * the pin-code. You don't need to worry about that though - the salting is done on the plaintext pin. (Yes, plaintext - * passwords are bad. Please don't secure your nuclear storage facility with this implementation) - * - * @return the generated salt - */ - static public BigInteger generateSalt() { - return HomekitUtils.generateSalt(); - } - - /** - * Generates a value to supply in {@link HomekitAuthInfo#getPrivateKey() HomekitAuthInfo.getPrivKey()}. This is used as the - * private key during pairing and connection setup. - * - * @return the generated key - * @throws InvalidAlgorithmParameterException if the JVM does not contain the necessary encryption algorithms. - */ - static public byte[] generateKey() throws InvalidAlgorithmParameterException { - return HomekitUtils.generateKey(); - } - - /** - * Generates a value to supply in {@link HomekitAuthInfo#getMac() HomekitAuthInfo.getMac()}. This is used as the unique - * identifier of the accessory during mDNS advertising. It is a valid MAC address generated in the locally administered - * range so as not to conflict with any commercial devices. - * - * @return the generated MAC - */ - static public String generateMac() { - return HomekitUtils.generateMac(); - } + private final HomekitHttpServer http; + private final InetAddress localAddress; + + /** + * Constructor. Contains an argument indicating the number of threads to use in the http server. + * The other constructors default this to the number of available processors, however you may + * increase this in an environment with many users and/or blocking accessory implementations. + * + * @param localAddress local address to bind to. + * @param port local port to bind to. + * @param nThreads number of threads to use in the http server + * @throws IOException when the server cannot bind to the supplied port + */ + public HomekitServer(InetAddress localAddress, int port, int nThreads) throws IOException { + this.localAddress = localAddress; + http = new HomekitHttpServer(port, nThreads); + } + + /** + * Constructor + * + * @param localAddress local address to bind to + * @param port local port to bind to + * @throws IOException when the server cannot bind to the supplied port + */ + public HomekitServer(InetAddress localAddress, int port) throws IOException { + this(localAddress, port, Runtime.getRuntime().availableProcessors()); + } + + /** + * Constructor + * + * @param port local port to bind to. + * @throws IOException when the server cannot bind to the supplied port + */ + public HomekitServer(int port) throws IOException { + this(InetAddress.getLocalHost(), port); + } + + /** Stops the service, closing down existing connections and preventing new ones. */ + public void stop() { + http.stop(); + } + + /** + * Creates a single (non-bridge) accessory + * + * @param authInfo authentication information for this accessory. These values should be persisted + * and re-supplied on re-start of your application. + * @param accessory accessory implementation. This will usually be an implementation of an + * interface in {#link com.beowulfe.hap.accessories com.beowulfe.hap.accessories}. + * @return the newly created server. Call {@link HomekitStandaloneAccessoryServer#start start} on + * this to begin. + * @throws IOException when mDNS cannot connect to the network + */ + public HomekitStandaloneAccessoryServer createStandaloneAccessory( + HomekitAuthInfo authInfo, HomekitAccessory accessory) throws IOException { + return new HomekitStandaloneAccessoryServer(accessory, http, localAddress, authInfo); + } + + /** + * Creates a bridge accessory, capable of holding multiple child accessories. This has the + * advantage over multiple standalone accessories of only requiring a single pairing from iOS for + * the bridge. + * + * @param authInfo authentication information for this accessory. These values should be persisted + * and re-supplied on re-start of your application. + * @param label label for the bridge. This will show in iOS during pairing. + * @param manufacturer manufacturer of the bridge. This information is exposed to iOS for unknown + * purposes. + * @param model model of the bridge. This is also exposed to iOS for unknown purposes. + * @param serialNumber serial number of the bridge. Also exposed. Purposes also unknown. + * @return the bridge, from which you can {@link HomekitRoot#addAccessory add accessories} and + * then {@link HomekitRoot#start start} handling requests. + * @throws IOException when mDNS cannot connect to the network + */ + public HomekitRoot createBridge( + HomekitAuthInfo authInfo, + String label, + String manufacturer, + String model, + String serialNumber) + throws IOException { + HomekitRoot root = new HomekitRoot(label, http, localAddress, authInfo); + root.addAccessory(new HomekitBridge(label, serialNumber, model, manufacturer)); + return root; + } + + /** + * Generates a value to supply in {@link HomekitAuthInfo#getSalt() HomekitAuthInfo.getSalt()}. + * This is used to salt the pin-code. You don't need to worry about that though - the salting is + * done on the plaintext pin. (Yes, plaintext passwords are bad. Please don't secure your nuclear + * storage facility with this implementation) + * + * @return the generated salt + */ + public static BigInteger generateSalt() { + return HomekitUtils.generateSalt(); + } + + /** + * Generates a value to supply in {@link HomekitAuthInfo#getPrivateKey() + * HomekitAuthInfo.getPrivKey()}. This is used as the private key during pairing and connection + * setup. + * + * @return the generated key + * @throws InvalidAlgorithmParameterException if the JVM does not contain the necessary encryption + * algorithms. + */ + public static byte[] generateKey() throws InvalidAlgorithmParameterException { + return HomekitUtils.generateKey(); + } + /** + * Generates a value to supply in {@link HomekitAuthInfo#getMac() HomekitAuthInfo.getMac()}. This + * is used as the unique identifier of the accessory during mDNS advertising. It is a valid MAC + * address generated in the locally administered range so as not to conflict with any commercial + * devices. + * + * @return the generated MAC + */ + public static String generateMac() { + return HomekitUtils.generateMac(); + } } diff --git a/src/main/java/com/beowulfe/hap/HomekitStandaloneAccessoryServer.java b/src/main/java/com/beowulfe/hap/HomekitStandaloneAccessoryServer.java index 9fee827f0..3b631f276 100644 --- a/src/main/java/com/beowulfe/hap/HomekitStandaloneAccessoryServer.java +++ b/src/main/java/com/beowulfe/hap/HomekitStandaloneAccessoryServer.java @@ -1,36 +1,34 @@ package com.beowulfe.hap; +import com.beowulfe.hap.impl.HomekitWebHandler; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; -import com.beowulfe.hap.impl.HomekitWebHandler; - /** - * A server for exposing standalone Homekit accessory (as opposed to a Bridge accessory which contains multiple accessories). - * Each standalone accessory will have its own pairing information, port, and pin. Instantiate this class via - * {@link HomekitServer#createStandaloneAccessory(HomekitAuthInfo, HomekitAccessory)}. + * A server for exposing standalone Homekit accessory (as opposed to a Bridge accessory which + * contains multiple accessories). Each standalone accessory will have its own pairing information, + * port, and pin. Instantiate this class via {@link + * HomekitServer#createStandaloneAccessory(HomekitAuthInfo, HomekitAccessory)}. * * @author Andy Lintner */ public class HomekitStandaloneAccessoryServer { - - private final HomekitRoot root; - HomekitStandaloneAccessoryServer(HomekitAccessory accessory, - HomekitWebHandler webHandler, InetAddress localhost, - HomekitAuthInfo authInfo) throws UnknownHostException, IOException { - root = new HomekitRoot(accessory.getLabel(), webHandler, localhost, authInfo); - root.addAccessory(accessory); - } - - /** - * Begins advertising and handling requests for this accessory. - */ - public void start() { - root.start(); - } + private final HomekitRoot root; - + HomekitStandaloneAccessoryServer( + HomekitAccessory accessory, + HomekitWebHandler webHandler, + InetAddress localhost, + HomekitAuthInfo authInfo) + throws UnknownHostException, IOException { + root = new HomekitRoot(accessory.getLabel(), webHandler, localhost, authInfo); + root.addAccessory(accessory); + } + /** Begins advertising and handling requests for this accessory. */ + public void start() { + root.start(); + } } diff --git a/src/main/java/com/beowulfe/hap/Service.java b/src/main/java/com/beowulfe/hap/Service.java index d9c74dae2..5bebea7be 100644 --- a/src/main/java/com/beowulfe/hap/Service.java +++ b/src/main/java/com/beowulfe/hap/Service.java @@ -1,8 +1,7 @@ package com.beowulfe.hap; -import java.util.List; - import com.beowulfe.hap.characteristics.Characteristic; +import java.util.List; /** * Interface for a Service offered by an accessory. @@ -11,22 +10,23 @@ */ public interface Service { - /** - * Characteristics are the variables offered for reading, updating, and eventing by the Service over the Homekit protocol. - * - * It is important to maintain the order of this list and not change its contents between invocations, or a pairing error - * will result. - * - * @return the list of Characteristics. - */ - List getCharacteristics(); - - /** - * The type is a UUID that uniquely identifies the type of Service offered. Apple defines several types for standard - * Services, however UUIDs outside this range are allowed for custom Services. - * - * @return A string representation of the UUID, with hexadecimal digits in the format ########-####-####-####-############. - */ - String getType(); + /** + * Characteristics are the variables offered for reading, updating, and eventing by the Service + * over the Homekit protocol. + * + *

It is important to maintain the order of this list and not change its contents between + * invocations, or a pairing error will result. + * + * @return the list of Characteristics. + */ + List getCharacteristics(); -} \ No newline at end of file + /** + * The type is a UUID that uniquely identifies the type of Service offered. Apple defines several + * types for standard Services, however UUIDs outside this range are allowed for custom Services. + * + * @return A string representation of the UUID, with hexadecimal digits in the format + * ########-####-####-####-############. + */ + String getType(); +} diff --git a/src/main/java/com/beowulfe/hap/accessories/BatteryAccessory.java b/src/main/java/com/beowulfe/hap/accessories/BatteryAccessory.java index b69bdf7e3..849454a97 100644 --- a/src/main/java/com/beowulfe/hap/accessories/BatteryAccessory.java +++ b/src/main/java/com/beowulfe/hap/accessories/BatteryAccessory.java @@ -1,8 +1,7 @@ package com.beowulfe.hap.accessories; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; /** * Do not use. Devices that have battery levels should implement LowBatteryStatusAccessory. @@ -12,22 +11,20 @@ @Deprecated public interface BatteryAccessory { - /** - * Retrieves the battery level of the accessory. - * - * @return a future that will contain the accessory's battery state - */ - CompletableFuture getBatteryLevelState(); + /** + * Retrieves the battery level of the accessory. + * + * @return a future that will contain the accessory's battery state + */ + CompletableFuture getBatteryLevelState(); - /** - * Subscribes to changes in the battery level. - * - * @param callback the function to call when battery level changes. - */ - void subscribeBatteryLevelState(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes in the battery level. + * + * @param callback the function to call when battery level changes. + */ + void subscribeBatteryLevelState(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the battery level. - */ - void unsubscribeBatteryLevelState(); + /** Unsubscribes from changes in the battery level. */ + void unsubscribeBatteryLevelState(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/BatteryStatusAccessory.java b/src/main/java/com/beowulfe/hap/accessories/BatteryStatusAccessory.java index 2e817a603..21f8f0356 100644 --- a/src/main/java/com/beowulfe/hap/accessories/BatteryStatusAccessory.java +++ b/src/main/java/com/beowulfe/hap/accessories/BatteryStatusAccessory.java @@ -1,34 +1,31 @@ package com.beowulfe.hap.accessories; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; /** - * An accessory that runs on batteries. Accessories that run on batteries are able to report - * battery level. + * An accessory that runs on batteries. Accessories that run on batteries are able to report battery + * level. * * @author Tim Harper */ public interface BatteryStatusAccessory { - /** - * Queries if the device battery level is low; returning a value of true - * will cause a low-battery status to appear in Home for the device. - * - * @return a future that will contain the accessory's low battery state - */ - CompletableFuture getLowBatteryState(); + /** + * Queries if the device battery level is low; returning a value of true will cause a low-battery + * status to appear in Home for the device. + * + * @return a future that will contain the accessory's low battery state + */ + CompletableFuture getLowBatteryState(); - /** - * Subscribes to changes in the battery level. - * - * @param callback the function to call when low battery state changes. - */ - void subscribeLowBatteryState(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes in the battery level. + * + * @param callback the function to call when low battery state changes. + */ + void subscribeLowBatteryState(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the low battery state. - */ - void unsubscribeLowBatteryState(); + /** Unsubscribes from changes in the low battery state. */ + void unsubscribeLowBatteryState(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/CarbonMonoxideSensor.java b/src/main/java/com/beowulfe/hap/accessories/CarbonMonoxideSensor.java index 545a6b22a..85bf73ac4 100644 --- a/src/main/java/com/beowulfe/hap/accessories/CarbonMonoxideSensor.java +++ b/src/main/java/com/beowulfe/hap/accessories/CarbonMonoxideSensor.java @@ -5,42 +5,39 @@ import com.beowulfe.hap.Service; import com.beowulfe.hap.accessories.properties.CarbonMonoxideDetectedState; import com.beowulfe.hap.impl.services.CarbonMonoxideSensorService; - import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; /** - *

A carbon monoxide sensor reports whether carbon monoxide has been detected or not.

+ * A carbon monoxide sensor reports whether carbon monoxide has been detected or not. * - *

Carbon monoxide sensors that run on batteries will need to implement this interface - * and also implement {@link BatteryStatusAccessory}.

+ *

Carbon monoxide sensors that run on batteries will need to implement this interface and also + * implement {@link BatteryStatusAccessory}. * * @author Gaston Dombiak */ public interface CarbonMonoxideSensor extends HomekitAccessory { - /** - * Retrieves the state of the sensor that indicates if carbon monoxide has been detected. - * - * @return a future that will contain the carbon monoxide sensor's state - */ - CompletableFuture getCarbonMonoxideDetectedState(); + /** + * Retrieves the state of the sensor that indicates if carbon monoxide has been detected. + * + * @return a future that will contain the carbon monoxide sensor's state + */ + CompletableFuture getCarbonMonoxideDetectedState(); - @Override - default Collection getServices() { - return Collections.singleton(new CarbonMonoxideSensorService(this)); - } + @Override + default Collection getServices() { + return Collections.singleton(new CarbonMonoxideSensorService(this)); + } - /** - * Subscribes to changes in the carbon monoxide's state. - * - * @param callback the function to call when the state changes. - */ - void subscribeCarbonMonoxideDetectedState(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes in the carbon monoxide's state. + * + * @param callback the function to call when the state changes. + */ + void subscribeCarbonMonoxideDetectedState(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the carbon monoxide's state. - */ - void unsubscribeCarbonMonoxideDetectedState(); + /** Unsubscribes from changes in the carbon monoxide's state. */ + void unsubscribeCarbonMonoxideDetectedState(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/ColorfulLightbulb.java b/src/main/java/com/beowulfe/hap/accessories/ColorfulLightbulb.java index 6d971502a..d218bece4 100644 --- a/src/main/java/com/beowulfe/hap/accessories/ColorfulLightbulb.java +++ b/src/main/java/com/beowulfe/hap/accessories/ColorfulLightbulb.java @@ -1,67 +1,65 @@ package com.beowulfe.hap.accessories; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; /** - * Extends {@link Lightbulb} with color settings. This will usually be implemented along with - * {@link DimmableLightbulb}, but not necessarily so. + * Extends {@link Lightbulb} with color settings. This will usually be implemented along with {@link + * DimmableLightbulb}, but not necessarily so. * * @author Andy Lintner */ public interface ColorfulLightbulb extends Lightbulb { - /** - * Retrieves the current hue of the light. - * - * @return a future that will contain the hue, expressed in arc degrees from 0 to 360. - */ - CompletableFuture getHue(); - - /** - * Sets the current hue of the light - * @param value the hue to set, expressed in arc degrees from 0 to 360. - * @return a future that completes when the hue is changed - * @throws Exception when the hue cannot be changed. - */ - CompletableFuture setHue(Double value) throws Exception; - - /** - * Subscribes to changes in the hue of the light. - * @param callback the function to call when the state changes. - */ - void subscribeHue(HomekitCharacteristicChangeCallback callback); - - /** - * Unsubscribes from changes in the hue of the light. - */ - void unsubscribeHue(); - - /** - * Retrieves the saturation of the light. - * - * @return a future that will contain the saturation, expressed as a value between 0 and 100. - */ - CompletableFuture getSaturation(); - - /** - * Sets the saturation of the light. - * @param value the saturation to set, expressed as a value between 0 and 100. - * @return a future that completes when the saturation is changed. - * @throws Exception when the saturation cannot be set. - */ - CompletableFuture setSaturation(Double value) throws Exception; - - /** - * Subscribes to changes in the saturation of the light. - * @param callback the function to call when the state changes. - */ - void subscribeSaturation(HomekitCharacteristicChangeCallback callback); - - /** - * Unsubscribes from changes in the saturation of the light. - */ - void unsubscribeSaturation(); - + /** + * Retrieves the current hue of the light. + * + * @return a future that will contain the hue, expressed in arc degrees from 0 to 360. + */ + CompletableFuture getHue(); + + /** + * Sets the current hue of the light + * + * @param value the hue to set, expressed in arc degrees from 0 to 360. + * @return a future that completes when the hue is changed + * @throws Exception when the hue cannot be changed. + */ + CompletableFuture setHue(Double value) throws Exception; + + /** + * Subscribes to changes in the hue of the light. + * + * @param callback the function to call when the state changes. + */ + void subscribeHue(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the hue of the light. */ + void unsubscribeHue(); + + /** + * Retrieves the saturation of the light. + * + * @return a future that will contain the saturation, expressed as a value between 0 and 100. + */ + CompletableFuture getSaturation(); + + /** + * Sets the saturation of the light. + * + * @param value the saturation to set, expressed as a value between 0 and 100. + * @return a future that completes when the saturation is changed. + * @throws Exception when the saturation cannot be set. + */ + CompletableFuture setSaturation(Double value) throws Exception; + + /** + * Subscribes to changes in the saturation of the light. + * + * @param callback the function to call when the state changes. + */ + void subscribeSaturation(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the saturation of the light. */ + void unsubscribeSaturation(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/ContactSensor.java b/src/main/java/com/beowulfe/hap/accessories/ContactSensor.java index 4f3b6bceb..4078278df 100644 --- a/src/main/java/com/beowulfe/hap/accessories/ContactSensor.java +++ b/src/main/java/com/beowulfe/hap/accessories/ContactSensor.java @@ -5,45 +5,41 @@ import com.beowulfe.hap.Service; import com.beowulfe.hap.accessories.properties.ContactState; import com.beowulfe.hap.impl.services.ContactSensorService; - import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; /** - *

A contact sensor that reports whether contact is detected or not. Typical - * contact sensors are window/door sensors. When contact is detected it means - * that the door/window is closed.

+ * A contact sensor that reports whether contact is detected or not. Typical contact sensors are + * window/door sensors. When contact is detected it means that the door/window is closed. * - *

Contact sensors that run on batteries will need to implement this interface - * and also implement {@link BatteryStatusAccessory}.

+ *

Contact sensors that run on batteries will need to implement this interface and also implement + * {@link BatteryStatusAccessory}. * * @author Gaston Dombiak */ public interface ContactSensor extends HomekitAccessory { - /** - * Retrieves the state of the contact. This is whether the contact is detected or not. - * Detected contact means door/window is closed. - * - * @return a future that will contain the contact's state - */ - CompletableFuture getCurrentState(); + /** + * Retrieves the state of the contact. This is whether the contact is detected or not. Detected + * contact means door/window is closed. + * + * @return a future that will contain the contact's state + */ + CompletableFuture getCurrentState(); - @Override - default Collection getServices() { - return Collections.singleton(new ContactSensorService(this)); - } + @Override + default Collection getServices() { + return Collections.singleton(new ContactSensorService(this)); + } - /** - * Subscribes to changes in the contact state. - * - * @param callback the function to call when the state changes. - */ - void subscribeContactState(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes in the contact state. + * + * @param callback the function to call when the state changes. + */ + void subscribeContactState(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the contact state. - */ - void unsubscribeContactState(); + /** Unsubscribes from changes in the contact state. */ + void unsubscribeContactState(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/DimmableLightbulb.java b/src/main/java/com/beowulfe/hap/accessories/DimmableLightbulb.java index 77ae675bf..e83b592e5 100644 --- a/src/main/java/com/beowulfe/hap/accessories/DimmableLightbulb.java +++ b/src/main/java/com/beowulfe/hap/accessories/DimmableLightbulb.java @@ -1,8 +1,7 @@ package com.beowulfe.hap.accessories; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; /** * Extends {@link Lightbulb} with brightness values. @@ -11,29 +10,29 @@ */ public interface DimmableLightbulb extends Lightbulb { - /** - * Retrieves the current brightness of the light - * @return a future that will contain the brightness, expressed as an integer between 0 and 100. - */ - CompletableFuture getBrightness(); - - /** - * Sets the current brightness of the light - * @param value the brightness, on a scale of 0 to 100, to set - * @return a future that completes when the brightness is changed - * @throws Exception when the brightness cannot be set - */ - CompletableFuture setBrightness(Integer value) throws Exception; - - /** - * Subscribes to changes in the brightness of the light. - * @param callback the function to call when the state changes. - */ - void subscribeBrightness(HomekitCharacteristicChangeCallback callback); - - /** - * Unsubscribes from changes in the brightness of the light. - */ - void unsubscribeBrightness(); - + /** + * Retrieves the current brightness of the light + * + * @return a future that will contain the brightness, expressed as an integer between 0 and 100. + */ + CompletableFuture getBrightness(); + + /** + * Sets the current brightness of the light + * + * @param value the brightness, on a scale of 0 to 100, to set + * @return a future that completes when the brightness is changed + * @throws Exception when the brightness cannot be set + */ + CompletableFuture setBrightness(Integer value) throws Exception; + + /** + * Subscribes to changes in the brightness of the light. + * + * @param callback the function to call when the state changes. + */ + void subscribeBrightness(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the brightness of the light. */ + void unsubscribeBrightness(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/Fan.java b/src/main/java/com/beowulfe/hap/accessories/Fan.java index 37a2871d7..fe1acbaae 100644 --- a/src/main/java/com/beowulfe/hap/accessories/Fan.java +++ b/src/main/java/com/beowulfe/hap/accessories/Fan.java @@ -1,12 +1,11 @@ package com.beowulfe.hap.accessories; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.*; import com.beowulfe.hap.accessories.properties.RotationDirection; import com.beowulfe.hap.impl.services.FanService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; /** * A fan, with power and rotational characteristics. @@ -15,84 +14,86 @@ */ public interface Fan extends HomekitAccessory { - /** - * Retrieves the current binary state of the fan's power. - * @return a future that will contain the binary state - */ - CompletableFuture getFanPower(); - - /** - * Retrieves the current rotation direction of the fan. - * @return a future that will contain the direction - */ - CompletableFuture getRotationDirection(); - - /** - * Retrieves the current speed of the fan's rotation - * @return a future that will contain the speed, expressed as an integer between 0 and 100. - */ - CompletableFuture getRotationSpeed(); - - /** - * Sets the binary state of the fan's power - * @param state the binary state to set - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setFanPower(boolean state) throws Exception; - - /** - * Sets the rotation direction of the fan - * @param direction the direction to set - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setRotationDirection(RotationDirection direction) throws Exception; - - - /** - * Sets the speed of the fan's rotation - * @param speed the speed to set, expressed as an integer between 0 and 100. - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setRotationSpeed(Integer speed) throws Exception; - - @Override - default Collection getServices() { - return Collections.singleton(new FanService(this)); - } - - /** - * Subscribes to changes in the binary state of the fan's power. - * @param callback the function to call when the state changes. - */ - void subscribeFanPower(HomekitCharacteristicChangeCallback callback); - - /** - * Subscribes to changes in the rotation direction of the fan. - * @param callback the function to call when the direction changes. - */ - void subscribeRotationDirection(HomekitCharacteristicChangeCallback callback); - - /** - * Subscribes to changes in the rotation speed of the fan. - * @param callback the function to call when the speed changes. - */ - void subscribeRotationSpeed(HomekitCharacteristicChangeCallback callback); - - /** - * Unsubscribes from changes in the binary state of the fan's power. - */ - void unsubscribeFanPower(); - - /** - * Unsubscribes from changes in the rotation direction of the fan. - */ - void unsubscribeRotationDirection(); - - /** - * Unsubscribes from changes in the fan's rotation speed. - */ - void unsubscribeRotationSpeed(); + /** + * Retrieves the current binary state of the fan's power. + * + * @return a future that will contain the binary state + */ + CompletableFuture getFanPower(); + + /** + * Retrieves the current rotation direction of the fan. + * + * @return a future that will contain the direction + */ + CompletableFuture getRotationDirection(); + + /** + * Retrieves the current speed of the fan's rotation + * + * @return a future that will contain the speed, expressed as an integer between 0 and 100. + */ + CompletableFuture getRotationSpeed(); + + /** + * Sets the binary state of the fan's power + * + * @param state the binary state to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setFanPower(boolean state) throws Exception; + + /** + * Sets the rotation direction of the fan + * + * @param direction the direction to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setRotationDirection(RotationDirection direction) throws Exception; + + /** + * Sets the speed of the fan's rotation + * + * @param speed the speed to set, expressed as an integer between 0 and 100. + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setRotationSpeed(Integer speed) throws Exception; + + @Override + default Collection getServices() { + return Collections.singleton(new FanService(this)); + } + + /** + * Subscribes to changes in the binary state of the fan's power. + * + * @param callback the function to call when the state changes. + */ + void subscribeFanPower(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the rotation direction of the fan. + * + * @param callback the function to call when the direction changes. + */ + void subscribeRotationDirection(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the rotation speed of the fan. + * + * @param callback the function to call when the speed changes. + */ + void subscribeRotationSpeed(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the binary state of the fan's power. */ + void unsubscribeFanPower(); + + /** Unsubscribes from changes in the rotation direction of the fan. */ + void unsubscribeRotationDirection(); + + /** Unsubscribes from changes in the fan's rotation speed. */ + void unsubscribeRotationSpeed(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/GarageDoor.java b/src/main/java/com/beowulfe/hap/accessories/GarageDoor.java index 43dbb8b46..9197516cd 100644 --- a/src/main/java/com/beowulfe/hap/accessories/GarageDoor.java +++ b/src/main/java/com/beowulfe/hap/accessories/GarageDoor.java @@ -1,12 +1,11 @@ package com.beowulfe.hap.accessories; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.*; import com.beowulfe.hap.accessories.properties.DoorState; import com.beowulfe.hap.impl.services.GarageDoorService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; /** * A garage door opener, with control and status of a garage door @@ -15,68 +14,68 @@ */ public interface GarageDoor extends HomekitAccessory { - /** - * Retrieves the current state of the door - * @return a future which will contain the door's state - */ - CompletableFuture getCurrentDoorState(); - - /** - * Retrieves the targeted state of the door - * @return a future which will contain the door's targeted state - */ - CompletableFuture getTargetDoorState(); - - /** - * Retrieves an indicator of an obstruction detected by the door - * @return a future which will contain the indicator - */ - CompletableFuture getObstructionDetected(); - - /** - * Sets the targeted state of the door. - * @param state the targeted state - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setTargetDoorState(DoorState state) throws Exception; + /** + * Retrieves the current state of the door + * + * @return a future which will contain the door's state + */ + CompletableFuture getCurrentDoorState(); + + /** + * Retrieves the targeted state of the door + * + * @return a future which will contain the door's targeted state + */ + CompletableFuture getTargetDoorState(); + + /** + * Retrieves an indicator of an obstruction detected by the door + * + * @return a future which will contain the indicator + */ + CompletableFuture getObstructionDetected(); + + /** + * Sets the targeted state of the door. + * + * @param state the targeted state + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setTargetDoorState(DoorState state) throws Exception; + + /** + * Subscribes to changes in the door's state + * + * @param callback the function to call when the state changes + */ + void subscribeCurrentDoorState(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the door's targeted state + * + * @param callback the function to call when the targeted state changes + */ + void subscribeTargetDoorState(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the obstruction detected indicator + * + * @param callback the function to call when the indicator chnages + */ + void subscribeObstructionDetected(HomekitCharacteristicChangeCallback callback); - /** - * Subscribes to changes in the door's state - * @param callback the function to call when the state changes - */ - void subscribeCurrentDoorState(HomekitCharacteristicChangeCallback callback); - - /** - * Subscribes to changes in the door's targeted state - * @param callback the function to call when the targeted state changes - */ - void subscribeTargetDoorState(HomekitCharacteristicChangeCallback callback); - - /** - * Subscribes to changes in the obstruction detected indicator - * @param callback the function to call when the indicator chnages - */ - void subscribeObstructionDetected(HomekitCharacteristicChangeCallback callback); + /** Unsubscribes from changes in the door's state */ + void unsubscribeCurrentDoorState(); - /** - * Unsubscribes from changes in the door's state - */ - void unsubscribeCurrentDoorState(); - - /** - * Unsubscribes from changes in the door's targeted state - */ - void unsubscribeTargetDoorState(); - - /** - * Unsubscribes from changes in the door's obstruction detected indicator - */ - void unsubscribeObstructionDetected(); + /** Unsubscribes from changes in the door's targeted state */ + void unsubscribeTargetDoorState(); + /** Unsubscribes from changes in the door's obstruction detected indicator */ + void unsubscribeObstructionDetected(); - @Override - default Collection getServices() { - return Collections.singleton(new GarageDoorService(this)); - } + @Override + default Collection getServices() { + return Collections.singleton(new GarageDoorService(this)); + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/HorizontalTiltingWindowCovering.java b/src/main/java/com/beowulfe/hap/accessories/HorizontalTiltingWindowCovering.java index abfaf4547..5835a7bd8 100644 --- a/src/main/java/com/beowulfe/hap/accessories/HorizontalTiltingWindowCovering.java +++ b/src/main/java/com/beowulfe/hap/accessories/HorizontalTiltingWindowCovering.java @@ -1,8 +1,7 @@ package com.beowulfe.hap.accessories; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; /** * Extends WindowCovering with the ability to control horizontal tilt angles @@ -11,45 +10,46 @@ */ public interface HorizontalTiltingWindowCovering extends WindowCovering { - /** - * Retrieves the current horizontal tilt angle - * @return a future that will contain the position as a value between -90 and 90 - */ - CompletableFuture getCurrentHorizontalTiltAngle(); - - /** - * Retrieves the target horizontal tilt angle - * @return a future that will contain the target position as a value between -90 and 90 - */ - CompletableFuture getTargetHorizontalTiltAngle(); - - /** - * Sets the target position - * @param angle the target angle to set, as a value between -90 and 90 - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setTargetHorizontalTiltAngle(int angle) throws Exception; - - /** - * Subscribes to changes in the current horizontal tilt angle. - * @param callback the function to call when the state changes. - */ - void subscribeCurrentHorizontalTiltAngle(HomekitCharacteristicChangeCallback callback); - - /** - * Subscribes to changes in the target horizontal tilt angle. - * @param callback the function to call when the state changes. - */ - void subscribeTargetHorizontalTiltAngle(HomekitCharacteristicChangeCallback callback); - - /** - * Unsubscribes from changes in the current horizontal tilt angle - */ - void unsubscribeCurrentHorizontalTiltAngle(); - - /** - * Unsubscribes from changes in the target horizontal tilt angle - */ - void unsubscribeTargetHorizontalTiltAngle(); + /** + * Retrieves the current horizontal tilt angle + * + * @return a future that will contain the position as a value between -90 and 90 + */ + CompletableFuture getCurrentHorizontalTiltAngle(); + + /** + * Retrieves the target horizontal tilt angle + * + * @return a future that will contain the target position as a value between -90 and 90 + */ + CompletableFuture getTargetHorizontalTiltAngle(); + + /** + * Sets the target position + * + * @param angle the target angle to set, as a value between -90 and 90 + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setTargetHorizontalTiltAngle(int angle) throws Exception; + + /** + * Subscribes to changes in the current horizontal tilt angle. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentHorizontalTiltAngle(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the target horizontal tilt angle. + * + * @param callback the function to call when the state changes. + */ + void subscribeTargetHorizontalTiltAngle(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current horizontal tilt angle */ + void unsubscribeCurrentHorizontalTiltAngle(); + + /** Unsubscribes from changes in the target horizontal tilt angle */ + void unsubscribeTargetHorizontalTiltAngle(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/HumiditySensor.java b/src/main/java/com/beowulfe/hap/accessories/HumiditySensor.java index b05bf209a..0401ee582 100644 --- a/src/main/java/com/beowulfe/hap/accessories/HumiditySensor.java +++ b/src/main/java/com/beowulfe/hap/accessories/HumiditySensor.java @@ -1,12 +1,11 @@ package com.beowulfe.hap.accessories; +import com.beowulfe.hap.*; +import com.beowulfe.hap.impl.services.HumiditySensorService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; -import com.beowulfe.hap.*; -import com.beowulfe.hap.impl.services.HumiditySensorService; - /** * A humidity sensor that reports the current relative humidity. * @@ -14,26 +13,25 @@ */ public interface HumiditySensor extends HomekitAccessory { - /** - * Retrieves the current relative humidity. - * @return a future that will contain the humidity as a value between 0 and 100 - */ - CompletableFuture getCurrentRelativeHumidity(); - - @Override - default Collection getServices() { - return Collections.singleton(new HumiditySensorService(this)); - } - - /** - * Subscribes to changes in the current relative humidity. - * @param callback the function to call when the state changes. - */ - void subscribeCurrentRelativeHumidity(HomekitCharacteristicChangeCallback callback); + /** + * Retrieves the current relative humidity. + * + * @return a future that will contain the humidity as a value between 0 and 100 + */ + CompletableFuture getCurrentRelativeHumidity(); + + @Override + default Collection getServices() { + return Collections.singleton(new HumiditySensorService(this)); + } + + /** + * Subscribes to changes in the current relative humidity. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentRelativeHumidity(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the current relative humidity. - */ - void unsubscribeCurrentRelativeHumidity(); - + /** Unsubscribes from changes in the current relative humidity. */ + void unsubscribeCurrentRelativeHumidity(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/LeakSensor.java b/src/main/java/com/beowulfe/hap/accessories/LeakSensor.java index 11443f155..9de42a52c 100644 --- a/src/main/java/com/beowulfe/hap/accessories/LeakSensor.java +++ b/src/main/java/com/beowulfe/hap/accessories/LeakSensor.java @@ -1,49 +1,42 @@ package com.beowulfe.hap.accessories; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitAccessory; import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.Service; import com.beowulfe.hap.impl.services.LeakSensorService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; /** - *

* A leak sensor that reports whether a leak has been detected. - *

* - *

- * Leak sensors that run on batteries will need to implement this interface - * and also implement {@link BatteryStatusAccessory}. - *

+ *

Leak sensors that run on batteries will need to implement this interface and also implement + * {@link BatteryStatusAccessory}. * * @author Tim Harper */ public interface LeakSensor extends HomekitAccessory { - /** - * Retrieves the state of the leak sensor. If true then leak has been detected. - * - * @return a future that will contain the leak sensor's state - */ - CompletableFuture getLeakDetected(); + /** + * Retrieves the state of the leak sensor. If true then leak has been detected. + * + * @return a future that will contain the leak sensor's state + */ + CompletableFuture getLeakDetected(); - @Override - default Collection getServices() { - return Collections.singleton(new LeakSensorService(this)); - } + @Override + default Collection getServices() { + return Collections.singleton(new LeakSensorService(this)); + } - /** - * Subscribes to changes in the leak sensor. - * - * @param callback the function to call when the state changes. - */ - void subscribeLeakDetected(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes in the leak sensor. + * + * @param callback the function to call when the state changes. + */ + void subscribeLeakDetected(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the leak sensor. - */ - void unsubscribeLeakDetected(); + /** Unsubscribes from changes in the leak sensor. */ + void unsubscribeLeakDetected(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/LightSensor.java b/src/main/java/com/beowulfe/hap/accessories/LightSensor.java index d9f7f5c65..7d16f406e 100644 --- a/src/main/java/com/beowulfe/hap/accessories/LightSensor.java +++ b/src/main/java/com/beowulfe/hap/accessories/LightSensor.java @@ -4,7 +4,6 @@ import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.Service; import com.beowulfe.hap.impl.services.LightSensorService; - import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; @@ -16,27 +15,25 @@ */ public interface LightSensor extends HomekitAccessory { - /** - * Retrieves the current ambient light level. - * - * @return a future that will contain the luminance level expressed in LUX. - */ - CompletableFuture getCurrentAmbientLightLevel(); + /** + * Retrieves the current ambient light level. + * + * @return a future that will contain the luminance level expressed in LUX. + */ + CompletableFuture getCurrentAmbientLightLevel(); - @Override - default Collection getServices() { - return Collections.singleton(new LightSensorService(this)); - } + @Override + default Collection getServices() { + return Collections.singleton(new LightSensorService(this)); + } - /** - * Subscribes to changes in the current ambient light level. - * - * @param callback the function to call when the state changes. - */ - void subscribeCurrentAmbientLightLevel(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes in the current ambient light level. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentAmbientLightLevel(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the current ambient light level. - */ - void unsubscribeCurrentAmbientLightLevel(); + /** Unsubscribes from changes in the current ambient light level. */ + void unsubscribeCurrentAmbientLightLevel(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/Lightbulb.java b/src/main/java/com/beowulfe/hap/accessories/Lightbulb.java index e60fe3a4e..58f350618 100644 --- a/src/main/java/com/beowulfe/hap/accessories/Lightbulb.java +++ b/src/main/java/com/beowulfe/hap/accessories/Lightbulb.java @@ -1,12 +1,11 @@ package com.beowulfe.hap.accessories; +import com.beowulfe.hap.*; +import com.beowulfe.hap.impl.services.LightbulbService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; -import com.beowulfe.hap.*; -import com.beowulfe.hap.impl.services.LightbulbService; - /** * A simple light with a binary state. * @@ -14,33 +13,34 @@ */ public interface Lightbulb extends HomekitAccessory { - /** - * Retrieves the current binary state of the light. - * @return a future that will contain the binary state - */ - CompletableFuture getLightbulbPowerState(); - - /** - * Sets the binary state of the light - * @param powerState the binary state to set - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setLightbulbPowerState(boolean powerState) throws Exception; - - @Override - default Collection getServices() { - return Collections.singleton(new LightbulbService(this)); - } - - /** - * Subscribes to changes in the binary state of the light. - * @param callback the function to call when the state changes. - */ - void subscribeLightbulbPowerState(HomekitCharacteristicChangeCallback callback); - - /** - * Unsubscribes from changes in the binary state of the light. - */ - void unsubscribeLightbulbPowerState(); + /** + * Retrieves the current binary state of the light. + * + * @return a future that will contain the binary state + */ + CompletableFuture getLightbulbPowerState(); + + /** + * Sets the binary state of the light + * + * @param powerState the binary state to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setLightbulbPowerState(boolean powerState) throws Exception; + + @Override + default Collection getServices() { + return Collections.singleton(new LightbulbService(this)); + } + + /** + * Subscribes to changes in the binary state of the light. + * + * @param callback the function to call when the state changes. + */ + void subscribeLightbulbPowerState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the binary state of the light. */ + void unsubscribeLightbulbPowerState(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/LockMechanism.java b/src/main/java/com/beowulfe/hap/accessories/LockMechanism.java index 320d5dc0b..5f0fca812 100644 --- a/src/main/java/com/beowulfe/hap/accessories/LockMechanism.java +++ b/src/main/java/com/beowulfe/hap/accessories/LockMechanism.java @@ -5,41 +5,40 @@ import com.beowulfe.hap.Service; import com.beowulfe.hap.accessories.properties.LockMechanismState; import com.beowulfe.hap.impl.services.LockMechanismService; - import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; /** - *

A lock capable of exposing its binary locked state. For a lock that can be locked/unlocked, use - * {@link LockableLockMechanism}.

+ * A lock capable of exposing its binary locked state. For a lock that can be locked/unlocked, use + * {@link LockableLockMechanism}. * - *

Locks that run on batteries will need to implement this interface and also - * implement {@link BatteryStatusAccessory}.

+ *

Locks that run on batteries will need to implement this interface and also implement {@link + * BatteryStatusAccessory}. * * @author Andy Lintner */ public interface LockMechanism extends HomekitAccessory { - /** - * Retrieves the current binary state of the lock. - * @return a future that will contain the binary state. - */ - CompletableFuture getCurrentMechanismState(); + /** + * Retrieves the current binary state of the lock. + * + * @return a future that will contain the binary state. + */ + CompletableFuture getCurrentMechanismState(); - /** - * Subscribes to changes in the binary state of the lock. - * @param callback the function to call when the state changes. - */ - void subscribeCurrentMechanismState(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes in the binary state of the lock. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentMechanismState(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the binary state of the lock. - */ - void unsubscribeCurrentMechanismState(); + /** Unsubscribes from changes in the binary state of the lock. */ + void unsubscribeCurrentMechanismState(); - @Override - default Collection getServices() { - return Collections.singleton(new LockMechanismService(this)); - } + @Override + default Collection getServices() { + return Collections.singleton(new LockMechanismService(this)); + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/LockableLockMechanism.java b/src/main/java/com/beowulfe/hap/accessories/LockableLockMechanism.java index 50affacad..ef55e4d00 100644 --- a/src/main/java/com/beowulfe/hap/accessories/LockableLockMechanism.java +++ b/src/main/java/com/beowulfe/hap/accessories/LockableLockMechanism.java @@ -1,9 +1,8 @@ package com.beowulfe.hap.accessories; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.properties.LockMechanismState; +import java.util.concurrent.CompletableFuture; /** * Extends {@link LockMechanism} with the ability to lock and unlock the mechanism. @@ -12,28 +11,28 @@ */ public interface LockableLockMechanism extends LockMechanism { - /** - * Sets the binary state of the lock mechanism. - * - * @param state true for a locked mechanism, false for unlocked. - * @throws Exception when the change cannot be made. - */ - void setTargetMechanismState(LockMechanismState state) throws Exception; + /** + * Sets the binary state of the lock mechanism. + * + * @param state true for a locked mechanism, false for unlocked. + * @throws Exception when the change cannot be made. + */ + void setTargetMechanismState(LockMechanismState state) throws Exception; - /** - * Retrieves the pending, but not yet completed, state of the lock mechanism. - * @return the binary state - */ - CompletableFuture getTargetMechanismState(); + /** + * Retrieves the pending, but not yet completed, state of the lock mechanism. + * + * @return the binary state + */ + CompletableFuture getTargetMechanismState(); - /** - * Subscribes to changes in the pending, but not yet completed, binary state. - * @param callback the function to call when the state changes. - */ - void subscribeTargetMechanismState(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes in the pending, but not yet completed, binary state. + * + * @param callback the function to call when the state changes. + */ + void subscribeTargetMechanismState(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the pending, but not yet completed, binary state. - */ - void unsubscribeTargetMechanismState(); + /** Unsubscribes from changes in the pending, but not yet completed, binary state. */ + void unsubscribeTargetMechanismState(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/MotionSensor.java b/src/main/java/com/beowulfe/hap/accessories/MotionSensor.java index ee13e839c..41bd4ccbe 100644 --- a/src/main/java/com/beowulfe/hap/accessories/MotionSensor.java +++ b/src/main/java/com/beowulfe/hap/accessories/MotionSensor.java @@ -4,42 +4,39 @@ import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.Service; import com.beowulfe.hap.impl.services.MotionSensorService; - import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; /** - *

A motion sensor that reports whether motion has been detected.

+ * A motion sensor that reports whether motion has been detected. * - *

Motion sensors that run on batteries will need to implement this interface - * and also implement {@link BatteryStatusAccessory}.

+ *

Motion sensors that run on batteries will need to implement this interface and also implement + * {@link BatteryStatusAccessory}. * * @author Gaston Dombiak */ public interface MotionSensor extends HomekitAccessory { - /** - * Retrieves the state of the motion sensor. If true then motion has been detected. - * - * @return a future that will contain the motion sensor's state - */ - CompletableFuture getMotionDetected(); + /** + * Retrieves the state of the motion sensor. If true then motion has been detected. + * + * @return a future that will contain the motion sensor's state + */ + CompletableFuture getMotionDetected(); - @Override - default Collection getServices() { - return Collections.singleton(new MotionSensorService(this)); - } + @Override + default Collection getServices() { + return Collections.singleton(new MotionSensorService(this)); + } - /** - * Subscribes to changes in the motion sensor. - * - * @param callback the function to call when the state changes. - */ - void subscribeMotionDetected(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes in the motion sensor. + * + * @param callback the function to call when the state changes. + */ + void subscribeMotionDetected(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the motion sensor. - */ - void unsubscribeMotionDetected(); + /** Unsubscribes from changes in the motion sensor. */ + void unsubscribeMotionDetected(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/Outlet.java b/src/main/java/com/beowulfe/hap/accessories/Outlet.java index 443acabe2..296c21a49 100644 --- a/src/main/java/com/beowulfe/hap/accessories/Outlet.java +++ b/src/main/java/com/beowulfe/hap/accessories/Outlet.java @@ -1,12 +1,11 @@ package com.beowulfe.hap.accessories; +import com.beowulfe.hap.*; +import com.beowulfe.hap.impl.services.OutletService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; -import com.beowulfe.hap.*; -import com.beowulfe.hap.impl.services.OutletService; - /** * A power outlet with boolean power and usage states. * @@ -14,51 +13,51 @@ */ public interface Outlet extends HomekitAccessory { - @Override - default Collection getServices() { - return Collections.singleton(new OutletService(this)); - } - - /** - * Retrieves the current binary state of the outlet's power. - * @return a future that will contain the binary state - */ - CompletableFuture getPowerState(); - - /** - * Retrieves the current binary state indicating whether the outlet is in use. - * @return a future that will contain the binary state - */ - CompletableFuture getOutletInUse(); - - /** - * Sets the binary state of the outlet's power. - * @param state the binary state to set - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setPowerState(boolean state) throws Exception; - - /** - * Subscribes to changes in the binary state of the outlet's power. - * @param callback the function to call when the state changes. - */ - void subscribePowerState(HomekitCharacteristicChangeCallback callback); - - /** - * Subscribes to changes in the binary state indicating whether the outlet is in use. - * @param callback the function to call when the state changes. - */ - void subscribeOutletInUse(HomekitCharacteristicChangeCallback callback); - - /** - * Unsubscribes from changes in the binary state of the outlet's power. - */ - void unsubscribePowerState(); - - /** - * Unsubscribes from changes in the binary state indicating whether hte outlet is in use. - */ - void unsubscribeOutletInUse(); - + @Override + default Collection getServices() { + return Collections.singleton(new OutletService(this)); + } + + /** + * Retrieves the current binary state of the outlet's power. + * + * @return a future that will contain the binary state + */ + CompletableFuture getPowerState(); + + /** + * Retrieves the current binary state indicating whether the outlet is in use. + * + * @return a future that will contain the binary state + */ + CompletableFuture getOutletInUse(); + + /** + * Sets the binary state of the outlet's power. + * + * @param state the binary state to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setPowerState(boolean state) throws Exception; + + /** + * Subscribes to changes in the binary state of the outlet's power. + * + * @param callback the function to call when the state changes. + */ + void subscribePowerState(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the binary state indicating whether the outlet is in use. + * + * @param callback the function to call when the state changes. + */ + void subscribeOutletInUse(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the binary state of the outlet's power. */ + void unsubscribePowerState(); + + /** Unsubscribes from changes in the binary state indicating whether hte outlet is in use. */ + void unsubscribeOutletInUse(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/SecuritySystem.java b/src/main/java/com/beowulfe/hap/accessories/SecuritySystem.java index 096937b25..86c274599 100644 --- a/src/main/java/com/beowulfe/hap/accessories/SecuritySystem.java +++ b/src/main/java/com/beowulfe/hap/accessories/SecuritySystem.java @@ -7,90 +7,84 @@ import com.beowulfe.hap.accessories.properties.SecuritySystemAlarmType; import com.beowulfe.hap.accessories.properties.TargetSecuritySystemState; import com.beowulfe.hap.impl.services.SecuritySystemService; - import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; /** - *

A security system that can be armed so that when a contact sensor is opened or a motion - * sensor detects movement, then a siren could be fired off. There are different modes for arming - * the system. See {@link TargetSecuritySystemState} for more information.

+ * A security system that can be armed so that when a contact sensor is opened or a motion sensor + * detects movement, then a siren could be fired off. There are different modes for arming the + * system. See {@link TargetSecuritySystemState} for more information. * * @author Gaston Dombiak */ public interface SecuritySystem extends HomekitAccessory { - /** - * Retrieves the current state of the security system. The state describes if the system - * is armed in any of its variations; or if the alarm has been triggered; or if the system - * is disarmed. - * - * @return current state of the security system. - */ - CompletableFuture getCurrentSecuritySystemState(); + /** + * Retrieves the current state of the security system. The state describes if the system is armed + * in any of its variations; or if the alarm has been triggered; or if the system is disarmed. + * + * @return current state of the security system. + */ + CompletableFuture getCurrentSecuritySystemState(); - /** - * Subscribes to changes to the state of the security system. - * - * @param callback the function to call when the state changes. - */ - void subscribeCurrentSecuritySystemState(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes to the state of the security system. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentSecuritySystemState(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the state of the security system. - */ - void unsubscribeCurrentSecuritySystemState(); + /** Unsubscribes from changes in the state of the security system. */ + void unsubscribeCurrentSecuritySystemState(); - /** - * Sets the state of the security system. The security system could be armed in any - * of its variations or disarmed. - * - * @param state target state of the security system. - * @throws Exception when the change cannot be made. - */ - void setTargetSecuritySystemState(TargetSecuritySystemState state) throws Exception; + /** + * Sets the state of the security system. The security system could be armed in any of its + * variations or disarmed. + * + * @param state target state of the security system. + * @throws Exception when the change cannot be made. + */ + void setTargetSecuritySystemState(TargetSecuritySystemState state) throws Exception; - /** - * Retrieves the pending, but not yet completed, state of the security system. - * - * @return target state of the security system. - */ - CompletableFuture getTargetSecuritySystemState(); + /** + * Retrieves the pending, but not yet completed, state of the security system. + * + * @return target state of the security system. + */ + CompletableFuture getTargetSecuritySystemState(); - /** - * Subscribes to changes in the pending, but not yet completed, state of the security system. - * - * @param callback the function to call when the state changes. - */ - void subscribeTargetSecuritySystemState(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes in the pending, but not yet completed, state of the security system. + * + * @param callback the function to call when the state changes. + */ + void subscribeTargetSecuritySystemState(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the pending, but not yet completed, state of the security system. - */ - void unsubscribeTargetSecuritySystemState(); + /** + * Unsubscribes from changes in the pending, but not yet completed, state of the security system. + */ + void unsubscribeTargetSecuritySystemState(); - /** - * Retrieves the alarm type of the security system. - * - * @return alarm type of the security system. - */ - CompletableFuture getAlarmTypeState(); + /** + * Retrieves the alarm type of the security system. + * + * @return alarm type of the security system. + */ + CompletableFuture getAlarmTypeState(); - /** - * Subscribes to changes to the alarm type of the security system. - * - * @param callback the function to call when the alarm type changes. - */ - void subscribeAlarmTypeState(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes to the alarm type of the security system. + * + * @param callback the function to call when the alarm type changes. + */ + void subscribeAlarmTypeState(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the alarm type of the security system. - */ - void unsubscribeAlarmTypeState(); + /** Unsubscribes from changes in the alarm type of the security system. */ + void unsubscribeAlarmTypeState(); - @Override - default Collection getServices() { - return Collections.singleton(new SecuritySystemService(this)); - } + @Override + default Collection getServices() { + return Collections.singleton(new SecuritySystemService(this)); + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/SmokeSensor.java b/src/main/java/com/beowulfe/hap/accessories/SmokeSensor.java index 431e8ec7a..1fa4c6412 100644 --- a/src/main/java/com/beowulfe/hap/accessories/SmokeSensor.java +++ b/src/main/java/com/beowulfe/hap/accessories/SmokeSensor.java @@ -5,42 +5,39 @@ import com.beowulfe.hap.Service; import com.beowulfe.hap.accessories.properties.SmokeDetectedState; import com.beowulfe.hap.impl.services.SmokeSensorService; - import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; /** - *

A smoke sensor reports whether smoke has been detected or not.

+ * A smoke sensor reports whether smoke has been detected or not. * - *

Smoke sensors that run on batteries will need to implement this interface - * and also implement {@link BatteryStatusAccessory}.

+ *

Smoke sensors that run on batteries will need to implement this interface and also implement + * {@link BatteryStatusAccessory}. * * @author Gaston Dombiak */ public interface SmokeSensor extends HomekitAccessory { - /** - * Retrieves the state of the smoke sensor. This is whether smoke has been detected or not. - * - * @return a future that will contain the smoke sensor's state - */ - CompletableFuture getSmokeDetectedState(); + /** + * Retrieves the state of the smoke sensor. This is whether smoke has been detected or not. + * + * @return a future that will contain the smoke sensor's state + */ + CompletableFuture getSmokeDetectedState(); - @Override - default Collection getServices() { - return Collections.singleton(new SmokeSensorService(this)); - } + @Override + default Collection getServices() { + return Collections.singleton(new SmokeSensorService(this)); + } - /** - * Subscribes to changes in the smoke sensor's state. - * - * @param callback the function to call when the state changes. - */ - void subscribeSmokeDetectedState(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes in the smoke sensor's state. + * + * @param callback the function to call when the state changes. + */ + void subscribeSmokeDetectedState(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the smoke sensor's state. - */ - void unsubscribeSmokeDetectedState(); + /** Unsubscribes from changes in the smoke sensor's state. */ + void unsubscribeSmokeDetectedState(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/Switch.java b/src/main/java/com/beowulfe/hap/accessories/Switch.java index 8885a5caf..e34f4be16 100644 --- a/src/main/java/com/beowulfe/hap/accessories/Switch.java +++ b/src/main/java/com/beowulfe/hap/accessories/Switch.java @@ -1,12 +1,11 @@ package com.beowulfe.hap.accessories; +import com.beowulfe.hap.*; +import com.beowulfe.hap.impl.services.SwitchService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; -import com.beowulfe.hap.*; -import com.beowulfe.hap.impl.services.SwitchService; - /** * A simple switch with a binary state. * @@ -14,33 +13,34 @@ */ public interface Switch extends HomekitAccessory { - /** - * Retrieves the current binary state of the switch. - * @return a future that will contain the binary state - */ - CompletableFuture getSwitchState(); - - /** - * Sets the binary state of the switch - * @param state the binary state to set - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setSwitchState(boolean state) throws Exception; - - @Override - default Collection getServices() { - return Collections.singleton(new SwitchService(this)); - } - - /** - * Subscribes to changes in the binary state of the switch. - * @param callback the function to call when the state changes. - */ - void subscribeSwitchState(HomekitCharacteristicChangeCallback callback); - - /** - * Unsubscribes from changes in the binary state of the switch. - */ - void unsubscribeSwitchState(); + /** + * Retrieves the current binary state of the switch. + * + * @return a future that will contain the binary state + */ + CompletableFuture getSwitchState(); + + /** + * Sets the binary state of the switch + * + * @param state the binary state to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setSwitchState(boolean state) throws Exception; + + @Override + default Collection getServices() { + return Collections.singleton(new SwitchService(this)); + } + + /** + * Subscribes to changes in the binary state of the switch. + * + * @param callback the function to call when the state changes. + */ + void subscribeSwitchState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the binary state of the switch. */ + void unsubscribeSwitchState(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/TemperatureSensor.java b/src/main/java/com/beowulfe/hap/accessories/TemperatureSensor.java index ac3dd639d..1ec14d6f2 100644 --- a/src/main/java/com/beowulfe/hap/accessories/TemperatureSensor.java +++ b/src/main/java/com/beowulfe/hap/accessories/TemperatureSensor.java @@ -1,12 +1,11 @@ package com.beowulfe.hap.accessories; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.*; import com.beowulfe.hap.accessories.properties.TemperatureUnit; import com.beowulfe.hap.impl.services.TemperatureSensorService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; /** * A temperature sensor that reports the current temperature @@ -15,45 +14,50 @@ */ public interface TemperatureSensor extends HomekitAccessory { - /** - * Retrieves the current temperature, in celsius degrees. - * @return a future that will contain the temperature. - */ - CompletableFuture getCurrentTemperature(); - - @Override - default Collection getServices() { - return Collections.singleton(new TemperatureSensorService(this)); - } - - /** - * Subscribes to changes in the current temperature. - * @param callback the function to call when the state changes. - */ - void subscribeCurrentTemperature(HomekitCharacteristicChangeCallback callback); - - /** - * Unsubscribes from changes in the current temperature. - */ - void unsubscribeCurrentTemperature(); - - /** - * Retrieves the minimum temperature, in celsius degrees, the thermostat can be set to. - * @return the minimum temperature. - */ - double getMinimumTemperature(); - - /** - * Retrieves the maximum temperature, in celsius degrees, the thermostat can be set to. - * @return the maximum temperature. - */ - double getMaximumTemperature(); - - /** - * Retrieves the temperature unit of the thermostat. The impact of this is unclear, as the actual temperature - * is always communicated in celsius degrees, and the iOS device uses the user's locale to determine - * the unit to convert to. - * @return the temperature unit of the thermostat. - */ - default TemperatureUnit getTemperatureUnit() { return TemperatureUnit.CELSIUS; } + /** + * Retrieves the current temperature, in celsius degrees. + * + * @return a future that will contain the temperature. + */ + CompletableFuture getCurrentTemperature(); + + @Override + default Collection getServices() { + return Collections.singleton(new TemperatureSensorService(this)); + } + + /** + * Subscribes to changes in the current temperature. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentTemperature(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current temperature. */ + void unsubscribeCurrentTemperature(); + + /** + * Retrieves the minimum temperature, in celsius degrees, the thermostat can be set to. + * + * @return the minimum temperature. + */ + double getMinimumTemperature(); + + /** + * Retrieves the maximum temperature, in celsius degrees, the thermostat can be set to. + * + * @return the maximum temperature. + */ + double getMaximumTemperature(); + + /** + * Retrieves the temperature unit of the thermostat. The impact of this is unclear, as the actual + * temperature is always communicated in celsius degrees, and the iOS device uses the user's + * locale to determine the unit to convert to. + * + * @return the temperature unit of the thermostat. + */ + default TemperatureUnit getTemperatureUnit() { + return TemperatureUnit.CELSIUS; + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/Thermostat.java b/src/main/java/com/beowulfe/hap/accessories/Thermostat.java index 40f916e44..380f365ca 100644 --- a/src/main/java/com/beowulfe/hap/accessories/Thermostat.java +++ b/src/main/java/com/beowulfe/hap/accessories/Thermostat.java @@ -6,11 +6,8 @@ * A thermostat with heating and cooling controls. * * @author Andy Lintner - * @deprecated Use {@link BasicThermostat}, {@link HeatingThermostat}, and {@link CoolingThermostat} instead + * @deprecated Use {@link BasicThermostat}, {@link HeatingThermostat}, and {@link CoolingThermostat} + * instead */ @Deprecated -public interface Thermostat extends BasicThermostat, HeatingThermostat, CoolingThermostat { - - - -} +public interface Thermostat extends BasicThermostat, HeatingThermostat, CoolingThermostat {} diff --git a/src/main/java/com/beowulfe/hap/accessories/Valve.java b/src/main/java/com/beowulfe/hap/accessories/Valve.java index 13606e09d..aef689123 100644 --- a/src/main/java/com/beowulfe/hap/accessories/Valve.java +++ b/src/main/java/com/beowulfe/hap/accessories/Valve.java @@ -1,14 +1,13 @@ package com.beowulfe.hap.accessories; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitAccessory; import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.Service; import com.beowulfe.hap.accessories.properties.ValveType; import com.beowulfe.hap.impl.services.ValveService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; /** * A Valve (sprinkler head, faucet, etc.) @@ -17,83 +16,77 @@ */ public interface Valve extends HomekitAccessory { - @Override - default Collection getServices() { - return Collections.singleton(new ValveService(this)); - } + @Override + default Collection getServices() { + return Collections.singleton(new ValveService(this)); + } - /** - * Retrieves the current active state of the valve; Active could mean the valve is open (but not necessarily - * running), - * or that the valve is associated with an active watering program (like a watering program) but is not currently - * running. - * - * To communicate water is flowing through a valve, inUse should be used. - * - * @return a future that will contain the binary state - */ - CompletableFuture getValveActive(); + /** + * Retrieves the current active state of the valve; Active could mean the valve is open (but not + * necessarily running), or that the valve is associated with an active watering program (like a + * watering program) but is not currently running. + * + *

To communicate water is flowing through a valve, inUse should be used. + * + * @return a future that will contain the binary state + */ + CompletableFuture getValveActive(); - /** - * Sets the valve active state - * - * @param active the binary state to set - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setValveActive(boolean active) throws Exception; + /** + * Sets the valve active state + * + * @param active the binary state to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setValveActive(boolean active) throws Exception; - /** - * Subscribes to changes in the active state of the valve. - * - * @param callback the function to call when the state changes. - */ - void subscribeValveActive(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes in the active state of the valve. + * + * @param callback the function to call when the state changes. + */ + void subscribeValveActive(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the valve active state. - */ - void unsubscribeValveActive(); + /** Unsubscribes from changes in the valve active state. */ + void unsubscribeValveActive(); - /** - * Retrieves the current inUse state of the valve; InUse usually means water is flowing through the valve. - * - * To communicate water is flowing through a valve, inUse should be used. - * - * @return a future that will contain the binary state - */ - CompletableFuture getValveInUse(); + /** + * Retrieves the current inUse state of the valve; InUse usually means water is flowing through + * the valve. + * + *

To communicate water is flowing through a valve, inUse should be used. + * + * @return a future that will contain the binary state + */ + CompletableFuture getValveInUse(); - /** - * Subscribes to changes in the inUse state of the valve. - * - * @param callback the function to call when the state changes. - */ - void subscribeValveInUse(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes in the inUse state of the valve. + * + * @param callback the function to call when the state changes. + */ + void subscribeValveInUse(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the valve inUse state. - */ - void unsubscribeValveInUse(); + /** Unsubscribes from changes in the valve inUse state. */ + void unsubscribeValveInUse(); - /** - * Retrieves the valve type. - * - * To communicate water is flowing through a valve, inUse should be used. - * - * @return a future that will contain the binary state - */ - CompletableFuture getValveType(); + /** + * Retrieves the valve type. + * + *

To communicate water is flowing through a valve, inUse should be used. + * + * @return a future that will contain the binary state + */ + CompletableFuture getValveType(); - /** - * Subscribes to changes in the valveType state of the valve. - * - * @param callback the function to call when the state changes. - */ - void subscribeValveType(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes in the valveType state of the valve. + * + * @param callback the function to call when the state changes. + */ + void subscribeValveType(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the valveType state light. - */ - void unsubscribeValveType(); + /** Unsubscribes from changes in the valveType state light. */ + void unsubscribeValveType(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/ValveWithTimer.java b/src/main/java/com/beowulfe/hap/accessories/ValveWithTimer.java index 4d3a7d25b..fdd296120 100644 --- a/src/main/java/com/beowulfe/hap/accessories/ValveWithTimer.java +++ b/src/main/java/com/beowulfe/hap/accessories/ValveWithTimer.java @@ -1,8 +1,7 @@ package com.beowulfe.hap.accessories; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; /** * Extends {@link Valve} with timer values. @@ -11,56 +10,50 @@ */ public interface ValveWithTimer extends Valve { - /** - * Retrieves the current duration for which the valve will run - * - * @return a future with the value - */ - CompletableFuture getRemainingDuration(); - - /** - * Subscribes to changes in the duration; note it is not necessary to emit a - * change every second, homekit infers the countdown progress clientside. - * - * @param callback the function when the existing duration has been replaced with a new one. - */ - void subscribeRemainingDuration(HomekitCharacteristicChangeCallback callback); - - /** - * Unsubscribes from changes - */ - void unsubscribeRemainingDuration(); - - /** - * Retrieves the current set duration for which the valve will be scheduled - * to run; this is usually used as the duration to use when the valve is set - * to active. - * - * @return a future with the value - */ - CompletableFuture getSetDuration(); - - /** - * Sets the duration for which the valve will be scheduled to run; this is - * usually used as the duration to use when the valve is set to active. - * - * If the valve is currently running, then Homekit assumes that changing - * this value affects the current remaining duration. - * - * @return a future with the value - */ - CompletableFuture setSetDuration(int value); - - /** - * Subscribes to changes in the set duration - * - * @param callback the function when the value has changed - */ - void subscribeSetDuration(HomekitCharacteristicChangeCallback callback); - - /** - * Unsubscribes from changes - */ - void unsubscribeSetDuration(); - + /** + * Retrieves the current duration for which the valve will run + * + * @return a future with the value + */ + CompletableFuture getRemainingDuration(); + + /** + * Subscribes to changes in the duration; note it is not necessary to emit a change every second, + * homekit infers the countdown progress clientside. + * + * @param callback the function when the existing duration has been replaced with a new one. + */ + void subscribeRemainingDuration(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribeRemainingDuration(); + + /** + * Retrieves the current set duration for which the valve will be scheduled to run; this is + * usually used as the duration to use when the valve is set to active. + * + * @return a future with the value + */ + CompletableFuture getSetDuration(); + + /** + * Sets the duration for which the valve will be scheduled to run; this is usually used as the + * duration to use when the valve is set to active. + * + *

If the valve is currently running, then Homekit assumes that changing this value affects the + * current remaining duration. + * + * @return a future with the value + */ + CompletableFuture setSetDuration(int value); + + /** + * Subscribes to changes in the set duration + * + * @param callback the function when the value has changed + */ + void subscribeSetDuration(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribeSetDuration(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/VerticalTiltingWindowCovering.java b/src/main/java/com/beowulfe/hap/accessories/VerticalTiltingWindowCovering.java index 263a09369..fae1c4c1b 100644 --- a/src/main/java/com/beowulfe/hap/accessories/VerticalTiltingWindowCovering.java +++ b/src/main/java/com/beowulfe/hap/accessories/VerticalTiltingWindowCovering.java @@ -1,8 +1,7 @@ package com.beowulfe.hap.accessories; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; /** * Extends WindowCovering with the ability to control vertical tilt angles @@ -11,45 +10,46 @@ */ public interface VerticalTiltingWindowCovering extends WindowCovering { - /** - * Retrieves the current vertical tilt angle - * @return a future that will contain the position as a value between -90 and 90 - */ - CompletableFuture getCurrentVerticalTiltAngle(); - - /** - * Retrieves the target vertical tilt angle - * @return a future that will contain the target position as a value between -90 and 90 - */ - CompletableFuture getTargetVerticalTiltAngle(); - - /** - * Sets the target position - * @param angle the target angle to set, as a value between -90 and 90 - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setTargetVerticalTiltAngle(int angle) throws Exception; - - /** - * Subscribes to changes in the current vertical tilt angle. - * @param callback the function to call when the state changes. - */ - void subscribeCurrentVerticalTiltAngle(HomekitCharacteristicChangeCallback callback); - - /** - * Subscribes to changes in the target vertical tilt angle. - * @param callback the function to call when the state changes. - */ - void subscribeTargetVerticalTiltAngle(HomekitCharacteristicChangeCallback callback); - - /** - * Unsubscribes from changes in the current vertical tilt angle - */ - void unsubscribeCurrentVerticalTiltAngle(); - - /** - * Unsubscribes from changes in the target vertical tilt angle - */ - void unsubscribeTargetVerticalTiltAngle(); + /** + * Retrieves the current vertical tilt angle + * + * @return a future that will contain the position as a value between -90 and 90 + */ + CompletableFuture getCurrentVerticalTiltAngle(); + + /** + * Retrieves the target vertical tilt angle + * + * @return a future that will contain the target position as a value between -90 and 90 + */ + CompletableFuture getTargetVerticalTiltAngle(); + + /** + * Sets the target position + * + * @param angle the target angle to set, as a value between -90 and 90 + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setTargetVerticalTiltAngle(int angle) throws Exception; + + /** + * Subscribes to changes in the current vertical tilt angle. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentVerticalTiltAngle(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the target vertical tilt angle. + * + * @param callback the function to call when the state changes. + */ + void subscribeTargetVerticalTiltAngle(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current vertical tilt angle */ + void unsubscribeCurrentVerticalTiltAngle(); + + /** Unsubscribes from changes in the target vertical tilt angle */ + void unsubscribeTargetVerticalTiltAngle(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/WindowCovering.java b/src/main/java/com/beowulfe/hap/accessories/WindowCovering.java index 230678f8c..340013ca7 100644 --- a/src/main/java/com/beowulfe/hap/accessories/WindowCovering.java +++ b/src/main/java/com/beowulfe/hap/accessories/WindowCovering.java @@ -1,12 +1,11 @@ package com.beowulfe.hap.accessories; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.*; import com.beowulfe.hap.accessories.properties.WindowCoveringPositionState; import com.beowulfe.hap.impl.services.WindowCoveringService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; /** * A window covering, like blinds, which can be remotely controlled. @@ -15,92 +14,94 @@ */ public interface WindowCovering extends HomekitAccessory { - /** - * Retrieves the current position - * @return a future that will contain the position as a value between 0 and 100 - */ - CompletableFuture getCurrentPosition(); - - /** - * Retrieves the target position - * @return a future that will contain the target position as a value between 0 and 100 - */ - CompletableFuture getTargetPosition(); - - /** - * Retrieves the state of the position: increasing, decreasing, or stopped - * @return a future that will contain the current state - */ - CompletableFuture getPositionState(); - - /** - * Retrieves an indication that the window covering is obstructed from moving - * @return a future that will contain a boolean indicating whether an obstruction is present - */ - CompletableFuture getObstructionDetected(); - - @Override - default Collection getServices() { - return Collections.singleton(new WindowCoveringService(this)); - } - - /** - * Sets the target position - * @param position the target position to set, as a value between 1 and 100 - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setTargetPosition(int position) throws Exception; - - /** - * Sets the hold position state - * @param hold whether or not to hold the current position state - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setHoldPosition(boolean hold) throws Exception; - - /** - * Subscribes to changes in the current position. - * @param callback the function to call when the state changes. - */ - void subscribeCurrentPosition(HomekitCharacteristicChangeCallback callback); - - /** - * Subscribes to changes in the target position. - * @param callback the function to call when the state changes. - */ - void subscribeTargetPosition(HomekitCharacteristicChangeCallback callback); - - /** - * Subscribes to changes in the position state: increasing, decreasing, or stopped - * @param callback the function to call when the state changes. - */ - void subscribePositionState(HomekitCharacteristicChangeCallback callback); - - /** - * Subscribes to changes in the obstruction detected state - * @param callback the function to call when the state changes. - */ - void subscribeObstructionDetected(HomekitCharacteristicChangeCallback callback); - - /** - * Unsubscribes from changes in the current position. - */ - void unsubscribeCurrentPosition(); - - /** - * Unsubscribes from changes in the target position. - */ - void unsubscribeTargetPosition(); - - /** - * Unsubscribes from changes in the position state - */ - void unsubscribePositionState(); - - /** - * Unsubscribes from changes in the obstruction detected state - */ - void unsubscribeObstructionDetected(); + /** + * Retrieves the current position + * + * @return a future that will contain the position as a value between 0 and 100 + */ + CompletableFuture getCurrentPosition(); + + /** + * Retrieves the target position + * + * @return a future that will contain the target position as a value between 0 and 100 + */ + CompletableFuture getTargetPosition(); + + /** + * Retrieves the state of the position: increasing, decreasing, or stopped + * + * @return a future that will contain the current state + */ + CompletableFuture getPositionState(); + + /** + * Retrieves an indication that the window covering is obstructed from moving + * + * @return a future that will contain a boolean indicating whether an obstruction is present + */ + CompletableFuture getObstructionDetected(); + + @Override + default Collection getServices() { + return Collections.singleton(new WindowCoveringService(this)); + } + + /** + * Sets the target position + * + * @param position the target position to set, as a value between 1 and 100 + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setTargetPosition(int position) throws Exception; + + /** + * Sets the hold position state + * + * @param hold whether or not to hold the current position state + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setHoldPosition(boolean hold) throws Exception; + + /** + * Subscribes to changes in the current position. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentPosition(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the target position. + * + * @param callback the function to call when the state changes. + */ + void subscribeTargetPosition(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the position state: increasing, decreasing, or stopped + * + * @param callback the function to call when the state changes. + */ + void subscribePositionState(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the obstruction detected state + * + * @param callback the function to call when the state changes. + */ + void subscribeObstructionDetected(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current position. */ + void unsubscribeCurrentPosition(); + + /** Unsubscribes from changes in the target position. */ + void unsubscribeTargetPosition(); + + /** Unsubscribes from changes in the position state */ + void unsubscribePositionState(); + + /** Unsubscribes from changes in the obstruction detected state */ + void unsubscribeObstructionDetected(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/package-info.java b/src/main/java/com/beowulfe/hap/accessories/package-info.java index 7c54d98bc..22f91c5ac 100644 --- a/src/main/java/com/beowulfe/hap/accessories/package-info.java +++ b/src/main/java/com/beowulfe/hap/accessories/package-info.java @@ -1,4 +1,2 @@ -/** - * Contains interfaces that can be implemented to represent an exposed accessory. - */ -package com.beowulfe.hap.accessories; \ No newline at end of file +/** Contains interfaces that can be implemented to represent an exposed accessory. */ +package com.beowulfe.hap.accessories; diff --git a/src/main/java/com/beowulfe/hap/accessories/properties/CarbonMonoxideDetectedState.java b/src/main/java/com/beowulfe/hap/accessories/properties/CarbonMonoxideDetectedState.java index 95bcf53b1..556eb2892 100644 --- a/src/main/java/com/beowulfe/hap/accessories/properties/CarbonMonoxideDetectedState.java +++ b/src/main/java/com/beowulfe/hap/accessories/properties/CarbonMonoxideDetectedState.java @@ -5,26 +5,28 @@ import java.util.stream.Collectors; public enum CarbonMonoxideDetectedState { + NORMAL(0), + ABNORMAL(1); - NORMAL(0), - ABNORMAL(1); + private static final Map reverse; - private final static Map reverse; - static { - reverse = Arrays.stream(CarbonMonoxideDetectedState.values()).collect(Collectors.toMap(CarbonMonoxideDetectedState::getCode, t -> t)); - } + static { + reverse = + Arrays.stream(CarbonMonoxideDetectedState.values()) + .collect(Collectors.toMap(CarbonMonoxideDetectedState::getCode, t -> t)); + } - public static CarbonMonoxideDetectedState fromCode(Integer code) { - return reverse.get(code); - } + public static CarbonMonoxideDetectedState fromCode(Integer code) { + return reverse.get(code); + } - private final int code; + private final int code; - CarbonMonoxideDetectedState(int code) { - this.code = code; - } + CarbonMonoxideDetectedState(int code) { + this.code = code; + } - public int getCode() { - return code; - } + public int getCode() { + return code; + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/properties/ContactState.java b/src/main/java/com/beowulfe/hap/accessories/properties/ContactState.java index ca4a352ae..b8a466ceb 100644 --- a/src/main/java/com/beowulfe/hap/accessories/properties/ContactState.java +++ b/src/main/java/com/beowulfe/hap/accessories/properties/ContactState.java @@ -5,26 +5,28 @@ import java.util.stream.Collectors; public enum ContactState { + DETECTED(0), + NOT_DETECTED(1); - DETECTED(0), - NOT_DETECTED(1); + private static final Map reverse; - private final static Map reverse; - static { - reverse = Arrays.stream(ContactState.values()).collect(Collectors.toMap(ContactState::getCode, t -> t)); - } + static { + reverse = + Arrays.stream(ContactState.values()) + .collect(Collectors.toMap(ContactState::getCode, t -> t)); + } - public static ContactState fromCode(Integer code) { - return reverse.get(code); - } + public static ContactState fromCode(Integer code) { + return reverse.get(code); + } - private final int code; + private final int code; - ContactState(int code) { - this.code = code; - } + ContactState(int code) { + this.code = code; + } - public int getCode() { - return code; - } + public int getCode() { + return code; + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/properties/CurrentSecuritySystemState.java b/src/main/java/com/beowulfe/hap/accessories/properties/CurrentSecuritySystemState.java index a9666c946..3c8291415 100644 --- a/src/main/java/com/beowulfe/hap/accessories/properties/CurrentSecuritySystemState.java +++ b/src/main/java/com/beowulfe/hap/accessories/properties/CurrentSecuritySystemState.java @@ -1,57 +1,48 @@ package com.beowulfe.hap.accessories.properties; import com.beowulfe.hap.accessories.SecuritySystem; - import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; /** - * The current state of a {@link SecuritySystem}. Unlike {@link TargetSecuritySystemState}, this enum - * includes a triggered state. + * The current state of a {@link SecuritySystem}. Unlike {@link TargetSecuritySystemState}, this + * enum includes a triggered state. * * @author Gaston Dombiak */ public enum CurrentSecuritySystemState { - /** - * The home is occupied and residents are active. - */ - STAY_ARM(0), - /** - * The home is unoccupied. - */ - AWAY_ARM(1), - /** - * The home is occupied and residents are sleeping. - */ - NIGHT_ARM(2), - /** - * The security system is disarmed. - */ - DISARMED(3), - /** - * The security system is triggered. - */ - TRIGGERED(4); - - private final static Map reverse; - static { - reverse = Arrays.stream(CurrentSecuritySystemState.values()).collect(Collectors - .toMap(CurrentSecuritySystemState::getCode, t -> t)); - } - - public static CurrentSecuritySystemState fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - CurrentSecuritySystemState(int code) { - this.code = code; - } - - public int getCode() { - return code; - } + /** The home is occupied and residents are active. */ + STAY_ARM(0), + /** The home is unoccupied. */ + AWAY_ARM(1), + /** The home is occupied and residents are sleeping. */ + NIGHT_ARM(2), + /** The security system is disarmed. */ + DISARMED(3), + /** The security system is triggered. */ + TRIGGERED(4); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(CurrentSecuritySystemState.values()) + .collect(Collectors.toMap(CurrentSecuritySystemState::getCode, t -> t)); + } + + public static CurrentSecuritySystemState fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + CurrentSecuritySystemState(int code) { + this.code = code; + } + + public int getCode() { + return code; + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/properties/DoorState.java b/src/main/java/com/beowulfe/hap/accessories/properties/DoorState.java index d85745986..bcb329768 100644 --- a/src/main/java/com/beowulfe/hap/accessories/properties/DoorState.java +++ b/src/main/java/com/beowulfe/hap/accessories/properties/DoorState.java @@ -5,30 +5,29 @@ import java.util.stream.Collectors; public enum DoorState { + OPEN(0), + CLOSED(1), + OPENING(2), + CLOSING(3), + STOPPED(4); - OPEN(0), - CLOSED(1), - OPENING(2), - CLOSING(3), - STOPPED(4); - - - private final static Map reverse; - static { - reverse = Arrays.stream(DoorState.values()).collect(Collectors.toMap(t -> t.getCode(), t -> t)); - } - - public static DoorState fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - private DoorState(int code) { - this.code = code; - } - - public int getCode() { - return code; - } + private static final Map reverse; + + static { + reverse = Arrays.stream(DoorState.values()).collect(Collectors.toMap(t -> t.getCode(), t -> t)); + } + + public static DoorState fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + private DoorState(int code) { + this.code = code; + } + + public int getCode() { + return code; + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/properties/LockMechanismState.java b/src/main/java/com/beowulfe/hap/accessories/properties/LockMechanismState.java index 8250c8e28..0b2afb5cc 100644 --- a/src/main/java/com/beowulfe/hap/accessories/properties/LockMechanismState.java +++ b/src/main/java/com/beowulfe/hap/accessories/properties/LockMechanismState.java @@ -1,40 +1,40 @@ package com.beowulfe.hap.accessories.properties; +import com.beowulfe.hap.accessories.LockMechanism; import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; -import com.beowulfe.hap.accessories.LockMechanism; - /** * The state of a {@link LockMechanism}. * * @author Andy Lintner */ public enum LockMechanismState { - - UNSECURED(0), - SECURED(1), - JAMMED(2), - UNKNOWN(3) - ; - - private final static Map reverse; - static { - reverse = Arrays.stream(LockMechanismState.values()).collect(Collectors.toMap(t -> t.getCode(), t -> t)); - } - - public static LockMechanismState fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - private LockMechanismState(int code) { - this.code = code; - } - - public int getCode() { - return code; - } + UNSECURED(0), + SECURED(1), + JAMMED(2), + UNKNOWN(3); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(LockMechanismState.values()) + .collect(Collectors.toMap(t -> t.getCode(), t -> t)); + } + + public static LockMechanismState fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + private LockMechanismState(int code) { + this.code = code; + } + + public int getCode() { + return code; + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/properties/RotationDirection.java b/src/main/java/com/beowulfe/hap/accessories/properties/RotationDirection.java index 9a7d4693f..4c504d28a 100644 --- a/src/main/java/com/beowulfe/hap/accessories/properties/RotationDirection.java +++ b/src/main/java/com/beowulfe/hap/accessories/properties/RotationDirection.java @@ -5,26 +5,28 @@ import java.util.stream.Collectors; public enum RotationDirection { - CLOCKWISE(0), - COUNTER_CLOCKWISE(1) - ; - - private final static Map reverse; - static { - reverse = Arrays.stream(RotationDirection.values()).collect(Collectors.toMap(t -> t.getCode(), t -> t)); - } - - public static RotationDirection fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - private RotationDirection(int code) { - this.code = code; - } - - public int getCode() { - return code; - } + CLOCKWISE(0), + COUNTER_CLOCKWISE(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(RotationDirection.values()) + .collect(Collectors.toMap(t -> t.getCode(), t -> t)); + } + + public static RotationDirection fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + private RotationDirection(int code) { + this.code = code; + } + + public int getCode() { + return code; + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/properties/SecuritySystemAlarmType.java b/src/main/java/com/beowulfe/hap/accessories/properties/SecuritySystemAlarmType.java index 161cd91f9..aa8b0806c 100644 --- a/src/main/java/com/beowulfe/hap/accessories/properties/SecuritySystemAlarmType.java +++ b/src/main/java/com/beowulfe/hap/accessories/properties/SecuritySystemAlarmType.java @@ -1,7 +1,6 @@ package com.beowulfe.hap.accessories.properties; import com.beowulfe.hap.accessories.SecuritySystem; - import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; @@ -13,32 +12,30 @@ */ public enum SecuritySystemAlarmType { - /** - * Alarm conditions are cleared - */ - NO_ALARM(0), - /** - * Alarm type is not known - */ - UNKNOWN(1); - - private final static Map reverse; - static { - reverse = Arrays.stream(SecuritySystemAlarmType.values()).collect(Collectors - .toMap(SecuritySystemAlarmType::getCode, t -> t)); - } - - public static SecuritySystemAlarmType fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - SecuritySystemAlarmType(int code) { - this.code = code; - } - - public int getCode() { - return code; - } + /** Alarm conditions are cleared */ + NO_ALARM(0), + /** Alarm type is not known */ + UNKNOWN(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(SecuritySystemAlarmType.values()) + .collect(Collectors.toMap(SecuritySystemAlarmType::getCode, t -> t)); + } + + public static SecuritySystemAlarmType fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + SecuritySystemAlarmType(int code) { + this.code = code; + } + + public int getCode() { + return code; + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/properties/SmokeDetectedState.java b/src/main/java/com/beowulfe/hap/accessories/properties/SmokeDetectedState.java index 159a767ee..7c929c6b2 100644 --- a/src/main/java/com/beowulfe/hap/accessories/properties/SmokeDetectedState.java +++ b/src/main/java/com/beowulfe/hap/accessories/properties/SmokeDetectedState.java @@ -5,26 +5,28 @@ import java.util.stream.Collectors; public enum SmokeDetectedState { + NOT_DETECTED(0), + DETECTED(1); - NOT_DETECTED(0), - DETECTED(1); + private static final Map reverse; - private final static Map reverse; - static { - reverse = Arrays.stream(SmokeDetectedState.values()).collect(Collectors.toMap(SmokeDetectedState::getCode, t -> t)); - } + static { + reverse = + Arrays.stream(SmokeDetectedState.values()) + .collect(Collectors.toMap(SmokeDetectedState::getCode, t -> t)); + } - public static SmokeDetectedState fromCode(Integer code) { - return reverse.get(code); - } + public static SmokeDetectedState fromCode(Integer code) { + return reverse.get(code); + } - private final int code; + private final int code; - SmokeDetectedState(int code) { - this.code = code; - } + SmokeDetectedState(int code) { + this.code = code; + } - public int getCode() { - return code; - } + public int getCode() { + return code; + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/properties/TargetSecuritySystemState.java b/src/main/java/com/beowulfe/hap/accessories/properties/TargetSecuritySystemState.java index 35bd087a2..bb924fcc8 100644 --- a/src/main/java/com/beowulfe/hap/accessories/properties/TargetSecuritySystemState.java +++ b/src/main/java/com/beowulfe/hap/accessories/properties/TargetSecuritySystemState.java @@ -1,7 +1,6 @@ package com.beowulfe.hap.accessories.properties; import com.beowulfe.hap.accessories.SecuritySystem; - import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; @@ -13,40 +12,34 @@ */ public enum TargetSecuritySystemState { - /** - * Arm the system when the home is occupied and residents are active. - */ - STAY_ARM(0), - /** - * Arm the system when the home is unoccupied. - */ - AWAY_ARM(1), - /** - * Arm the system when the home is occupied and residents are sleeping. - */ - NIGHT_ARM(2), - /** - * Disarm the system. - */ - DISARM(3); - - private final static Map reverse; - static { - reverse = Arrays.stream(TargetSecuritySystemState.values()).collect(Collectors - .toMap(TargetSecuritySystemState::getCode, t -> t)); - } - - public static TargetSecuritySystemState fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - TargetSecuritySystemState(int code) { - this.code = code; - } - - public int getCode() { - return code; - } + /** Arm the system when the home is occupied and residents are active. */ + STAY_ARM(0), + /** Arm the system when the home is unoccupied. */ + AWAY_ARM(1), + /** Arm the system when the home is occupied and residents are sleeping. */ + NIGHT_ARM(2), + /** Disarm the system. */ + DISARM(3); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(TargetSecuritySystemState.values()) + .collect(Collectors.toMap(TargetSecuritySystemState::getCode, t -> t)); + } + + public static TargetSecuritySystemState fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + TargetSecuritySystemState(int code) { + this.code = code; + } + + public int getCode() { + return code; + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/properties/TemperatureUnit.java b/src/main/java/com/beowulfe/hap/accessories/properties/TemperatureUnit.java index 7cf1a476e..27bf004b4 100644 --- a/src/main/java/com/beowulfe/hap/accessories/properties/TemperatureUnit.java +++ b/src/main/java/com/beowulfe/hap/accessories/properties/TemperatureUnit.java @@ -1,38 +1,37 @@ package com.beowulfe.hap.accessories.properties; +import com.beowulfe.hap.accessories.TemperatureSensor; import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; -import com.beowulfe.hap.accessories.TemperatureSensor; - /** * The temperature unit used by a {@link TemperatureSensor}. * * @author Andy Lintner */ public enum TemperatureUnit { + CELSIUS(0), + FAHRENHEIT(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(TemperatureUnit.values()).collect(Collectors.toMap(t -> t.getCode(), t -> t)); + } + + static TemperatureUnit fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + private TemperatureUnit(int code) { + this.code = code; + } - CELSIUS(0), - FAHRENHEIT(1) - ; - - private final static Map reverse; - static { - reverse = Arrays.stream(TemperatureUnit.values()).collect(Collectors.toMap(t -> t.getCode(), t -> t)); - } - - static TemperatureUnit fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - private TemperatureUnit(int code) { - this.code = code; - } - - public int getCode() { - return code; - } + public int getCode() { + return code; + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/properties/ThermostatMode.java b/src/main/java/com/beowulfe/hap/accessories/properties/ThermostatMode.java index 081e11793..4d6f2f893 100644 --- a/src/main/java/com/beowulfe/hap/accessories/properties/ThermostatMode.java +++ b/src/main/java/com/beowulfe/hap/accessories/properties/ThermostatMode.java @@ -1,40 +1,39 @@ package com.beowulfe.hap.accessories.properties; +import com.beowulfe.hap.accessories.thermostat.BasicThermostat; import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; -import com.beowulfe.hap.accessories.thermostat.BasicThermostat; - /** * The mode used by a {@link BasicThermostat} * * @author Andy Lintner */ public enum ThermostatMode { + OFF(0), + HEAT(1), + COOL(2), + AUTO(3); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(ThermostatMode.values()).collect(Collectors.toMap(t -> t.getCode(), t -> t)); + } + + public static ThermostatMode fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + private ThermostatMode(int code) { + this.code = code; + } - OFF(0), - HEAT(1), - COOL(2), - AUTO(3) - ; - - private final static Map reverse; - static { - reverse = Arrays.stream(ThermostatMode.values()).collect(Collectors.toMap(t -> t.getCode(), t -> t)); - } - - public static ThermostatMode fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - private ThermostatMode(int code) { - this.code = code; - } - - public int getCode() { - return code; - } + public int getCode() { + return code; + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/properties/ValveType.java b/src/main/java/com/beowulfe/hap/accessories/properties/ValveType.java index 0991e8fd4..a16105155 100644 --- a/src/main/java/com/beowulfe/hap/accessories/properties/ValveType.java +++ b/src/main/java/com/beowulfe/hap/accessories/properties/ValveType.java @@ -1,39 +1,38 @@ package com.beowulfe.hap.accessories.properties; +import com.beowulfe.hap.accessories.Valve; import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; -import com.beowulfe.hap.accessories.Valve; - /** * The mode used by a {@link Valve} * * @author Tim Harper */ public enum ValveType { + GENERIC(0), + IRRIGATION(1), + SHOWER(2), + WATER_FAUCET(3); - GENERIC(0), - IRRIGATION(1), - SHOWER(2), - WATER_FAUCET(3); + private static final Map reverse; - private final static Map reverse; - static { - reverse = Arrays.stream(ValveType.values()).collect(Collectors.toMap(t -> t.getCode(), t -> t)); - } + static { + reverse = Arrays.stream(ValveType.values()).collect(Collectors.toMap(t -> t.getCode(), t -> t)); + } - public static ValveType fromCode(Integer code) { - return reverse.get(code); - } + public static ValveType fromCode(Integer code) { + return reverse.get(code); + } - private final int code; + private final int code; - private ValveType(int code) { - this.code = code; - } + private ValveType(int code) { + this.code = code; + } - public int getCode() { - return code; - } + public int getCode() { + return code; + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/properties/WindowCoveringPositionState.java b/src/main/java/com/beowulfe/hap/accessories/properties/WindowCoveringPositionState.java index 29624ad81..b57bc4b6f 100644 --- a/src/main/java/com/beowulfe/hap/accessories/properties/WindowCoveringPositionState.java +++ b/src/main/java/com/beowulfe/hap/accessories/properties/WindowCoveringPositionState.java @@ -1,38 +1,39 @@ package com.beowulfe.hap.accessories.properties; +import com.beowulfe.hap.accessories.WindowCovering; import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; -import com.beowulfe.hap.accessories.WindowCovering; - /** * The position state used by a {@link WindowCovering} * * @author Andy Lintner */ public enum WindowCoveringPositionState { - DECREASING(0), - INCREASING(1), - STOPPED(2) - ; - - private final static Map reverse; - static { - reverse = Arrays.stream(WindowCoveringPositionState.values()).collect(Collectors.toMap(t -> t.getCode(), t -> t)); - } - - public static WindowCoveringPositionState fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - private WindowCoveringPositionState(int code) { - this.code = code; - } - - public int getCode() { - return code; - } + DECREASING(0), + INCREASING(1), + STOPPED(2); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(WindowCoveringPositionState.values()) + .collect(Collectors.toMap(t -> t.getCode(), t -> t)); + } + + public static WindowCoveringPositionState fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + private WindowCoveringPositionState(int code) { + this.code = code; + } + + public int getCode() { + return code; + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/properties/package-info.java b/src/main/java/com/beowulfe/hap/accessories/properties/package-info.java index ee3991bbf..e234adb4c 100644 --- a/src/main/java/com/beowulfe/hap/accessories/properties/package-info.java +++ b/src/main/java/com/beowulfe/hap/accessories/properties/package-info.java @@ -1,4 +1,2 @@ -/** - * Contains properties used by {@link com.beowulfe.hap.accessories}. - */ -package com.beowulfe.hap.accessories.properties; \ No newline at end of file +/** Contains properties used by {@link com.beowulfe.hap.accessories}. */ +package com.beowulfe.hap.accessories.properties; diff --git a/src/main/java/com/beowulfe/hap/accessories/thermostat/BasicThermostat.java b/src/main/java/com/beowulfe/hap/accessories/thermostat/BasicThermostat.java index 803738ca8..f6fc9ec36 100644 --- a/src/main/java/com/beowulfe/hap/accessories/thermostat/BasicThermostat.java +++ b/src/main/java/com/beowulfe/hap/accessories/thermostat/BasicThermostat.java @@ -1,83 +1,88 @@ package com.beowulfe.hap.accessories.thermostat; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.*; import com.beowulfe.hap.accessories.TemperatureSensor; import com.beowulfe.hap.accessories.properties.ThermostatMode; import com.beowulfe.hap.impl.services.ThermostatService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; public interface BasicThermostat extends HomekitAccessory, TemperatureSensor { - /** - * Retrieves the current {@link ThermostatMode} of the thermostat. - * @return a future that will contain the mode. - */ - CompletableFuture getCurrentMode(); + /** + * Retrieves the current {@link ThermostatMode} of the thermostat. + * + * @return a future that will contain the mode. + */ + CompletableFuture getCurrentMode(); + + /** + * Subscribes to changes in the {@link ThermostatMode} of the thermostat. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentMode(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the mode of the thermostat. */ + void unsubscribeCurrentMode(); - /** - * Subscribes to changes in the {@link ThermostatMode} of the thermostat. - * @param callback the function to call when the state changes. - */ - void subscribeCurrentMode(HomekitCharacteristicChangeCallback callback); + /** + * Sets the {@link ThermostatMode} of the thermostat. + * + * @param mode The {@link ThermostatMode} to set. + * @throws Exception when the change cannot be made. + */ + void setTargetMode(ThermostatMode mode) throws Exception; - /** - * Unsubscribes from changes in the mode of the thermostat. - */ - void unsubscribeCurrentMode(); + /** + * Retrieves the pending, but not yet complete, {@link ThermostatMode} of the thermostat. + * + * @return a future that will contain the target mode. + */ + CompletableFuture getTargetMode(); - /** - * Sets the {@link ThermostatMode} of the thermostat. - * @param mode The {@link ThermostatMode} to set. - * @throws Exception when the change cannot be made. - */ - void setTargetMode(ThermostatMode mode) throws Exception; + /** + * Subscribes to changes in the pending, but not yet complete, {@link ThermostatMode} of the + * thermostat. + * + * @param callback the function to call when the state changes. + */ + void subscribeTargetMode(HomekitCharacteristicChangeCallback callback); - /** - * Retrieves the pending, but not yet complete, {@link ThermostatMode} of the thermostat. - * @return a future that will contain the target mode. - */ - CompletableFuture getTargetMode(); + /** + * Unsubscribes from changes in the pending, but not yet complete, {@link ThermostatMode} of the + * thermostat. + */ + void unsubscribeTargetMode(); - /** - * Subscribes to changes in the pending, but not yet complete, {@link ThermostatMode} of the thermostat. - * @param callback the function to call when the state changes. - */ - void subscribeTargetMode(HomekitCharacteristicChangeCallback callback); + /** + * Retrieves the target temperature, in celsius degrees. + * + * @return a future that will contain the target temperature. + */ + CompletableFuture getTargetTemperature(); - /** - * Unsubscribes from changes in the pending, but not yet complete, {@link ThermostatMode} of the thermostat. - */ - void unsubscribeTargetMode(); + /** + * Sets the target temperature. + * + * @param value the target temperature, in celsius degrees. + * @throws Exception when the temperature cannot be changed. + */ + void setTargetTemperature(Double value) throws Exception; - /** - * Retrieves the target temperature, in celsius degrees. - * @return a future that will contain the target temperature. - */ - CompletableFuture getTargetTemperature(); + /** + * Subscribes to changes in the target temperature. + * + * @param callback the function to call when the state changes. + */ + void subscribeTargetTemperature(HomekitCharacteristicChangeCallback callback); - /** - * Sets the target temperature. - * @param value the target temperature, in celsius degrees. - * @throws Exception when the temperature cannot be changed. - */ - void setTargetTemperature(Double value) throws Exception; - - /** - * Subscribes to changes in the target temperature. - * @param callback the function to call when the state changes. - */ - void subscribeTargetTemperature(HomekitCharacteristicChangeCallback callback); + /** Unsubscribes from changes in the target temperature. */ + void unsubscribeTargetTemperature(); - /** - * Unsubscribes from changes in the target temperature. - */ - void unsubscribeTargetTemperature(); - - @Override - default Collection getServices() { - return Collections.singleton(new ThermostatService(this)); - } + @Override + default Collection getServices() { + return Collections.singleton(new ThermostatService(this)); + } } diff --git a/src/main/java/com/beowulfe/hap/accessories/thermostat/CoolingThermostat.java b/src/main/java/com/beowulfe/hap/accessories/thermostat/CoolingThermostat.java index 60b0fe175..cb60ed88b 100644 --- a/src/main/java/com/beowulfe/hap/accessories/thermostat/CoolingThermostat.java +++ b/src/main/java/com/beowulfe/hap/accessories/thermostat/CoolingThermostat.java @@ -1,32 +1,32 @@ package com.beowulfe.hap.accessories.thermostat; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; public interface CoolingThermostat extends BasicThermostat { - /** - * Retrieves the temperature above which the thermostat should begin cooling. - * @return a future that will contain the threshold temperature, in celsius degrees. - */ - CompletableFuture getCoolingThresholdTemperature(); + /** + * Retrieves the temperature above which the thermostat should begin cooling. + * + * @return a future that will contain the threshold temperature, in celsius degrees. + */ + CompletableFuture getCoolingThresholdTemperature(); + + /** + * Sets the temperature above which the thermostat should begin cooling. + * + * @param value the threshold temperature, in celsius degrees. + * @throws Exception when the threshold temperature cannot be changed. + */ + void setCoolingThresholdTemperature(Double value) throws Exception; - /** - * Sets the temperature above which the thermostat should begin cooling. - * @param value the threshold temperature, in celsius degrees. - * @throws Exception when the threshold temperature cannot be changed. - */ - void setCoolingThresholdTemperature(Double value) throws Exception; - - /** - * Subscribes to changes in the cooling threshold. - * @param callback the function to call when the state changes. - */ - void subscribeCoolingThresholdTemperature(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes in the cooling threshold. + * + * @param callback the function to call when the state changes. + */ + void subscribeCoolingThresholdTemperature(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the cooling threshold. - */ - void unsubscribeCoolingThresholdTemperature(); + /** Unsubscribes from changes in the cooling threshold. */ + void unsubscribeCoolingThresholdTemperature(); } diff --git a/src/main/java/com/beowulfe/hap/accessories/thermostat/HeatingThermostat.java b/src/main/java/com/beowulfe/hap/accessories/thermostat/HeatingThermostat.java index 9a0013677..36e946316 100644 --- a/src/main/java/com/beowulfe/hap/accessories/thermostat/HeatingThermostat.java +++ b/src/main/java/com/beowulfe/hap/accessories/thermostat/HeatingThermostat.java @@ -1,32 +1,32 @@ package com.beowulfe.hap.accessories.thermostat; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; public interface HeatingThermostat extends BasicThermostat { - /** - * Retrieves the temperature below which the thermostat should begin heating. - * @return a future that will contain the threshold temperature, in celsius degrees. - */ - CompletableFuture getHeatingThresholdTemperature(); + /** + * Retrieves the temperature below which the thermostat should begin heating. + * + * @return a future that will contain the threshold temperature, in celsius degrees. + */ + CompletableFuture getHeatingThresholdTemperature(); + + /** + * Sets the temperature below which the thermostat should begin heating. + * + * @param value the threshold temperature, in celsius degrees. + * @throws Exception when the threshold temperature cannot be changed. + */ + void setHeatingThresholdTemperature(Double value) throws Exception; - /** - * Sets the temperature below which the thermostat should begin heating. - * @param value the threshold temperature, in celsius degrees. - * @throws Exception when the threshold temperature cannot be changed. - */ - void setHeatingThresholdTemperature(Double value) throws Exception; - - /** - * Subscribes to changes in the heating threshold. - * @param callback the function to call when the state changes. - */ - void subscribeHeatingThresholdTemperature(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes in the heating threshold. + * + * @param callback the function to call when the state changes. + */ + void subscribeHeatingThresholdTemperature(HomekitCharacteristicChangeCallback callback); - /** - * Unsubscribes from changes in the heating threshold. - */ - void unsubscribeHeatingThresholdTemperature(); + /** Unsubscribes from changes in the heating threshold. */ + void unsubscribeHeatingThresholdTemperature(); } diff --git a/src/main/java/com/beowulfe/hap/characteristics/BaseCharacteristic.java b/src/main/java/com/beowulfe/hap/characteristics/BaseCharacteristic.java index 661e53910..a4ba443b1 100644 --- a/src/main/java/com/beowulfe/hap/characteristics/BaseCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/characteristics/BaseCharacteristic.java @@ -4,13 +4,11 @@ import java.math.BigInteger; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; - import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.json.JsonObject; import javax.json.JsonObjectBuilder; import javax.json.JsonValue; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,168 +18,169 @@ * @author Andy Lintner */ public abstract class BaseCharacteristic implements Characteristic { - - private final Logger logger = LoggerFactory.getLogger(BaseCharacteristic.class); - - private final String type; - private final String format; - private final boolean isWritable; - private final boolean isReadable; - private final boolean isEventable; - private final String description; - - /** - * Default constructor - * - * @param type a string containing a UUID that indicates the type of characteristic. Apple defines a set of these, - * however implementors can create their own as well. - * @param format a string indicating the value type, which must be a recognized type by the consuming device. - * @param isWritable indicates whether the value can be changed. - * @param isReadable indicates whether the value can be retrieved. - * @param description a description of the characteristic to be passed to the consuming device. - */ - public BaseCharacteristic(String type, String format, boolean isWritable, boolean isReadable, String description) { - if (type == null || format == null || description == null) { - throw new NullPointerException(); - } - - this.type = type; - this.format = format; - this.isWritable = isWritable; - this.isReadable = isReadable; - this.isEventable = this instanceof EventableCharacteristic; - this.description = description; - } - - @Override - /** - * {@inheritDoc} - */ - public final CompletableFuture toJson(int iid) { - return makeBuilder(iid).thenApply(builder -> builder.build()); - } - - /** - * Creates the JSON serialized form of the accessory for use over the Homekit Accessory Protocol. - * - * @param instanceId the static id of the accessory. - * @return a future that will complete with the JSON builder for the object. - */ - protected CompletableFuture makeBuilder(int instanceId) { - CompletableFuture futureValue = getValue(); - - if (futureValue == null) { - logger.error("Could not retrieve value "+this.getClass().getName()); - return null; - } - - return futureValue.exceptionally(t -> { - logger.error("Could not retrieve value "+this.getClass().getName(), t); - return null; - }).thenApply(value -> { - JsonArrayBuilder perms = Json.createArrayBuilder(); - if (isWritable) { - perms.add("pw"); - } - if (isReadable) { - perms.add("pr"); - } - if (isEventable) { - perms.add("ev"); - } - JsonObjectBuilder builder = Json.createObjectBuilder() - .add("iid", instanceId) - .add("type", type) - .add("perms", perms.build()) - .add("format", format) - .add("events", false) - .add("bonjour", false) - .add("description", description); - setJsonValue(builder, value); - return builder; - }); - } - - /** - * {@inheritDoc} - */ - @Override - public final void setValue(JsonValue jsonValue) { - try { - this.setValue(convert(jsonValue)); - } catch (Exception e) { - logger.error("Error while setting JSON value", e); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void supplyValue(JsonObjectBuilder builder) { - try { - setJsonValue(builder, getValue().get()); - } catch (InterruptedException | ExecutionException e) { - logger.error("Error retrieving value", e); - setJsonValue(builder, getDefault()); - } - } - - /** - * Converts from the JSON value to a Java object of the type T - * - * @param jsonValue the JSON value to convert from. - * @return the converted Java object. - */ - protected abstract T convert(JsonValue jsonValue); - - /** - * Update the characteristic value using a new value supplied by the connected client. - * - * @param value the new value to set. - * @throws Exception if the value cannot be set. - */ - protected abstract void setValue(T value) throws Exception; - - /** - * Retrieves the current value of the characteristic. - * - * @return a future that will complete with the current value. - */ - protected abstract CompletableFuture getValue(); - - /** - * Supplies a default value for the characteristic to send to connected clients when the real value. - * cannot be retrieved. - * - * @return a sensible default value. - */ - protected abstract T getDefault(); - - /** - * Writes the value key to the serialized characteristic - * - * @param builder The JSON builder to add the value to - * @param value The value to add - */ - protected void setJsonValue(JsonObjectBuilder builder, T value) { - //I don't like this - there should really be a way to construct a disconnected JSONValue... - if (value instanceof Boolean) { - builder.add("value", (Boolean) value); - } else if (value instanceof Double) { - builder.add("value", (Double) value); - } else if (value instanceof Integer) { - builder.add("value", (Integer) value); - } else if (value instanceof Long) { - builder.add("value", (Long) value); - } else if (value instanceof BigInteger) { - builder.add("value", (BigInteger) value); - } else if (value instanceof BigDecimal){ - builder.add("value", (BigDecimal) value); - } else if (value == null) { - builder.addNull("value"); - } else { - builder.add("value", value.toString()); - } - } + + private final Logger logger = LoggerFactory.getLogger(BaseCharacteristic.class); + + private final String type; + private final String format; + private final boolean isWritable; + private final boolean isReadable; + private final boolean isEventable; + private final String description; + + /** + * Default constructor + * + * @param type a string containing a UUID that indicates the type of characteristic. Apple defines + * a set of these, however implementors can create their own as well. + * @param format a string indicating the value type, which must be a recognized type by the + * consuming device. + * @param isWritable indicates whether the value can be changed. + * @param isReadable indicates whether the value can be retrieved. + * @param description a description of the characteristic to be passed to the consuming device. + */ + public BaseCharacteristic( + String type, String format, boolean isWritable, boolean isReadable, String description) { + if (type == null || format == null || description == null) { + throw new NullPointerException(); + } + + this.type = type; + this.format = format; + this.isWritable = isWritable; + this.isReadable = isReadable; + this.isEventable = this instanceof EventableCharacteristic; + this.description = description; + } + + @Override + /** {@inheritDoc} */ + public final CompletableFuture toJson(int iid) { + return makeBuilder(iid).thenApply(builder -> builder.build()); + } + + /** + * Creates the JSON serialized form of the accessory for use over the Homekit Accessory Protocol. + * + * @param instanceId the static id of the accessory. + * @return a future that will complete with the JSON builder for the object. + */ + protected CompletableFuture makeBuilder(int instanceId) { + CompletableFuture futureValue = getValue(); + + if (futureValue == null) { + logger.error("Could not retrieve value " + this.getClass().getName()); + return null; + } + + return futureValue + .exceptionally( + t -> { + logger.error("Could not retrieve value " + this.getClass().getName(), t); + return null; + }) + .thenApply( + value -> { + JsonArrayBuilder perms = Json.createArrayBuilder(); + if (isWritable) { + perms.add("pw"); + } + if (isReadable) { + perms.add("pr"); + } + if (isEventable) { + perms.add("ev"); + } + JsonObjectBuilder builder = + Json.createObjectBuilder() + .add("iid", instanceId) + .add("type", type) + .add("perms", perms.build()) + .add("format", format) + .add("events", false) + .add("bonjour", false) + .add("description", description); + setJsonValue(builder, value); + return builder; + }); + } + + /** {@inheritDoc} */ + @Override + public final void setValue(JsonValue jsonValue) { + try { + this.setValue(convert(jsonValue)); + } catch (Exception e) { + logger.error("Error while setting JSON value", e); + } + } + + /** {@inheritDoc} */ + @Override + public void supplyValue(JsonObjectBuilder builder) { + try { + setJsonValue(builder, getValue().get()); + } catch (InterruptedException | ExecutionException e) { + logger.error("Error retrieving value", e); + setJsonValue(builder, getDefault()); + } + } + + /** + * Converts from the JSON value to a Java object of the type T + * + * @param jsonValue the JSON value to convert from. + * @return the converted Java object. + */ + protected abstract T convert(JsonValue jsonValue); + + /** + * Update the characteristic value using a new value supplied by the connected client. + * + * @param value the new value to set. + * @throws Exception if the value cannot be set. + */ + protected abstract void setValue(T value) throws Exception; + + /** + * Retrieves the current value of the characteristic. + * + * @return a future that will complete with the current value. + */ + protected abstract CompletableFuture getValue(); + + /** + * Supplies a default value for the characteristic to send to connected clients when the real + * value. cannot be retrieved. + * + * @return a sensible default value. + */ + protected abstract T getDefault(); + + /** + * Writes the value key to the serialized characteristic + * + * @param builder The JSON builder to add the value to + * @param value The value to add + */ + protected void setJsonValue(JsonObjectBuilder builder, T value) { + // I don't like this - there should really be a way to construct a disconnected JSONValue... + if (value instanceof Boolean) { + builder.add("value", (Boolean) value); + } else if (value instanceof Double) { + builder.add("value", (Double) value); + } else if (value instanceof Integer) { + builder.add("value", (Integer) value); + } else if (value instanceof Long) { + builder.add("value", (Long) value); + } else if (value instanceof BigInteger) { + builder.add("value", (BigInteger) value); + } else if (value instanceof BigDecimal) { + builder.add("value", (BigDecimal) value); + } else if (value == null) { + builder.addNull("value"); + } else { + builder.add("value", value.toString()); + } + } } diff --git a/src/main/java/com/beowulfe/hap/characteristics/BooleanCharacteristic.java b/src/main/java/com/beowulfe/hap/characteristics/BooleanCharacteristic.java index 4782e9a72..1564c6f43 100644 --- a/src/main/java/com/beowulfe/hap/characteristics/BooleanCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/characteristics/BooleanCharacteristic.java @@ -10,42 +10,33 @@ * @author Andy Lintner */ public abstract class BooleanCharacteristic extends BaseCharacteristic { - - /** - * Default constructor - * - * @param type a string containing a UUID that indicates the type of characteristic. Apple defines a set of these, - * however implementors can create their own as well. - * @param isWritable indicates whether the value can be changed. - * @param isReadable indicates whether the value can be retrieved. - * @param description a description of the characteristic to be passed to the consuming device. - */ - public BooleanCharacteristic(String type, boolean isWritable, boolean isReadable, String description) { - super( type, - "bool", - isWritable, - isReadable, - description); - - } - - /** - * {@inheritDoc} - */ - @Override - protected Boolean convert(JsonValue jsonValue) { - if (jsonValue.getValueType().equals(ValueType.NUMBER)) { - return ((JsonNumber) jsonValue).intValue() > 0; - } - return jsonValue.equals(JsonValue.TRUE); - } - - /** - * {@inheritDoc} - */ - @Override - protected Boolean getDefault() { - return false; - } + /** + * Default constructor + * + * @param type a string containing a UUID that indicates the type of characteristic. Apple defines + * a set of these, however implementors can create their own as well. + * @param isWritable indicates whether the value can be changed. + * @param isReadable indicates whether the value can be retrieved. + * @param description a description of the characteristic to be passed to the consuming device. + */ + public BooleanCharacteristic( + String type, boolean isWritable, boolean isReadable, String description) { + super(type, "bool", isWritable, isReadable, description); + } + + /** {@inheritDoc} */ + @Override + protected Boolean convert(JsonValue jsonValue) { + if (jsonValue.getValueType().equals(ValueType.NUMBER)) { + return ((JsonNumber) jsonValue).intValue() > 0; + } + return jsonValue.equals(JsonValue.TRUE); + } + + /** {@inheritDoc} */ + @Override + protected Boolean getDefault() { + return false; + } } diff --git a/src/main/java/com/beowulfe/hap/characteristics/Characteristic.java b/src/main/java/com/beowulfe/hap/characteristics/Characteristic.java index 1ee1a3833..1530aeee0 100644 --- a/src/main/java/com/beowulfe/hap/characteristics/Characteristic.java +++ b/src/main/java/com/beowulfe/hap/characteristics/Characteristic.java @@ -1,43 +1,45 @@ package com.beowulfe.hap.characteristics; import java.util.concurrent.CompletableFuture; - import javax.json.JsonObject; import javax.json.JsonObjectBuilder; import javax.json.JsonValue; /** * Interface for the characteristics provided by a Service. - * - * Characteristics are the lowest level building block of the Homekit Accessory Protocol. They define variables - * that can be retrieved or set by the remote client. Most consumers of this library will be better served by using - * one of the characteristic classes in {@link com.beowulfe.hap.characteristics} when creating custom accessory types - * (the standard accessories from {@link com.beowulfe.hap.accessories} already include the necessary characteristics), - * instead of trying to implement the JSON formats directly. + * + *

Characteristics are the lowest level building block of the Homekit Accessory Protocol. They + * define variables that can be retrieved or set by the remote client. Most consumers of this + * library will be better served by using one of the characteristic classes in {@link + * com.beowulfe.hap.characteristics} when creating custom accessory types (the standard accessories + * from {@link com.beowulfe.hap.accessories} already include the necessary characteristics), instead + * of trying to implement the JSON formats directly. * * @author Andy Lintner */ public interface Characteristic { - /** - * Adds an attribute to the passed JsonObjectBuilder named "value" with the current value of the characteristic. - * - * @param characteristicBuilder the JsonObjectBuilder to add the value attribute to. - */ - void supplyValue(JsonObjectBuilder characteristicBuilder); + /** + * Adds an attribute to the passed JsonObjectBuilder named "value" with the current value of the + * characteristic. + * + * @param characteristicBuilder the JsonObjectBuilder to add the value attribute to. + */ + void supplyValue(JsonObjectBuilder characteristicBuilder); - /** - * Creates the JSON representation of the characteristic, in accordance with the Homekit Accessory Protocol. - * - * @param iid The instance ID of the characteristic to be included in the serialization. - * @return the future completing with the resulting JSON. - */ - CompletableFuture toJson(int iid); + /** + * Creates the JSON representation of the characteristic, in accordance with the Homekit Accessory + * Protocol. + * + * @param iid The instance ID of the characteristic to be included in the serialization. + * @return the future completing with the resulting JSON. + */ + CompletableFuture toJson(int iid); - /** - * Invoked by the remote client, this updates the current value of the characteristic. - * - * @param jsonValue the JSON serialized value to set. - */ - void setValue(JsonValue jsonValue); + /** + * Invoked by the remote client, this updates the current value of the characteristic. + * + * @param jsonValue the JSON serialized value to set. + */ + void setValue(JsonValue jsonValue); } diff --git a/src/main/java/com/beowulfe/hap/characteristics/EnumCharacteristic.java b/src/main/java/com/beowulfe/hap/characteristics/EnumCharacteristic.java index 2c1d84188..4007afcf5 100644 --- a/src/main/java/com/beowulfe/hap/characteristics/EnumCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/characteristics/EnumCharacteristic.java @@ -1,73 +1,65 @@ package com.beowulfe.hap.characteristics; import java.util.concurrent.CompletableFuture; - import javax.json.JsonNumber; import javax.json.JsonObject; import javax.json.JsonObjectBuilder; import javax.json.JsonValue; /** - * Characteristic that exposes an Enum value. Enums are represented as an Integer value - * in the Homekit protocol, and classes extending this one must handle the static mapping - * to an Integer value. + * Characteristic that exposes an Enum value. Enums are represented as an Integer value in the + * Homekit protocol, and classes extending this one must handle the static mapping to an Integer + * value. * * @author Andy Lintner */ public abstract class EnumCharacteristic extends BaseCharacteristic { - private final int maxValue; - - /** - * Default constructor - * - * @param type a string containing a UUID that indicates the type of characteristic. Apple defines a set of these, - * however implementors can create their own as well. - * @param isWritable indicates whether the value can be changed. - * @param isReadable indicates whether the value can be retrieved. - * @param description a description of the characteristic to be passed to the consuming device. - * @param maxValue the number of enum items. - */ - public EnumCharacteristic(String type, boolean isWritable, boolean isReadable, String description, int maxValue) { - super(type, "int", isWritable, isReadable, description); - this.maxValue = maxValue; - } - - /** - * {@inheritDoc} - */ - @Override - protected CompletableFuture makeBuilder(int iid) { - return super.makeBuilder(iid).thenApply(builder -> { - return builder - .add("minValue", 0) - .add("maxValue", maxValue) - .add("minStep", 1); - }); - } - - /** - * {@inheritDoc} - */ - @Override - protected Integer convert(JsonValue jsonValue) { - if (jsonValue instanceof JsonNumber) { - return ((JsonNumber) jsonValue).intValue(); - } else if (jsonValue == JsonObject.TRUE) { - return 1; //For at least one enum type (locks), homekit will send a true instead of 1 - } else if (jsonValue == JsonObject.FALSE) { - return 0; - } else { - throw new IndexOutOfBoundsException("Cannot convert "+jsonValue.getClass()+" to int"); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected Integer getDefault() { - return 0; - } + private final int maxValue; + + /** + * Default constructor + * + * @param type a string containing a UUID that indicates the type of characteristic. Apple defines + * a set of these, however implementors can create their own as well. + * @param isWritable indicates whether the value can be changed. + * @param isReadable indicates whether the value can be retrieved. + * @param description a description of the characteristic to be passed to the consuming device. + * @param maxValue the number of enum items. + */ + public EnumCharacteristic( + String type, boolean isWritable, boolean isReadable, String description, int maxValue) { + super(type, "int", isWritable, isReadable, description); + this.maxValue = maxValue; + } + + /** {@inheritDoc} */ + @Override + protected CompletableFuture makeBuilder(int iid) { + return super.makeBuilder(iid) + .thenApply( + builder -> { + return builder.add("minValue", 0).add("maxValue", maxValue).add("minStep", 1); + }); + } + + /** {@inheritDoc} */ + @Override + protected Integer convert(JsonValue jsonValue) { + if (jsonValue instanceof JsonNumber) { + return ((JsonNumber) jsonValue).intValue(); + } else if (jsonValue == JsonObject.TRUE) { + return 1; // For at least one enum type (locks), homekit will send a true instead of 1 + } else if (jsonValue == JsonObject.FALSE) { + return 0; + } else { + throw new IndexOutOfBoundsException("Cannot convert " + jsonValue.getClass() + " to int"); + } + } + /** {@inheritDoc} */ + @Override + protected Integer getDefault() { + return 0; + } } diff --git a/src/main/java/com/beowulfe/hap/characteristics/EventableCharacteristic.java b/src/main/java/com/beowulfe/hap/characteristics/EventableCharacteristic.java index d36ec649c..c9527838f 100644 --- a/src/main/java/com/beowulfe/hap/characteristics/EventableCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/characteristics/EventableCharacteristic.java @@ -9,14 +9,14 @@ */ public interface EventableCharacteristic extends Characteristic { - /** - * Begin listening to changes to this characteristic. When a change is made, call the provided function. - * @param callback a function to call when a change is made to the characteristic value. - */ - void subscribe(HomekitCharacteristicChangeCallback callback); + /** + * Begin listening to changes to this characteristic. When a change is made, call the provided + * function. + * + * @param callback a function to call when a change is made to the characteristic value. + */ + void subscribe(HomekitCharacteristicChangeCallback callback); - /** - * Stop listening to changes to this characteristic. - */ - void unsubscribe(); + /** Stop listening to changes to this characteristic. */ + void unsubscribe(); } diff --git a/src/main/java/com/beowulfe/hap/characteristics/FloatCharacteristic.java b/src/main/java/com/beowulfe/hap/characteristics/FloatCharacteristic.java index 4ff06f36b..fda9aec56 100644 --- a/src/main/java/com/beowulfe/hap/characteristics/FloatCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/characteristics/FloatCharacteristic.java @@ -1,12 +1,11 @@ package com.beowulfe.hap.characteristics; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import java.util.concurrent.CompletableFuture; import javax.json.JsonNumber; import javax.json.JsonObjectBuilder; import javax.json.JsonValue; -import java.util.concurrent.CompletableFuture; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A characteristic that provides a Float value type. @@ -15,91 +14,105 @@ */ public abstract class FloatCharacteristic extends BaseCharacteristic { - private final static Logger LOGGER = LoggerFactory.getLogger(FloatCharacteristic.class); + private static final Logger LOGGER = LoggerFactory.getLogger(FloatCharacteristic.class); + + private final double minValue; + private final double maxValue; + private final double minStep; + private final String unit; + + /** + * Default constructor + * + * @param type a string containing a UUID that indicates the type of characteristic. Apple defines + * a set of these, however implementors can create their own as well. + * @param isWritable indicates whether the value can be changed. + * @param isReadable indicates whether the value can be retrieved. + * @param description a description of the characteristic to be passed to the consuming device. + * @param minValue the minimum supported value. + * @param maxValue the maximum supported value + * @param minStep the smallest supported step. Values will be rounded to a multiple of this. + * @param unit a description of the unit this characteristic supports. + */ + public FloatCharacteristic( + String type, + boolean isWritable, + boolean isReadable, + String description, + double minValue, + double maxValue, + double minStep, + String unit) { + super(type, "float", isWritable, isReadable, description); + this.minValue = minValue; + this.maxValue = maxValue; + this.minStep = minStep; + this.unit = unit; + } + + /** {@inheritDoc} */ + @Override + protected CompletableFuture makeBuilder(int iid) { + return super.makeBuilder(iid) + .thenApply( + builder -> + builder + .add("minValue", minValue) + .add("maxValue", maxValue) + .add("minStep", minStep) + .add("unit", unit)); + } + + /** {@inheritDoc} */ + @Override + protected Double convert(JsonValue jsonValue) { + return ((JsonNumber) jsonValue).doubleValue(); + } + + /** + * {@inheritDoc}. Calls the getDoubleValue method and applies rounding to the minStep supplied in + * the constructor. + */ + @Override + protected final CompletableFuture getValue() { + double rounder = 1 / this.minStep; + return getDoubleValue() + .thenApply(d -> d == null ? null : Math.round(d * rounder) / rounder) + .thenApply( + d -> { + if (d != null) { + if (d < minValue) { + LOGGER.warn( + "Detected value out of range " + + d + + ". Returning min value instead. Characteristic " + + this); + return minValue; + } + if (d > maxValue) { + LOGGER.warn( + "Detected value out of range " + + d + + ". Returning max value instead. Characteristic " + + this); + return maxValue; + } + return d; + } + return null; + }); + } - private final double minValue; - private final double maxValue; - private final double minStep; - private final String unit; - - /** - * Default constructor - * - * @param type a string containing a UUID that indicates the type of characteristic. Apple defines a set of these, - * however implementors can create their own as well. - * @param isWritable indicates whether the value can be changed. - * @param isReadable indicates whether the value can be retrieved. - * @param description a description of the characteristic to be passed to the consuming device. - * @param minValue the minimum supported value. - * @param maxValue the maximum supported value - * @param minStep the smallest supported step. Values will be rounded to a multiple of this. - * @param unit a description of the unit this characteristic supports. - */ - public FloatCharacteristic(String type, boolean isWritable, boolean isReadable, String description, - double minValue, double maxValue, double minStep, String unit) { - super(type, "float", isWritable, isReadable, description); - this.minValue = minValue; - this.maxValue = maxValue; - this.minStep = minStep; - this.unit = unit; - } - - /** - * {@inheritDoc} - */ - @Override - protected CompletableFuture makeBuilder(int iid) { - return super.makeBuilder(iid).thenApply(builder -> builder - .add("minValue", minValue) - .add("maxValue", maxValue) - .add("minStep", minStep) - .add("unit", unit)); - } + /** {@inheritDoc} */ + @Override + protected Double getDefault() { + return minValue; + } - /** - * {@inheritDoc} - */ - @Override - protected Double convert(JsonValue jsonValue) { - return ((JsonNumber) jsonValue).doubleValue(); - } - - /** - * {@inheritDoc}. Calls the getDoubleValue method and applies rounding to the minStep supplied in the constructor. - */ - @Override - protected final CompletableFuture getValue() { - double rounder = 1 / this.minStep; - return getDoubleValue().thenApply(d -> d == null ? null : Math.round(d * rounder) / rounder) - .thenApply(d -> { - if (d != null) { - if (d < minValue) { - LOGGER.warn("Detected value out of range " + d - + ". Returning min value instead. Characteristic " + this); - return minValue; - } - if (d > maxValue) { - LOGGER.warn("Detected value out of range " + d - + ". Returning max value instead. Characteristic " + this); - return maxValue; - } - return d; - } - return null; - }); - } - - /** - * {@inheritDoc} - */ - @Override - protected Double getDefault() { - return minValue; - } - - /** - * Supplies the value of this characteristic as a double. - * @return a future that will contain the value. - */ - protected abstract CompletableFuture getDoubleValue(); + /** + * Supplies the value of this characteristic as a double. + * + * @return a future that will contain the value. + */ + protected abstract CompletableFuture getDoubleValue(); } diff --git a/src/main/java/com/beowulfe/hap/characteristics/IntegerCharacteristic.java b/src/main/java/com/beowulfe/hap/characteristics/IntegerCharacteristic.java index a3c0fcfb6..30adeaa6b 100644 --- a/src/main/java/com/beowulfe/hap/characteristics/IntegerCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/characteristics/IntegerCharacteristic.java @@ -1,7 +1,6 @@ package com.beowulfe.hap.characteristics; import java.util.concurrent.CompletableFuture; - import javax.json.JsonNumber; import javax.json.JsonObjectBuilder; import javax.json.JsonValue; @@ -13,58 +12,59 @@ */ public abstract class IntegerCharacteristic extends BaseCharacteristic { - private final int minValue; - private final int maxValue; - private final String unit; - - /** - * Default constructor - * - * @param type a string containing a UUID that indicates the type of characteristic. Apple defines a set of these, - * however implementors can create their own as well. - * @param isWritable indicates whether the value can be changed. - * @param isReadable indicates whether the value can be retrieved. - * @param description a description of the characteristic to be passed to the consuming device. - * @param minValue the minimum supported value. - * @param maxValue the maximum supported value - * @param unit a description of the unit this characteristic supports. - */ - public IntegerCharacteristic(String type, boolean isWritable, boolean isReadable, String description, - int minValue, int maxValue, String unit) { - super(type, "int", isWritable, isReadable, description); - this.minValue = minValue; - this.maxValue = maxValue; - this.unit = unit; - } - - /** - * {@inheritDoc} - */ - @Override - protected CompletableFuture makeBuilder(int iid) { - return super.makeBuilder(iid).thenApply(builder -> { - return builder - .add("minValue", minValue) - .add("maxValue", maxValue) - .add("minStep", 1) - .add("unit", unit); - }); - } - - /** - * {@inheritDoc} - */ - @Override - protected Integer getDefault() { - return minValue; - } - - /** - * {@inheritDoc} - */ - @Override - protected Integer convert(JsonValue jsonValue) { - return ((JsonNumber) jsonValue).intValue(); - } + private final int minValue; + private final int maxValue; + private final String unit; + + /** + * Default constructor + * + * @param type a string containing a UUID that indicates the type of characteristic. Apple defines + * a set of these, however implementors can create their own as well. + * @param isWritable indicates whether the value can be changed. + * @param isReadable indicates whether the value can be retrieved. + * @param description a description of the characteristic to be passed to the consuming device. + * @param minValue the minimum supported value. + * @param maxValue the maximum supported value + * @param unit a description of the unit this characteristic supports. + */ + public IntegerCharacteristic( + String type, + boolean isWritable, + boolean isReadable, + String description, + int minValue, + int maxValue, + String unit) { + super(type, "int", isWritable, isReadable, description); + this.minValue = minValue; + this.maxValue = maxValue; + this.unit = unit; + } + + /** {@inheritDoc} */ + @Override + protected CompletableFuture makeBuilder(int iid) { + return super.makeBuilder(iid) + .thenApply( + builder -> { + return builder + .add("minValue", minValue) + .add("maxValue", maxValue) + .add("minStep", 1) + .add("unit", unit); + }); + } + + /** {@inheritDoc} */ + @Override + protected Integer getDefault() { + return minValue; + } + /** {@inheritDoc} */ + @Override + protected Integer convert(JsonValue jsonValue) { + return ((JsonNumber) jsonValue).intValue(); + } } diff --git a/src/main/java/com/beowulfe/hap/characteristics/StaticStringCharacteristic.java b/src/main/java/com/beowulfe/hap/characteristics/StaticStringCharacteristic.java index b3737ac12..8deaeb386 100644 --- a/src/main/java/com/beowulfe/hap/characteristics/StaticStringCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/characteristics/StaticStringCharacteristic.java @@ -1,7 +1,6 @@ package com.beowulfe.hap.characteristics; import java.util.concurrent.CompletableFuture; - import javax.json.JsonObjectBuilder; import javax.json.JsonString; import javax.json.JsonValue; @@ -13,65 +12,50 @@ */ public class StaticStringCharacteristic extends BaseCharacteristic { - final private static int MAX_LEN = 255; - - final private String value; - - /** - * Default constructor - * - * @param type a string containing a UUID that indicates the type of characteristic. Apple defines a set of these, - * however implementors can create their own as well. - * @param description a description of the characteristic to be passed to the consuming device. - * @param value the value of the static string. - */ - public StaticStringCharacteristic(String type, String description, String value) { - super(type, - "string", - false, - true, - description); - this.value = value; - } - - /** - * {@inheritDoc} - */ - @Override - protected CompletableFuture makeBuilder(int iid) { - return super.makeBuilder(iid).thenApply(builder -> builder.add("maxLen", MAX_LEN)); - } + private static final int MAX_LEN = 255; + + private final String value; + + /** + * Default constructor + * + * @param type a string containing a UUID that indicates the type of characteristic. Apple defines + * a set of these, however implementors can create their own as well. + * @param description a description of the characteristic to be passed to the consuming device. + * @param value the value of the static string. + */ + public StaticStringCharacteristic(String type, String description, String value) { + super(type, "string", false, true, description); + this.value = value; + } + + /** {@inheritDoc} */ + @Override + protected CompletableFuture makeBuilder(int iid) { + return super.makeBuilder(iid).thenApply(builder -> builder.add("maxLen", MAX_LEN)); + } + + /** {@inheritDoc} */ + @Override + public String convert(JsonValue jsonValue) { + return ((JsonString) jsonValue).getString(); + } - /** - * {@inheritDoc} - */ - @Override - public String convert(JsonValue jsonValue) { - return ((JsonString) jsonValue).getString(); - } + /** {@inheritDoc} */ + @Override + public void setValue(String value) throws Exception { + throw new Exception("Cannot modify static strings"); + } - /** - * {@inheritDoc} - */ - @Override - public void setValue(String value) throws Exception { - throw new Exception("Cannot modify static strings"); - } + /** {@inheritDoc} */ + @Override + protected CompletableFuture getValue() { + return CompletableFuture.completedFuture(value).thenApply(s -> s != null ? s : "Unavailable"); + } - /** - * {@inheritDoc} - */ - @Override - protected CompletableFuture getValue() { - return CompletableFuture.completedFuture(value).thenApply(s -> s != null ? s : "Unavailable"); - } - - /** - * {@inheritDoc} - */ - @Override - protected String getDefault() { - return "Unknown"; - } - + /** {@inheritDoc} */ + @Override + protected String getDefault() { + return "Unknown"; + } } diff --git a/src/main/java/com/beowulfe/hap/characteristics/WriteOnlyBooleanCharacteristic.java b/src/main/java/com/beowulfe/hap/characteristics/WriteOnlyBooleanCharacteristic.java index bcf100b6e..8f60c8e61 100644 --- a/src/main/java/com/beowulfe/hap/characteristics/WriteOnlyBooleanCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/characteristics/WriteOnlyBooleanCharacteristic.java @@ -1,7 +1,6 @@ package com.beowulfe.hap.characteristics; import java.util.concurrent.CompletableFuture; - import javax.json.JsonObjectBuilder; /** @@ -10,27 +9,25 @@ * @author Andy Lintner */ public abstract class WriteOnlyBooleanCharacteristic extends BooleanCharacteristic { - - /** - * Default constructor - * - * @param type a string containing a UUID that indicates the type of characteristic. Apple defines a set of these, - * however implementors can create their own as well. - * @param description a description of the characteristic to be passed to the consuming device. - */ - public WriteOnlyBooleanCharacteristic(String type, String description) { - super( type, - true, - false, - description - ); - } - - @Override - protected final CompletableFuture getValue() { return CompletableFuture.completedFuture(false); } - @Override - protected final void setJsonValue(JsonObjectBuilder builder, Boolean value) { - //Do nothing - non-readable characteristics cannot have a value key set - } + /** + * Default constructor + * + * @param type a string containing a UUID that indicates the type of characteristic. Apple defines + * a set of these, however implementors can create their own as well. + * @param description a description of the characteristic to be passed to the consuming device. + */ + public WriteOnlyBooleanCharacteristic(String type, String description) { + super(type, true, false, description); + } + + @Override + protected final CompletableFuture getValue() { + return CompletableFuture.completedFuture(false); + } + + @Override + protected final void setJsonValue(JsonObjectBuilder builder, Boolean value) { + // Do nothing - non-readable characteristics cannot have a value key set + } } diff --git a/src/main/java/com/beowulfe/hap/characteristics/package-info.java b/src/main/java/com/beowulfe/hap/characteristics/package-info.java index 94b6dd9eb..78085ead2 100644 --- a/src/main/java/com/beowulfe/hap/characteristics/package-info.java +++ b/src/main/java/com/beowulfe/hap/characteristics/package-info.java @@ -1,4 +1,4 @@ /** * Contains the basic characteristic types that can be supplied over the Homekit Accessory Protocol. */ -package com.beowulfe.hap.characteristics; \ No newline at end of file +package com.beowulfe.hap.characteristics; diff --git a/src/main/java/com/beowulfe/hap/impl/ExceptionalConsumer.java b/src/main/java/com/beowulfe/hap/impl/ExceptionalConsumer.java index 4db49d49c..ecbd5faad 100644 --- a/src/main/java/com/beowulfe/hap/impl/ExceptionalConsumer.java +++ b/src/main/java/com/beowulfe/hap/impl/ExceptionalConsumer.java @@ -1,5 +1,5 @@ package com.beowulfe.hap.impl; public interface ExceptionalConsumer { - void accept(T t) throws Exception; + void accept(T t) throws Exception; } diff --git a/src/main/java/com/beowulfe/hap/impl/HomekitBridge.java b/src/main/java/com/beowulfe/hap/impl/HomekitBridge.java index 9b4fb2d7b..476c83681 100644 --- a/src/main/java/com/beowulfe/hap/impl/HomekitBridge.java +++ b/src/main/java/com/beowulfe/hap/impl/HomekitBridge.java @@ -1,54 +1,51 @@ package com.beowulfe.hap.impl; -import java.util.Collection; -import java.util.Collections; - import com.beowulfe.hap.Service; import com.beowulfe.hap.impl.accessories.Bridge; +import java.util.Collection; +import java.util.Collections; public class HomekitBridge implements Bridge { - private final String label; - private final String serialNumber; - private final String model; - private final String manufacturer; - - public HomekitBridge(String label, String serialNumber, String model, - String manufacturer) { - this.label = label; - this.serialNumber = serialNumber; - this.model = model; - this.manufacturer = manufacturer; - } - - @Override - public String getLabel() { - return label; - } - - @Override - public String getSerialNumber() { - return serialNumber; - } - - @Override - public String getModel() { - return model; - } - - @Override - public String getManufacturer() { - return manufacturer; - } - - @Override - public Collection getServices() { - return Collections.emptyList(); - } - - @Override - public int getId() { - return 1; - } + private final String label; + private final String serialNumber; + private final String model; + private final String manufacturer; + + public HomekitBridge(String label, String serialNumber, String model, String manufacturer) { + this.label = label; + this.serialNumber = serialNumber; + this.model = model; + this.manufacturer = manufacturer; + } + + @Override + public String getLabel() { + return label; + } + + @Override + public String getSerialNumber() { + return serialNumber; + } + + @Override + public String getModel() { + return model; + } + + @Override + public String getManufacturer() { + return manufacturer; + } + + @Override + public Collection getServices() { + return Collections.emptyList(); + } + @Override + public int getId() { + return 1; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/HomekitRegistry.java b/src/main/java/com/beowulfe/hap/impl/HomekitRegistry.java index b784cae63..f691d9320 100644 --- a/src/main/java/com/beowulfe/hap/impl/HomekitRegistry.java +++ b/src/main/java/com/beowulfe/hap/impl/HomekitRegistry.java @@ -4,89 +4,88 @@ import com.beowulfe.hap.Service; import com.beowulfe.hap.characteristics.Characteristic; import com.beowulfe.hap.impl.services.AccessoryInformationService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class HomekitRegistry { - - private final static Logger logger = LoggerFactory.getLogger(HomekitRegistry.class); - private final String label; - private final Map accessories; - private final Map> services = new HashMap<>(); - private final Map> characteristics = new HashMap<>(); - private boolean isAllowUnauthenticatedRequests = false; - - public HomekitRegistry(String label) { - this.label = label; - this.accessories = new ConcurrentHashMap<>(); - reset(); - } - - public synchronized void reset() { - characteristics.clear(); - services.clear(); - for (HomekitAccessory accessory: accessories.values()) { - int iid = 0; - List newServices; - try { - newServices = new ArrayList<>(2); - newServices.add(new AccessoryInformationService(accessory)); - newServices.addAll(accessory.getServices()); - } catch (Exception e) { - logger.error("Could not instantiate services for accessory "+accessory.getLabel(), e); - services.put(accessory, Collections.emptyList()); - continue; - } - Map newCharacteristics = new HashMap<>(); - services.put(accessory, newServices); - for (Service service: newServices) { - iid++; - for (Characteristic characteristic: service.getCharacteristics()) { - newCharacteristics.put(++iid, characteristic); - } - } - characteristics.put(accessory, newCharacteristics); - } - } - - public String getLabel() { - return label; - } + private static final Logger logger = LoggerFactory.getLogger(HomekitRegistry.class); + + private final String label; + private final Map accessories; + private final Map> services = new HashMap<>(); + private final Map> characteristics = + new HashMap<>(); + private boolean isAllowUnauthenticatedRequests = false; + + public HomekitRegistry(String label) { + this.label = label; + this.accessories = new ConcurrentHashMap<>(); + reset(); + } + + public synchronized void reset() { + characteristics.clear(); + services.clear(); + for (HomekitAccessory accessory : accessories.values()) { + int iid = 0; + List newServices; + try { + newServices = new ArrayList<>(2); + newServices.add(new AccessoryInformationService(accessory)); + newServices.addAll(accessory.getServices()); + } catch (Exception e) { + logger.error("Could not instantiate services for accessory " + accessory.getLabel(), e); + services.put(accessory, Collections.emptyList()); + continue; + } + Map newCharacteristics = new HashMap<>(); + services.put(accessory, newServices); + for (Service service : newServices) { + iid++; + for (Characteristic characteristic : service.getCharacteristics()) { + newCharacteristics.put(++iid, characteristic); + } + } + characteristics.put(accessory, newCharacteristics); + } + } + + public String getLabel() { + return label; + } + + public Collection getAccessories() { + return accessories.values(); + } + + public List getServices(Integer aid) { + return Collections.unmodifiableList(services.get(accessories.get(aid))); + } - public Collection getAccessories() { - return accessories.values(); - } - - public List getServices(Integer aid) { - return Collections.unmodifiableList(services.get(accessories.get(aid))); - } - - public Map getCharacteristics(Integer aid) { - Map characteristics = this.characteristics.get(accessories.get(aid)); - if (characteristics == null) { - return Collections.emptyMap(); - } - return Collections.unmodifiableMap(characteristics); - } + public Map getCharacteristics(Integer aid) { + Map characteristics = this.characteristics.get(accessories.get(aid)); + if (characteristics == null) { + return Collections.emptyMap(); + } + return Collections.unmodifiableMap(characteristics); + } - public void add(HomekitAccessory accessory) { - accessories.put(accessory.getId(), accessory); - } + public void add(HomekitAccessory accessory) { + accessories.put(accessory.getId(), accessory); + } - public void remove(HomekitAccessory accessory) { - accessories.remove(accessory.getId()); - } - - public boolean isAllowUnauthenticatedRequests() { - return isAllowUnauthenticatedRequests; - } + public void remove(HomekitAccessory accessory) { + accessories.remove(accessory.getId()); + } - public void setAllowUnauthenticatedRequests(boolean allow) { - this.isAllowUnauthenticatedRequests = allow; - } + public boolean isAllowUnauthenticatedRequests() { + return isAllowUnauthenticatedRequests; + } + public void setAllowUnauthenticatedRequests(boolean allow) { + this.isAllowUnauthenticatedRequests = allow; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/HomekitUtils.java b/src/main/java/com/beowulfe/hap/impl/HomekitUtils.java index 8e5f09d70..f82eb77e1 100644 --- a/src/main/java/com/beowulfe/hap/impl/HomekitUtils.java +++ b/src/main/java/com/beowulfe/hap/impl/HomekitUtils.java @@ -1,47 +1,48 @@ package com.beowulfe.hap.impl; +import com.nimbusds.srp6.SRP6Routines; import java.math.BigInteger; import java.security.InvalidAlgorithmParameterException; import java.security.SecureRandom; import java.util.stream.Collectors; import java.util.stream.Stream; - import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable; import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec; -import com.nimbusds.srp6.SRP6Routines; - public class HomekitUtils { - - private static volatile SecureRandom secureRandom; - public static BigInteger generateSalt() { - return new BigInteger(SRP6Routines.generateRandomSalt(16)); - } - - public static byte[] generateKey() throws InvalidAlgorithmParameterException { - EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("ed25519-sha-512"); - byte[] seed = new byte[spec.getCurve().getField().getb()/8]; - getSecureRandom().nextBytes(seed); - return seed; - } - - public static String generateMac() { - int byte1 = ((getSecureRandom().nextInt(255) + 1) | 2) & 0xFE; //Unicast locally administered MAC; - return Integer.toHexString(byte1) + ":" + Stream.generate(() -> getSecureRandom().nextInt(255) + 1) - .limit(5) - .map(i -> Integer.toHexString(i)) - .collect(Collectors.joining(":")); - } - - private static SecureRandom getSecureRandom() { - if (secureRandom == null) { - synchronized(HomekitUtils.class) { - if (secureRandom == null) { - secureRandom = new SecureRandom(); - } - } - } - return secureRandom; - } + private static volatile SecureRandom secureRandom; + + public static BigInteger generateSalt() { + return new BigInteger(SRP6Routines.generateRandomSalt(16)); + } + + public static byte[] generateKey() throws InvalidAlgorithmParameterException { + EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("ed25519-sha-512"); + byte[] seed = new byte[spec.getCurve().getField().getb() / 8]; + getSecureRandom().nextBytes(seed); + return seed; + } + + public static String generateMac() { + int byte1 = + ((getSecureRandom().nextInt(255) + 1) | 2) & 0xFE; // Unicast locally administered MAC; + return Integer.toHexString(byte1) + + ":" + + Stream.generate(() -> getSecureRandom().nextInt(255) + 1) + .limit(5) + .map(i -> Integer.toHexString(i)) + .collect(Collectors.joining(":")); + } + + private static SecureRandom getSecureRandom() { + if (secureRandom == null) { + synchronized (HomekitUtils.class) { + if (secureRandom == null) { + secureRandom = new SecureRandom(); + } + } + } + return secureRandom; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/HomekitWebHandler.java b/src/main/java/com/beowulfe/hap/impl/HomekitWebHandler.java index 3652789db..32755e94e 100644 --- a/src/main/java/com/beowulfe/hap/impl/HomekitWebHandler.java +++ b/src/main/java/com/beowulfe/hap/impl/HomekitWebHandler.java @@ -1,16 +1,13 @@ package com.beowulfe.hap.impl; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.impl.http.HomekitClientConnectionFactory; - +import java.util.concurrent.CompletableFuture; public interface HomekitWebHandler { - CompletableFuture start(HomekitClientConnectionFactory clientConnectionFactory); - - void stop(); + CompletableFuture start(HomekitClientConnectionFactory clientConnectionFactory); + + void stop(); - void resetConnections(); - + void resetConnections(); } diff --git a/src/main/java/com/beowulfe/hap/impl/accessories/Bridge.java b/src/main/java/com/beowulfe/hap/impl/accessories/Bridge.java index 3ecf1328e..a0a1201ed 100644 --- a/src/main/java/com/beowulfe/hap/impl/accessories/Bridge.java +++ b/src/main/java/com/beowulfe/hap/impl/accessories/Bridge.java @@ -4,7 +4,6 @@ public interface Bridge extends HomekitAccessory { - @Override - default void identify() {} - + @Override + default void identify() {} } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/carbonmonoxide/CarbonMonoxideDetectedCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/carbonmonoxide/CarbonMonoxideDetectedCharacteristic.java index 4e1dbf481..989f7024c 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/carbonmonoxide/CarbonMonoxideDetectedCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/carbonmonoxide/CarbonMonoxideDetectedCharacteristic.java @@ -5,34 +5,37 @@ import com.beowulfe.hap.accessories.properties.CarbonMonoxideDetectedState; import com.beowulfe.hap.characteristics.EnumCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; - import java.util.concurrent.CompletableFuture; -public class CarbonMonoxideDetectedCharacteristic extends EnumCharacteristic implements EventableCharacteristic { - - private final CarbonMonoxideSensor carbonMonoxideSensor; - - public CarbonMonoxideDetectedCharacteristic(CarbonMonoxideSensor carbonMonoxideSensor) { - super("00000069-0000-1000-8000-0026BB765291", false, true, "Carbon Monoxide Detected", 1); - this.carbonMonoxideSensor = carbonMonoxideSensor; - } - - @Override - protected CompletableFuture getValue() { - return carbonMonoxideSensor.getCarbonMonoxideDetectedState().thenApply(CarbonMonoxideDetectedState::getCode); - } - - @Override - protected void setValue(Integer value) throws Exception { - //Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - carbonMonoxideSensor.subscribeCarbonMonoxideDetectedState(callback); - } - - @Override - public void unsubscribe() { - carbonMonoxideSensor.unsubscribeCarbonMonoxideDetectedState(); - }} +public class CarbonMonoxideDetectedCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { + + private final CarbonMonoxideSensor carbonMonoxideSensor; + + public CarbonMonoxideDetectedCharacteristic(CarbonMonoxideSensor carbonMonoxideSensor) { + super("00000069-0000-1000-8000-0026BB765291", false, true, "Carbon Monoxide Detected", 1); + this.carbonMonoxideSensor = carbonMonoxideSensor; + } + + @Override + protected CompletableFuture getValue() { + return carbonMonoxideSensor + .getCarbonMonoxideDetectedState() + .thenApply(CarbonMonoxideDetectedState::getCode); + } + + @Override + protected void setValue(Integer value) throws Exception { + // Read Only + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + carbonMonoxideSensor.subscribeCarbonMonoxideDetectedState(callback); + } + + @Override + public void unsubscribe() { + carbonMonoxideSensor.unsubscribeCarbonMonoxideDetectedState(); + } +} diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/common/ActiveCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/common/ActiveCharacteristic.java index b3f94f7de..e0f8f409c 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/common/ActiveCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/common/ActiveCharacteristic.java @@ -1,47 +1,49 @@ package com.beowulfe.hap.impl.characteristics.common; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.characteristics.BooleanCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.impl.ExceptionalConsumer; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; public class ActiveCharacteristic extends BooleanCharacteristic implements EventableCharacteristic { - private final Supplier> getter; - private final ExceptionalConsumer setter; - private final Consumer subscriber; - private final Runnable unsubscriber; - - public ActiveCharacteristic(Supplier> getter, ExceptionalConsumer setter, - Consumer subscriber, Runnable unsubscriber) { - super("000000B0-0000-1000-8000-0026BB765291", true, true, "Active"); - this.getter = getter; - this.setter = setter; - this.subscriber = subscriber; - this.unsubscriber = unsubscriber; - } - - @Override - protected CompletableFuture getValue() { - return getter.get(); - } - - @Override - protected void setValue(Boolean value) throws Exception { - setter.accept(value); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - subscriber.accept(callback); - } - - @Override - public void unsubscribe() { - unsubscriber.run(); - } + private final Supplier> getter; + private final ExceptionalConsumer setter; + private final Consumer subscriber; + private final Runnable unsubscriber; + + public ActiveCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super("000000B0-0000-1000-8000-0026BB765291", true, true, "Active"); + this.getter = getter; + this.setter = setter; + this.subscriber = subscriber; + this.unsubscriber = unsubscriber; + } + + @Override + protected CompletableFuture getValue() { + return getter.get(); + } + + @Override + protected void setValue(Boolean value) throws Exception { + setter.accept(value); + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + subscriber.accept(callback); + } + + @Override + public void unsubscribe() { + unsubscriber.run(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/common/BatteryLevelCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/common/BatteryLevelCharacteristic.java index f0cd9aa4f..6a042ce11 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/common/BatteryLevelCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/common/BatteryLevelCharacteristic.java @@ -1,49 +1,50 @@ package com.beowulfe.hap.impl.characteristics.common; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.IntegerCharacteristic; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; /** - * This characteristic is used by a stand-alone BatteryService, which describes - * a stand-alone battery device, not the battery status of a battery operated - * device such as a motion sensor. + * This characteristic is used by a stand-alone BatteryService, which describes a stand-alone + * battery device, not the battery status of a battery operated device such as a motion sensor. */ -public class BatteryLevelCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { - - private final Supplier> getter; - private final Consumer subscriber; - private final Runnable unsubscriber; - - public BatteryLevelCharacteristic(Supplier> getter, - Consumer subscriber, Runnable unsubscriber) { - super("00000068-0000-1000-8000-0026BB765291", false, true, "Battery Level", 0, 100, "%"); - this.getter = getter; - this.subscriber = subscriber; - this.unsubscriber = unsubscriber; - } - - @Override - protected CompletableFuture getValue() { - return getter.get(); - } - - @Override - protected void setValue(Integer value) throws Exception { - // Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - subscriber.accept(callback); - } - - @Override - public void unsubscribe() { - unsubscriber.run(); - } +public class BatteryLevelCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + private final Supplier> getter; + private final Consumer subscriber; + private final Runnable unsubscriber; + + public BatteryLevelCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super("00000068-0000-1000-8000-0026BB765291", false, true, "Battery Level", 0, 100, "%"); + this.getter = getter; + this.subscriber = subscriber; + this.unsubscriber = unsubscriber; + } + + @Override + protected CompletableFuture getValue() { + return getter.get(); + } + + @Override + protected void setValue(Integer value) throws Exception { + // Read Only + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + subscriber.accept(callback); + } + + @Override + public void unsubscribe() { + unsubscriber.run(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/common/InUseCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/common/InUseCharacteristic.java index 25ecace9d..f050f6ef0 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/common/InUseCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/common/InUseCharacteristic.java @@ -1,44 +1,45 @@ package com.beowulfe.hap.impl.characteristics.common; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.characteristics.BooleanCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; public class InUseCharacteristic extends BooleanCharacteristic implements EventableCharacteristic { - private final Supplier> getter; - private final Consumer subscriber; - private final Runnable unsubscriber; - - public InUseCharacteristic(Supplier> getter, - Consumer subscriber, Runnable unsubscriber) { - super("000000D2-0000-1000-8000-0026BB765291", false, true, "InUse"); - this.getter = getter; - this.subscriber = subscriber; - this.unsubscriber = unsubscriber; - } - - @Override - protected CompletableFuture getValue() { - return getter.get(); - } - - @Override - protected void setValue(Boolean value) throws Exception { - // Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - subscriber.accept(callback); - } - - @Override - public void unsubscribe() { - unsubscriber.run(); - } + private final Supplier> getter; + private final Consumer subscriber; + private final Runnable unsubscriber; + + public InUseCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super("000000D2-0000-1000-8000-0026BB765291", false, true, "InUse"); + this.getter = getter; + this.subscriber = subscriber; + this.unsubscriber = unsubscriber; + } + + @Override + protected CompletableFuture getValue() { + return getter.get(); + } + + @Override + protected void setValue(Boolean value) throws Exception { + // Read Only + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + subscriber.accept(callback); + } + + @Override + public void unsubscribe() { + unsubscriber.run(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/common/LowBatteryStatusCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/common/LowBatteryStatusCharacteristic.java index ad44c51e2..74fd47b7c 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/common/LowBatteryStatusCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/common/LowBatteryStatusCharacteristic.java @@ -1,44 +1,46 @@ package com.beowulfe.hap.impl.characteristics.common; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.characteristics.BooleanCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; -public class LowBatteryStatusCharacteristic extends BooleanCharacteristic implements EventableCharacteristic { - - private final Supplier> getter; - private final Consumer subscriber; - private final Runnable unsubscriber; - - public LowBatteryStatusCharacteristic(Supplier> getter, - Consumer subscriber, Runnable unsubscriber) { - super("00000079-0000-1000-8000-0026BB765291", false, true, "Status Low Battery"); - this.getter = getter; - this.subscriber = subscriber; - this.unsubscriber = unsubscriber; - } - - @Override - protected CompletableFuture getValue() { - return getter.get(); - } - - @Override - protected void setValue(Boolean value) throws Exception { - // Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - subscriber.accept(callback); - } - - @Override - public void unsubscribe() { - unsubscriber.run(); - } +public class LowBatteryStatusCharacteristic extends BooleanCharacteristic + implements EventableCharacteristic { + + private final Supplier> getter; + private final Consumer subscriber; + private final Runnable unsubscriber; + + public LowBatteryStatusCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super("00000079-0000-1000-8000-0026BB765291", false, true, "Status Low Battery"); + this.getter = getter; + this.subscriber = subscriber; + this.unsubscriber = unsubscriber; + } + + @Override + protected CompletableFuture getValue() { + return getter.get(); + } + + @Override + protected void setValue(Boolean value) throws Exception { + // Read Only + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + subscriber.accept(callback); + } + + @Override + public void unsubscribe() { + unsubscriber.run(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/common/Name.java b/src/main/java/com/beowulfe/hap/impl/characteristics/common/Name.java index a80c1941b..3a5e41eb7 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/common/Name.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/common/Name.java @@ -3,10 +3,8 @@ import com.beowulfe.hap.characteristics.StaticStringCharacteristic; public class Name extends StaticStringCharacteristic { - - public Name(String label) { - super("00000023-0000-1000-8000-0026BB765291", - "Name of the accessory", - label); - } + + public Name(String label) { + super("00000023-0000-1000-8000-0026BB765291", "Name of the accessory", label); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/common/ObstructionDetectedCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/common/ObstructionDetectedCharacteristic.java index 4cc365beb..f3910082e 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/common/ObstructionDetectedCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/common/ObstructionDetectedCharacteristic.java @@ -1,47 +1,46 @@ package com.beowulfe.hap.impl.characteristics.common; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.characteristics.BooleanCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; -public class ObstructionDetectedCharacteristic extends BooleanCharacteristic implements EventableCharacteristic { - - private final Supplier> getter; - private final Consumer subscriber; - private final Runnable unsubscriber; - - public ObstructionDetectedCharacteristic(Supplier> getter, - Consumer subscriber, Runnable unsubscriber) { - super("00000024-0000-1000-8000-0026BB765291", false, true, "An obstruction has been detected"); - this.getter = getter; - this.subscriber = subscriber; - this.unsubscriber = unsubscriber; - } - - @Override - protected void setValue(Boolean value) throws Exception { - //Read Only - } - - @Override - protected CompletableFuture getValue() { - return getter.get(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - subscriber.accept(callback); - } - - @Override - public void unsubscribe() { - unsubscriber.run(); - } - - - +public class ObstructionDetectedCharacteristic extends BooleanCharacteristic + implements EventableCharacteristic { + + private final Supplier> getter; + private final Consumer subscriber; + private final Runnable unsubscriber; + + public ObstructionDetectedCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super("00000024-0000-1000-8000-0026BB765291", false, true, "An obstruction has been detected"); + this.getter = getter; + this.subscriber = subscriber; + this.unsubscriber = unsubscriber; + } + + @Override + protected void setValue(Boolean value) throws Exception { + // Read Only + } + + @Override + protected CompletableFuture getValue() { + return getter.get(); + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + subscriber.accept(callback); + } + + @Override + public void unsubscribe() { + unsubscriber.run(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/common/PowerStateCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/common/PowerStateCharacteristic.java index 0902902ee..1479f55fb 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/common/PowerStateCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/common/PowerStateCharacteristic.java @@ -1,51 +1,50 @@ package com.beowulfe.hap.impl.characteristics.common; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.characteristics.BooleanCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.impl.ExceptionalConsumer; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class PowerStateCharacteristic extends BooleanCharacteristic + implements EventableCharacteristic { + + private final Supplier> getter; + private final ExceptionalConsumer setter; + private final Consumer subscriber; + private final Runnable unsubscriber; + + public PowerStateCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super("00000025-0000-1000-8000-0026BB765291", true, true, "Turn on and off"); + this.getter = getter; + this.setter = setter; + this.subscriber = subscriber; + this.unsubscriber = unsubscriber; + } + + @Override + public void setValue(Boolean value) throws Exception { + setter.accept(value); + } + + @Override + protected CompletableFuture getValue() { + return getter.get(); + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + subscriber.accept(callback); + } -public class PowerStateCharacteristic extends BooleanCharacteristic implements EventableCharacteristic { - - private final Supplier> getter; - private final ExceptionalConsumer setter; - private final Consumer subscriber; - private final Runnable unsubscriber; - - public PowerStateCharacteristic(Supplier> getter, ExceptionalConsumer setter, - Consumer subscriber, Runnable unsubscriber) { - super("00000025-0000-1000-8000-0026BB765291", - true, - true, - "Turn on and off"); - this.getter = getter; - this.setter = setter; - this.subscriber = subscriber; - this.unsubscriber = unsubscriber; - } - - @Override - public void setValue(Boolean value) throws Exception { - setter.accept(value); - } - - @Override - protected CompletableFuture getValue() { - return getter.get(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - subscriber.accept(callback); - } - - @Override - public void unsubscribe() { - unsubscriber.run(); - } - + @Override + public void unsubscribe() { + unsubscriber.run(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/common/RemainingDurationCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/common/RemainingDurationCharacteristic.java index 37d47c2ff..f57253de9 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/common/RemainingDurationCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/common/RemainingDurationCharacteristic.java @@ -1,44 +1,53 @@ package com.beowulfe.hap.impl.characteristics.common; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.IntegerCharacteristic; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; -public class RemainingDurationCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { - - private final Supplier> getter; - private final Consumer subscriber; - private final Runnable unsubscriber; - - public RemainingDurationCharacteristic(Supplier> getter, - Consumer subscriber, Runnable unsubscriber) { - super("000000D4-0000-1000-8000-0026BB765291", false, true, "Remaining Duration", 0, 3600, "seconds"); - this.getter = getter; - this.subscriber = subscriber; - this.unsubscriber = unsubscriber; - } - - @Override - protected CompletableFuture getValue() { - return getter.get(); - } - - @Override - protected void setValue(Integer value) throws Exception { - // Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - subscriber.accept(callback); - } - - @Override - public void unsubscribe() { - unsubscriber.run(); - } +public class RemainingDurationCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + private final Supplier> getter; + private final Consumer subscriber; + private final Runnable unsubscriber; + + public RemainingDurationCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000D4-0000-1000-8000-0026BB765291", + false, + true, + "Remaining Duration", + 0, + 3600, + "seconds"); + this.getter = getter; + this.subscriber = subscriber; + this.unsubscriber = unsubscriber; + } + + @Override + protected CompletableFuture getValue() { + return getter.get(); + } + + @Override + protected void setValue(Integer value) throws Exception { + // Read Only + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + subscriber.accept(callback); + } + + @Override + public void unsubscribe() { + unsubscriber.run(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/contactsensor/ContactSensorStateCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/contactsensor/ContactSensorStateCharacteristic.java index 57a710a02..0792d8531 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/contactsensor/ContactSensorStateCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/contactsensor/ContactSensorStateCharacteristic.java @@ -5,35 +5,35 @@ import com.beowulfe.hap.accessories.properties.ContactState; import com.beowulfe.hap.characteristics.EnumCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; - import java.util.concurrent.CompletableFuture; -public class ContactSensorStateCharacteristic extends EnumCharacteristic implements EventableCharacteristic { +public class ContactSensorStateCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { - private final ContactSensor contactSensor; + private final ContactSensor contactSensor; - public ContactSensorStateCharacteristic(ContactSensor contactSensor) { - super("0000006A-0000-1000-8000-0026BB765291", false, true, "Contact State", 1); - this.contactSensor = contactSensor; - } + public ContactSensorStateCharacteristic(ContactSensor contactSensor) { + super("0000006A-0000-1000-8000-0026BB765291", false, true, "Contact State", 1); + this.contactSensor = contactSensor; + } - @Override - protected CompletableFuture getValue() { - return contactSensor.getCurrentState().thenApply(ContactState::getCode); - } + @Override + protected CompletableFuture getValue() { + return contactSensor.getCurrentState().thenApply(ContactState::getCode); + } - @Override - protected void setValue(Integer value) throws Exception { - //Read Only - } + @Override + protected void setValue(Integer value) throws Exception { + // Read Only + } - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - contactSensor.subscribeContactState(callback); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + contactSensor.subscribeContactState(callback); + } - @Override - public void unsubscribe() { - contactSensor.unsubscribeContactState(); - } + @Override + public void unsubscribe() { + contactSensor.unsubscribeContactState(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/fan/RotationDirectionCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/fan/RotationDirectionCharacteristic.java index 16d4009be..a051ed4e2 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/fan/RotationDirectionCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/fan/RotationDirectionCharacteristic.java @@ -1,40 +1,39 @@ package com.beowulfe.hap.impl.characteristics.fan; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.Fan; import com.beowulfe.hap.accessories.properties.RotationDirection; import com.beowulfe.hap.characteristics.EnumCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; +import java.util.concurrent.CompletableFuture; -public class RotationDirectionCharacteristic extends EnumCharacteristic implements EventableCharacteristic { +public class RotationDirectionCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { - private final Fan fan; - - public RotationDirectionCharacteristic(Fan fan) { - super("00000028-0000-1000-8000-0026BB765291", true, true, "Rotation Direction", 1); - this.fan = fan; - } + private final Fan fan; - @Override - protected void setValue(Integer value) throws Exception { - fan.setRotationDirection(RotationDirection.fromCode(value)); - } + public RotationDirectionCharacteristic(Fan fan) { + super("00000028-0000-1000-8000-0026BB765291", true, true, "Rotation Direction", 1); + this.fan = fan; + } - @Override - protected CompletableFuture getValue() { - return fan.getRotationDirection().thenApply(s -> s.getCode()); - } + @Override + protected void setValue(Integer value) throws Exception { + fan.setRotationDirection(RotationDirection.fromCode(value)); + } - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - fan.subscribeRotationDirection(callback); - } + @Override + protected CompletableFuture getValue() { + return fan.getRotationDirection().thenApply(s -> s.getCode()); + } - @Override - public void unsubscribe() { - fan.unsubscribeRotationDirection(); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + fan.subscribeRotationDirection(callback); + } + @Override + public void unsubscribe() { + fan.unsubscribeRotationDirection(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/fan/RotationSpeedCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/fan/RotationSpeedCharacteristic.java index 8db60b6ed..e3c899e2a 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/fan/RotationSpeedCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/fan/RotationSpeedCharacteristic.java @@ -1,40 +1,38 @@ package com.beowulfe.hap.impl.characteristics.fan; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.Fan; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.IntegerCharacteristic; +import java.util.concurrent.CompletableFuture; -public class RotationSpeedCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { +public class RotationSpeedCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { - private final Fan fan; - - public RotationSpeedCharacteristic(Fan fan) { - super("00000029-0000-1000-8000-0026BB765291", true, true, "Rotation speed", 0, - 100, "%"); - this.fan = fan; - } + private final Fan fan; - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - fan.subscribeRotationSpeed(callback); - } + public RotationSpeedCharacteristic(Fan fan) { + super("00000029-0000-1000-8000-0026BB765291", true, true, "Rotation speed", 0, 100, "%"); + this.fan = fan; + } - @Override - public void unsubscribe() { - fan.unsubscribeRotationSpeed(); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + fan.subscribeRotationSpeed(callback); + } - @Override - protected void setValue(Integer value) throws Exception { - fan.setRotationSpeed(value); - } + @Override + public void unsubscribe() { + fan.unsubscribeRotationSpeed(); + } - @Override - protected CompletableFuture getValue() { - return fan.getRotationSpeed(); - } + @Override + protected void setValue(Integer value) throws Exception { + fan.setRotationSpeed(value); + } + @Override + protected CompletableFuture getValue() { + return fan.getRotationSpeed(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/garage/CurrentDoorStateCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/garage/CurrentDoorStateCharacteristic.java index 82bf04a0b..694ce3666 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/garage/CurrentDoorStateCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/garage/CurrentDoorStateCharacteristic.java @@ -1,40 +1,39 @@ package com.beowulfe.hap.impl.characteristics.garage; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.GarageDoor; import com.beowulfe.hap.accessories.properties.DoorState; import com.beowulfe.hap.characteristics.EnumCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; +import java.util.concurrent.CompletableFuture; -public class CurrentDoorStateCharacteristic extends EnumCharacteristic implements EventableCharacteristic { +public class CurrentDoorStateCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { - private final GarageDoor door; - - public CurrentDoorStateCharacteristic(GarageDoor door) { - super("00000032-0000-1000-8000-0026BB765291", true, true, "Target Door State", 1); - this.door = door; - } + private final GarageDoor door; - @Override - protected void setValue(Integer value) throws Exception { - door.setTargetDoorState(DoorState.fromCode(value)); - } + public CurrentDoorStateCharacteristic(GarageDoor door) { + super("00000032-0000-1000-8000-0026BB765291", true, true, "Target Door State", 1); + this.door = door; + } - @Override - protected CompletableFuture getValue() { - return door.getTargetDoorState().thenApply(s -> s.getCode()); - } + @Override + protected void setValue(Integer value) throws Exception { + door.setTargetDoorState(DoorState.fromCode(value)); + } - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - door.subscribeTargetDoorState(callback); - } + @Override + protected CompletableFuture getValue() { + return door.getTargetDoorState().thenApply(s -> s.getCode()); + } - @Override - public void unsubscribe() { - door.unsubscribeTargetDoorState(); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + door.subscribeTargetDoorState(callback); + } + @Override + public void unsubscribe() { + door.unsubscribeTargetDoorState(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/garage/TargetDoorStateCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/garage/TargetDoorStateCharacteristic.java index f7cb9ec30..843260621 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/garage/TargetDoorStateCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/garage/TargetDoorStateCharacteristic.java @@ -1,39 +1,38 @@ package com.beowulfe.hap.impl.characteristics.garage; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.GarageDoor; import com.beowulfe.hap.characteristics.EnumCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; +import java.util.concurrent.CompletableFuture; -public class TargetDoorStateCharacteristic extends EnumCharacteristic implements EventableCharacteristic { +public class TargetDoorStateCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { - private final GarageDoor door; - - public TargetDoorStateCharacteristic(GarageDoor door) { - super("0000000E-0000-1000-8000-0026BB765291", false, true, "Current Door State", 4); - this.door = door; - } + private final GarageDoor door; - @Override - protected void setValue(Integer value) throws Exception { - //Read Only - } + public TargetDoorStateCharacteristic(GarageDoor door) { + super("0000000E-0000-1000-8000-0026BB765291", false, true, "Current Door State", 4); + this.door = door; + } - @Override - protected CompletableFuture getValue() { - return door.getCurrentDoorState().thenApply(s -> s.getCode()); - } + @Override + protected void setValue(Integer value) throws Exception { + // Read Only + } - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - door.subscribeCurrentDoorState(callback); - } + @Override + protected CompletableFuture getValue() { + return door.getCurrentDoorState().thenApply(s -> s.getCode()); + } - @Override - public void unsubscribe() { - door.unsubscribeCurrentDoorState(); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + door.subscribeCurrentDoorState(callback); + } + @Override + public void unsubscribe() { + door.unsubscribeCurrentDoorState(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/humiditysensor/CurrentRelativeHumidityCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/humiditysensor/CurrentRelativeHumidityCharacteristic.java index 3c181da96..6a5380d8f 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/humiditysensor/CurrentRelativeHumidityCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/humiditysensor/CurrentRelativeHumidityCharacteristic.java @@ -1,39 +1,46 @@ package com.beowulfe.hap.impl.characteristics.humiditysensor; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.HumiditySensor; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.FloatCharacteristic; +import java.util.concurrent.CompletableFuture; + +public class CurrentRelativeHumidityCharacteristic extends FloatCharacteristic + implements EventableCharacteristic { + + private final HumiditySensor sensor; + + public CurrentRelativeHumidityCharacteristic(HumiditySensor sensor) { + super( + "00000010-0000-1000-8000-0026BB765291", + false, + true, + "Current relative humidity", + 0, + 100, + 0.1, + "%"); + this.sensor = sensor; + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + sensor.subscribeCurrentRelativeHumidity(callback); + } + + @Override + public void unsubscribe() { + sensor.unsubscribeCurrentRelativeHumidity(); + } + + @Override + protected void setValue(Double value) throws Exception { + // Read Only + } -public class CurrentRelativeHumidityCharacteristic extends FloatCharacteristic implements EventableCharacteristic { - - private final HumiditySensor sensor; - - public CurrentRelativeHumidityCharacteristic(HumiditySensor sensor) { - super("00000010-0000-1000-8000-0026BB765291", false, true, "Current relative humidity", 0, 100, - 0.1, "%"); - this.sensor = sensor; - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - sensor.subscribeCurrentRelativeHumidity(callback); - } - - @Override - public void unsubscribe() { - sensor.unsubscribeCurrentRelativeHumidity(); - } - - @Override - protected void setValue(Double value) throws Exception { - //Read Only - } - - @Override - protected CompletableFuture getDoubleValue() { - return sensor.getCurrentRelativeHumidity(); - } + @Override + protected CompletableFuture getDoubleValue() { + return sensor.getCurrentRelativeHumidity(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/information/Identify.java b/src/main/java/com/beowulfe/hap/impl/characteristics/information/Identify.java index 670f78907..16127e86c 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/information/Identify.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/information/Identify.java @@ -5,19 +5,19 @@ public class Identify extends WriteOnlyBooleanCharacteristic { - private HomekitAccessory accessory; - - public Identify(HomekitAccessory accessory) throws Exception { - super("00000014-0000-1000-8000-0026BB765291", - "Identifies the accessory via a physical action on the accessory"); - this.accessory = accessory; - } + private HomekitAccessory accessory; - @Override - public void setValue(Boolean value) throws Exception { - if (value) { - accessory.identify(); - } - } + public Identify(HomekitAccessory accessory) throws Exception { + super( + "00000014-0000-1000-8000-0026BB765291", + "Identifies the accessory via a physical action on the accessory"); + this.accessory = accessory; + } + @Override + public void setValue(Boolean value) throws Exception { + if (value) { + accessory.identify(); + } + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/information/Manufacturer.java b/src/main/java/com/beowulfe/hap/impl/characteristics/information/Manufacturer.java index 15b050cd0..6e4a207f9 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/information/Manufacturer.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/information/Manufacturer.java @@ -5,10 +5,10 @@ public class Manufacturer extends StaticStringCharacteristic { - public Manufacturer(HomekitAccessory accessory) throws Exception { - super("00000020-0000-1000-8000-0026BB765291", - "The name of the manufacturer", - accessory.getManufacturer()); - } - + public Manufacturer(HomekitAccessory accessory) throws Exception { + super( + "00000020-0000-1000-8000-0026BB765291", + "The name of the manufacturer", + accessory.getManufacturer()); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/information/Model.java b/src/main/java/com/beowulfe/hap/impl/characteristics/information/Model.java index f720809f7..4c0325f69 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/information/Model.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/information/Model.java @@ -5,10 +5,7 @@ public class Model extends StaticStringCharacteristic { - public Model(HomekitAccessory accessory) throws Exception { - super("00000021-0000-1000-8000-0026BB765291", - "The name of the model", - accessory.getModel()); - } - + public Model(HomekitAccessory accessory) throws Exception { + super("00000021-0000-1000-8000-0026BB765291", "The name of the model", accessory.getModel()); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/information/SerialNumber.java b/src/main/java/com/beowulfe/hap/impl/characteristics/information/SerialNumber.java index f5dcf71b5..ae65afffe 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/information/SerialNumber.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/information/SerialNumber.java @@ -5,10 +5,10 @@ public class SerialNumber extends StaticStringCharacteristic { - public SerialNumber(HomekitAccessory accessory) throws Exception { - super("00000030-0000-1000-8000-0026BB765291", - "The serial number of the accessory", - accessory.getSerialNumber()); - } - + public SerialNumber(HomekitAccessory accessory) throws Exception { + super( + "00000030-0000-1000-8000-0026BB765291", + "The serial number of the accessory", + accessory.getSerialNumber()); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/leaksensor/LeakDetectedStateCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/leaksensor/LeakDetectedStateCharacteristic.java index 371d0b52a..47817821d 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/leaksensor/LeakDetectedStateCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/leaksensor/LeakDetectedStateCharacteristic.java @@ -1,38 +1,38 @@ package com.beowulfe.hap.impl.characteristics.leaksensor; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.LeakSensor; import com.beowulfe.hap.characteristics.BooleanCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; +import java.util.concurrent.CompletableFuture; -public class LeakDetectedStateCharacteristic extends BooleanCharacteristic implements EventableCharacteristic { +public class LeakDetectedStateCharacteristic extends BooleanCharacteristic + implements EventableCharacteristic { - private final LeakSensor leakSensor; + private final LeakSensor leakSensor; - public LeakDetectedStateCharacteristic(LeakSensor leakSensor) { - super("00000070-0000-1000-8000-0026BB765291", false, true, "Leak Detected"); - this.leakSensor = leakSensor; - } + public LeakDetectedStateCharacteristic(LeakSensor leakSensor) { + super("00000070-0000-1000-8000-0026BB765291", false, true, "Leak Detected"); + this.leakSensor = leakSensor; + } - @Override - protected CompletableFuture getValue() { - return leakSensor.getLeakDetected(); - } + @Override + protected CompletableFuture getValue() { + return leakSensor.getLeakDetected(); + } - @Override - protected void setValue(Boolean value) throws Exception { - // Read Only - } + @Override + protected void setValue(Boolean value) throws Exception { + // Read Only + } - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - leakSensor.subscribeLeakDetected(callback); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + leakSensor.subscribeLeakDetected(callback); + } - @Override - public void unsubscribe() { - leakSensor.unsubscribeLeakDetected(); - } + @Override + public void unsubscribe() { + leakSensor.unsubscribeLeakDetected(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/light/AmbientLightLevelCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/light/AmbientLightLevelCharacteristic.java index 32797ca83..b9838b714 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/light/AmbientLightLevelCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/light/AmbientLightLevelCharacteristic.java @@ -4,36 +4,43 @@ import com.beowulfe.hap.accessories.LightSensor; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.FloatCharacteristic; - import java.util.concurrent.CompletableFuture; -public class AmbientLightLevelCharacteristic extends FloatCharacteristic implements EventableCharacteristic { - - private final LightSensor lightSensor; - - public AmbientLightLevelCharacteristic(LightSensor lightSensor) { - super("0000006B-0000-1000-8000-0026BB765291", false, true, "Current ambient light level", 0.0001, 100000, - 0.0001, "lux"); - this.lightSensor = lightSensor; - } - - @Override - protected void setValue(Double value) throws Exception { - //Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - lightSensor.subscribeCurrentAmbientLightLevel(callback); - } - - @Override - public void unsubscribe() { - lightSensor.unsubscribeCurrentAmbientLightLevel(); - } - - @Override - protected CompletableFuture getDoubleValue() { - return lightSensor.getCurrentAmbientLightLevel(); - } +public class AmbientLightLevelCharacteristic extends FloatCharacteristic + implements EventableCharacteristic { + + private final LightSensor lightSensor; + + public AmbientLightLevelCharacteristic(LightSensor lightSensor) { + super( + "0000006B-0000-1000-8000-0026BB765291", + false, + true, + "Current ambient light level", + 0.0001, + 100000, + 0.0001, + "lux"); + this.lightSensor = lightSensor; + } + + @Override + protected void setValue(Double value) throws Exception { + // Read Only + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + lightSensor.subscribeCurrentAmbientLightLevel(callback); + } + + @Override + public void unsubscribe() { + lightSensor.unsubscribeCurrentAmbientLightLevel(); + } + + @Override + protected CompletableFuture getDoubleValue() { + return lightSensor.getCurrentAmbientLightLevel(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/lightbulb/BrightnessCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/lightbulb/BrightnessCharacteristic.java index 27da3fa57..3cd303332 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/lightbulb/BrightnessCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/lightbulb/BrightnessCharacteristic.java @@ -1,40 +1,45 @@ package com.beowulfe.hap.impl.characteristics.lightbulb; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.DimmableLightbulb; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.IntegerCharacteristic; +import java.util.concurrent.CompletableFuture; -public class BrightnessCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { - - private final DimmableLightbulb lightbulb; - - public BrightnessCharacteristic(DimmableLightbulb lightbulb) { - super("00000008-0000-1000-8000-0026BB765291", true, true, "Adjust brightness of the light", 0, - 100, "%"); - this.lightbulb = lightbulb; - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - lightbulb.subscribeBrightness(callback); - } - - @Override - public void unsubscribe() { - lightbulb.unsubscribeBrightness(); - } - - @Override - protected void setValue(Integer value) throws Exception { - lightbulb.setBrightness(value); - } - - @Override - protected CompletableFuture getValue() { - return lightbulb.getBrightness(); - } - +public class BrightnessCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + private final DimmableLightbulb lightbulb; + + public BrightnessCharacteristic(DimmableLightbulb lightbulb) { + super( + "00000008-0000-1000-8000-0026BB765291", + true, + true, + "Adjust brightness of the light", + 0, + 100, + "%"); + this.lightbulb = lightbulb; + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + lightbulb.subscribeBrightness(callback); + } + + @Override + public void unsubscribe() { + lightbulb.unsubscribeBrightness(); + } + + @Override + protected void setValue(Integer value) throws Exception { + lightbulb.setBrightness(value); + } + + @Override + protected CompletableFuture getValue() { + return lightbulb.getBrightness(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/lightbulb/HueCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/lightbulb/HueCharacteristic.java index a17b41f14..561a74b39 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/lightbulb/HueCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/lightbulb/HueCharacteristic.java @@ -1,41 +1,45 @@ package com.beowulfe.hap.impl.characteristics.lightbulb; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.ColorfulLightbulb; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.FloatCharacteristic; +import java.util.concurrent.CompletableFuture; public class HueCharacteristic extends FloatCharacteristic implements EventableCharacteristic { - private final ColorfulLightbulb lightbulb; - - public HueCharacteristic(ColorfulLightbulb lightbulb) { - super("00000013-0000-1000-8000-0026BB765291", true, true, "Adjust hue of the light", 0, 360, 1, "arcdegrees"); - this.lightbulb = lightbulb; - } - - @Override - protected void setValue(Double value) throws Exception { - lightbulb.setHue(value); - } - - @Override - protected CompletableFuture getDoubleValue() { - return lightbulb.getHue(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - lightbulb.subscribeHue(callback); - } - - @Override - public void unsubscribe() { - lightbulb.unsubscribeHue(); - } - - - + private final ColorfulLightbulb lightbulb; + + public HueCharacteristic(ColorfulLightbulb lightbulb) { + super( + "00000013-0000-1000-8000-0026BB765291", + true, + true, + "Adjust hue of the light", + 0, + 360, + 1, + "arcdegrees"); + this.lightbulb = lightbulb; + } + + @Override + protected void setValue(Double value) throws Exception { + lightbulb.setHue(value); + } + + @Override + protected CompletableFuture getDoubleValue() { + return lightbulb.getHue(); + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + lightbulb.subscribeHue(callback); + } + + @Override + public void unsubscribe() { + lightbulb.unsubscribeHue(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/lightbulb/SaturationCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/lightbulb/SaturationCharacteristic.java index ae2e4d183..29d39da3b 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/lightbulb/SaturationCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/lightbulb/SaturationCharacteristic.java @@ -1,40 +1,46 @@ package com.beowulfe.hap.impl.characteristics.lightbulb; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.ColorfulLightbulb; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.FloatCharacteristic; +import java.util.concurrent.CompletableFuture; -public class SaturationCharacteristic extends FloatCharacteristic implements EventableCharacteristic { - - private final ColorfulLightbulb lightbulb; - - public SaturationCharacteristic(ColorfulLightbulb lightbulb) { - super("0000002F-0000-1000-8000-0026BB765291", true, true, "Adjust saturation of the light", 0, - 100, 1, "%"); - this.lightbulb = lightbulb; - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - lightbulb.subscribeSaturation(callback); - } - - @Override - public void unsubscribe() { - lightbulb.unsubscribeSaturation(); - } - - @Override - protected void setValue(Double value) throws Exception { - lightbulb.setSaturation(value); - } - - @Override - protected CompletableFuture getDoubleValue() { - return lightbulb.getSaturation(); - } - +public class SaturationCharacteristic extends FloatCharacteristic + implements EventableCharacteristic { + + private final ColorfulLightbulb lightbulb; + + public SaturationCharacteristic(ColorfulLightbulb lightbulb) { + super( + "0000002F-0000-1000-8000-0026BB765291", + true, + true, + "Adjust saturation of the light", + 0, + 100, + 1, + "%"); + this.lightbulb = lightbulb; + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + lightbulb.subscribeSaturation(callback); + } + + @Override + public void unsubscribe() { + lightbulb.unsubscribeSaturation(); + } + + @Override + protected void setValue(Double value) throws Exception { + lightbulb.setSaturation(value); + } + + @Override + protected CompletableFuture getDoubleValue() { + return lightbulb.getSaturation(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/lock/mechanism/CurrentLockMechanismStateCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/lock/mechanism/CurrentLockMechanismStateCharacteristic.java index 6cb0c247d..0fa3922f1 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/lock/mechanism/CurrentLockMechanismStateCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/lock/mechanism/CurrentLockMechanismStateCharacteristic.java @@ -1,39 +1,38 @@ package com.beowulfe.hap.impl.characteristics.lock.mechanism; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.LockMechanism; import com.beowulfe.hap.characteristics.EnumCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; +import java.util.concurrent.CompletableFuture; -public class CurrentLockMechanismStateCharacteristic extends EnumCharacteristic implements EventableCharacteristic { +public class CurrentLockMechanismStateCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { - private final LockMechanism lock; - - public CurrentLockMechanismStateCharacteristic(LockMechanism lock) { - super("0000001D-0000-1000-8000-0026BB765291", false, true, "Current lock state", 3); - this.lock = lock; - } + private final LockMechanism lock; - @Override - protected void setValue(Integer value) throws Exception { - //Not writable - } + public CurrentLockMechanismStateCharacteristic(LockMechanism lock) { + super("0000001D-0000-1000-8000-0026BB765291", false, true, "Current lock state", 3); + this.lock = lock; + } - @Override - protected CompletableFuture getValue() { - return lock.getCurrentMechanismState().thenApply(s -> s.getCode()); - } + @Override + protected void setValue(Integer value) throws Exception { + // Not writable + } - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - lock.subscribeCurrentMechanismState(callback); - } + @Override + protected CompletableFuture getValue() { + return lock.getCurrentMechanismState().thenApply(s -> s.getCode()); + } - @Override - public void unsubscribe() { - lock.unsubscribeCurrentMechanismState(); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + lock.subscribeCurrentMechanismState(callback); + } + @Override + public void unsubscribe() { + lock.unsubscribeCurrentMechanismState(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/lock/mechanism/TargetLockMechanismStateCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/lock/mechanism/TargetLockMechanismStateCharacteristic.java index 65c5bc469..d9e6b147c 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/lock/mechanism/TargetLockMechanismStateCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/lock/mechanism/TargetLockMechanismStateCharacteristic.java @@ -1,40 +1,39 @@ package com.beowulfe.hap.impl.characteristics.lock.mechanism; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.LockableLockMechanism; import com.beowulfe.hap.accessories.properties.LockMechanismState; import com.beowulfe.hap.characteristics.EnumCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; +import java.util.concurrent.CompletableFuture; -public class TargetLockMechanismStateCharacteristic extends EnumCharacteristic implements EventableCharacteristic { +public class TargetLockMechanismStateCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { - private final LockableLockMechanism lock; - - public TargetLockMechanismStateCharacteristic(LockableLockMechanism lock) { - super("0000001E-0000-1000-8000-0026BB765291", true, true, "Current lock state", 3); - this.lock = lock; - } + private final LockableLockMechanism lock; - @Override - protected void setValue(Integer value) throws Exception { - lock.setTargetMechanismState(LockMechanismState.fromCode(value)); - } + public TargetLockMechanismStateCharacteristic(LockableLockMechanism lock) { + super("0000001E-0000-1000-8000-0026BB765291", true, true, "Current lock state", 3); + this.lock = lock; + } - @Override - protected CompletableFuture getValue() { - return lock.getTargetMechanismState().thenApply(s -> s.getCode()); - } + @Override + protected void setValue(Integer value) throws Exception { + lock.setTargetMechanismState(LockMechanismState.fromCode(value)); + } - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - lock.subscribeTargetMechanismState(callback); - } + @Override + protected CompletableFuture getValue() { + return lock.getTargetMechanismState().thenApply(s -> s.getCode()); + } - @Override - public void unsubscribe() { - lock.unsubscribeTargetMechanismState(); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + lock.subscribeTargetMechanismState(callback); + } + @Override + public void unsubscribe() { + lock.unsubscribeTargetMechanismState(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/motionsensor/MotionDetectedStateCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/motionsensor/MotionDetectedStateCharacteristic.java index 3d02299e2..064474ae3 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/motionsensor/MotionDetectedStateCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/motionsensor/MotionDetectedStateCharacteristic.java @@ -4,35 +4,35 @@ import com.beowulfe.hap.accessories.MotionSensor; import com.beowulfe.hap.characteristics.BooleanCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; - import java.util.concurrent.CompletableFuture; -public class MotionDetectedStateCharacteristic extends BooleanCharacteristic implements EventableCharacteristic { +public class MotionDetectedStateCharacteristic extends BooleanCharacteristic + implements EventableCharacteristic { - private final MotionSensor motionSensor; + private final MotionSensor motionSensor; - public MotionDetectedStateCharacteristic(MotionSensor motionSensor) { - super("00000022-0000-1000-8000-0026BB765291", false, true, "Motion Detected"); - this.motionSensor = motionSensor; - } + public MotionDetectedStateCharacteristic(MotionSensor motionSensor) { + super("00000022-0000-1000-8000-0026BB765291", false, true, "Motion Detected"); + this.motionSensor = motionSensor; + } - @Override - protected CompletableFuture getValue() { - return motionSensor.getMotionDetected(); - } + @Override + protected CompletableFuture getValue() { + return motionSensor.getMotionDetected(); + } - @Override - protected void setValue(Boolean value) throws Exception { - //Read Only - } + @Override + protected void setValue(Boolean value) throws Exception { + // Read Only + } - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - motionSensor.subscribeMotionDetected(callback); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + motionSensor.subscribeMotionDetected(callback); + } - @Override - public void unsubscribe() { - motionSensor.unsubscribeMotionDetected(); - } + @Override + public void unsubscribe() { + motionSensor.unsubscribeMotionDetected(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/outlet/OutletInUseCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/outlet/OutletInUseCharacteristic.java index 110575936..2edf41e0b 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/outlet/OutletInUseCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/outlet/OutletInUseCharacteristic.java @@ -1,41 +1,38 @@ package com.beowulfe.hap.impl.characteristics.outlet; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.Outlet; import com.beowulfe.hap.characteristics.BooleanCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; +import java.util.concurrent.CompletableFuture; -public class OutletInUseCharacteristic extends BooleanCharacteristic implements EventableCharacteristic { - - private final Outlet outlet; - - public OutletInUseCharacteristic(Outlet outlet) { - super("00000026-0000-1000-8000-0026BB765291", false, true, "The outlet is in use"); - this.outlet = outlet; - } +public class OutletInUseCharacteristic extends BooleanCharacteristic + implements EventableCharacteristic { - @Override - protected void setValue(Boolean value) throws Exception { - //Read Only - } + private final Outlet outlet; - @Override - protected CompletableFuture getValue() { - return outlet.getOutletInUse(); - } + public OutletInUseCharacteristic(Outlet outlet) { + super("00000026-0000-1000-8000-0026BB765291", false, true, "The outlet is in use"); + this.outlet = outlet; + } - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - outlet.subscribeOutletInUse(callback); - } + @Override + protected void setValue(Boolean value) throws Exception { + // Read Only + } - @Override - public void unsubscribe() { - outlet.unsubscribeOutletInUse(); - } + @Override + protected CompletableFuture getValue() { + return outlet.getOutletInUse(); + } - + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + outlet.subscribeOutletInUse(callback); + } + @Override + public void unsubscribe() { + outlet.unsubscribeOutletInUse(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/securitysystem/CurrentSecuritySystemStateCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/securitysystem/CurrentSecuritySystemStateCharacteristic.java index 327a45a49..f97de285f 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/securitysystem/CurrentSecuritySystemStateCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/securitysystem/CurrentSecuritySystemStateCharacteristic.java @@ -5,35 +5,37 @@ import com.beowulfe.hap.accessories.properties.CurrentSecuritySystemState; import com.beowulfe.hap.characteristics.EnumCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; - import java.util.concurrent.CompletableFuture; -public class CurrentSecuritySystemStateCharacteristic extends EnumCharacteristic implements EventableCharacteristic { - - private final SecuritySystem securitySystem; - - public CurrentSecuritySystemStateCharacteristic(SecuritySystem securitySystem) { - super("00000066-0000-1000-8000-0026BB765291", false, true, "Current security system state", 4); - this.securitySystem = securitySystem; - } - - @Override - protected CompletableFuture getValue() { - return securitySystem.getCurrentSecuritySystemState().thenApply(CurrentSecuritySystemState::getCode); - } - - @Override - protected void setValue(Integer value) throws Exception { - //Not writable - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - securitySystem.subscribeCurrentSecuritySystemState(callback); - } - - @Override - public void unsubscribe() { - securitySystem.unsubscribeCurrentSecuritySystemState(); - } +public class CurrentSecuritySystemStateCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { + + private final SecuritySystem securitySystem; + + public CurrentSecuritySystemStateCharacteristic(SecuritySystem securitySystem) { + super("00000066-0000-1000-8000-0026BB765291", false, true, "Current security system state", 4); + this.securitySystem = securitySystem; + } + + @Override + protected CompletableFuture getValue() { + return securitySystem + .getCurrentSecuritySystemState() + .thenApply(CurrentSecuritySystemState::getCode); + } + + @Override + protected void setValue(Integer value) throws Exception { + // Not writable + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + securitySystem.subscribeCurrentSecuritySystemState(callback); + } + + @Override + public void unsubscribe() { + securitySystem.unsubscribeCurrentSecuritySystemState(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/securitysystem/SecuritySystemAlarmTypeCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/securitysystem/SecuritySystemAlarmTypeCharacteristic.java index 680089b61..9bbaed53a 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/securitysystem/SecuritySystemAlarmTypeCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/securitysystem/SecuritySystemAlarmTypeCharacteristic.java @@ -5,35 +5,35 @@ import com.beowulfe.hap.accessories.properties.SecuritySystemAlarmType; import com.beowulfe.hap.characteristics.EnumCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; - import java.util.concurrent.CompletableFuture; -public class SecuritySystemAlarmTypeCharacteristic extends EnumCharacteristic implements EventableCharacteristic { +public class SecuritySystemAlarmTypeCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { - private final SecuritySystem securitySystem; + private final SecuritySystem securitySystem; - public SecuritySystemAlarmTypeCharacteristic(SecuritySystem securitySystem) { - super("0000008E-0000-1000-8000-0026BB765291", false, true, "Security system alarm type", 1); - this.securitySystem = securitySystem; - } + public SecuritySystemAlarmTypeCharacteristic(SecuritySystem securitySystem) { + super("0000008E-0000-1000-8000-0026BB765291", false, true, "Security system alarm type", 1); + this.securitySystem = securitySystem; + } - @Override - protected CompletableFuture getValue() { - return securitySystem.getAlarmTypeState().thenApply(SecuritySystemAlarmType::getCode); - } + @Override + protected CompletableFuture getValue() { + return securitySystem.getAlarmTypeState().thenApply(SecuritySystemAlarmType::getCode); + } - @Override - protected void setValue(Integer value) throws Exception { - //Not writable - } + @Override + protected void setValue(Integer value) throws Exception { + // Not writable + } - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - securitySystem.subscribeAlarmTypeState(callback); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + securitySystem.subscribeAlarmTypeState(callback); + } - @Override - public void unsubscribe() { - securitySystem.unsubscribeAlarmTypeState(); - } + @Override + public void unsubscribe() { + securitySystem.unsubscribeAlarmTypeState(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/securitysystem/TargetSecuritySystemStateCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/securitysystem/TargetSecuritySystemStateCharacteristic.java index 7fc53fa72..c6d5910f2 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/securitysystem/TargetSecuritySystemStateCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/securitysystem/TargetSecuritySystemStateCharacteristic.java @@ -5,35 +5,37 @@ import com.beowulfe.hap.accessories.properties.TargetSecuritySystemState; import com.beowulfe.hap.characteristics.EnumCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; - import java.util.concurrent.CompletableFuture; -public class TargetSecuritySystemStateCharacteristic extends EnumCharacteristic implements EventableCharacteristic { - - private final SecuritySystem securitySystem; - - public TargetSecuritySystemStateCharacteristic(SecuritySystem securitySystem) { - super("00000067-0000-1000-8000-0026BB765291", true, true, "Target security system state", 3); - this.securitySystem = securitySystem; - } - - @Override - protected CompletableFuture getValue() { - return securitySystem.getTargetSecuritySystemState().thenApply(TargetSecuritySystemState::getCode); - } - - @Override - protected void setValue(Integer value) throws Exception { - securitySystem.setTargetSecuritySystemState(TargetSecuritySystemState.fromCode(value)); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - securitySystem.subscribeTargetSecuritySystemState(callback); - } - - @Override - public void unsubscribe() { - securitySystem.unsubscribeTargetSecuritySystemState(); - } +public class TargetSecuritySystemStateCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { + + private final SecuritySystem securitySystem; + + public TargetSecuritySystemStateCharacteristic(SecuritySystem securitySystem) { + super("00000067-0000-1000-8000-0026BB765291", true, true, "Target security system state", 3); + this.securitySystem = securitySystem; + } + + @Override + protected CompletableFuture getValue() { + return securitySystem + .getTargetSecuritySystemState() + .thenApply(TargetSecuritySystemState::getCode); + } + + @Override + protected void setValue(Integer value) throws Exception { + securitySystem.setTargetSecuritySystemState(TargetSecuritySystemState.fromCode(value)); + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + securitySystem.subscribeTargetSecuritySystemState(callback); + } + + @Override + public void unsubscribe() { + securitySystem.unsubscribeTargetSecuritySystemState(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/smokesensor/SmokeDetectedCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/smokesensor/SmokeDetectedCharacteristic.java index c351697d8..2de1a8c6b 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/smokesensor/SmokeDetectedCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/smokesensor/SmokeDetectedCharacteristic.java @@ -5,35 +5,35 @@ import com.beowulfe.hap.accessories.properties.SmokeDetectedState; import com.beowulfe.hap.characteristics.EnumCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; - import java.util.concurrent.CompletableFuture; -public class SmokeDetectedCharacteristic extends EnumCharacteristic implements EventableCharacteristic { +public class SmokeDetectedCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { - private final SmokeSensor smokeSensor; + private final SmokeSensor smokeSensor; - public SmokeDetectedCharacteristic(SmokeSensor smokeSensor) { - super("00000076-0000-1000-8000-0026BB765291", false, true, "Smoke Detected", 1); - this.smokeSensor = smokeSensor; - } + public SmokeDetectedCharacteristic(SmokeSensor smokeSensor) { + super("00000076-0000-1000-8000-0026BB765291", false, true, "Smoke Detected", 1); + this.smokeSensor = smokeSensor; + } - @Override - protected CompletableFuture getValue() { - return smokeSensor.getSmokeDetectedState().thenApply(SmokeDetectedState::getCode); - } + @Override + protected CompletableFuture getValue() { + return smokeSensor.getSmokeDetectedState().thenApply(SmokeDetectedState::getCode); + } - @Override - protected void setValue(Integer value) throws Exception { - //Read Only - } + @Override + protected void setValue(Integer value) throws Exception { + // Read Only + } - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - smokeSensor.subscribeSmokeDetectedState(callback); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + smokeSensor.subscribeSmokeDetectedState(callback); + } - @Override - public void unsubscribe() { - smokeSensor.unsubscribeSmokeDetectedState(); - } + @Override + public void unsubscribe() { + smokeSensor.unsubscribeSmokeDetectedState(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/AbstractHeatingCoolingModeCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/AbstractHeatingCoolingModeCharacteristic.java index 1966ac9b4..e0ac363eb 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/AbstractHeatingCoolingModeCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/AbstractHeatingCoolingModeCharacteristic.java @@ -1,30 +1,29 @@ package com.beowulfe.hap.impl.characteristics.thermostat; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.accessories.properties.ThermostatMode; import com.beowulfe.hap.characteristics.EnumCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; +import java.util.concurrent.CompletableFuture; + +abstract class AbstractHeatingCoolingModeCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { -abstract class AbstractHeatingCoolingModeCharacteristic extends EnumCharacteristic implements EventableCharacteristic { + public AbstractHeatingCoolingModeCharacteristic( + String type, boolean isWritable, String description) { + super(type, isWritable, true, description, 3); + } - public AbstractHeatingCoolingModeCharacteristic(String type, boolean isWritable, String description) { - super(type, isWritable, true, description, 3); - } + @Override + protected final void setValue(Integer value) throws Exception { + setModeValue(ThermostatMode.fromCode(value)); + } - @Override - protected final void setValue(Integer value) throws Exception { - setModeValue(ThermostatMode.fromCode(value)); - } + @Override + protected final CompletableFuture getValue() { + return getModeValue().thenApply(t -> t.getCode()); + } - @Override - protected final CompletableFuture getValue() { - return getModeValue().thenApply(t -> t.getCode()); - } + protected abstract void setModeValue(ThermostatMode mode) throws Exception; - protected abstract void setModeValue(ThermostatMode mode) throws Exception; - - protected abstract CompletableFuture getModeValue(); - - + protected abstract CompletableFuture getModeValue(); } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/AbstractTemperatureCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/AbstractTemperatureCharacteristic.java index 090680068..28a2edf18 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/AbstractTemperatureCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/AbstractTemperatureCharacteristic.java @@ -4,11 +4,19 @@ import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.FloatCharacteristic; -public abstract class AbstractTemperatureCharacteristic extends FloatCharacteristic implements EventableCharacteristic { - - public AbstractTemperatureCharacteristic(String type, boolean isWritable, String description, TemperatureSensor sensor) { - super(type, isWritable, true, description, sensor.getMinimumTemperature(), sensor.getMaximumTemperature(), - 0.1, "celsius"); - } +public abstract class AbstractTemperatureCharacteristic extends FloatCharacteristic + implements EventableCharacteristic { + public AbstractTemperatureCharacteristic( + String type, boolean isWritable, String description, TemperatureSensor sensor) { + super( + type, + isWritable, + true, + description, + sensor.getMinimumTemperature(), + sensor.getMaximumTemperature(), + 0.1, + "celsius"); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/CoolingThresholdTemperatureCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/CoolingThresholdTemperatureCharacteristic.java index c509d97a4..2c17598d0 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/CoolingThresholdTemperatureCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/CoolingThresholdTemperatureCharacteristic.java @@ -1,38 +1,39 @@ package com.beowulfe.hap.impl.characteristics.thermostat; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.thermostat.CoolingThermostat; +import java.util.concurrent.CompletableFuture; -public class CoolingThresholdTemperatureCharacteristic extends - AbstractTemperatureCharacteristic { - - private final CoolingThermostat thermostat; - - public CoolingThresholdTemperatureCharacteristic(CoolingThermostat thermostat) { - super("0000000D-0000-1000-8000-0026BB765291", true, "Temperature above which cooling will be active", thermostat); - this.thermostat = thermostat; - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - thermostat.subscribeCoolingThresholdTemperature(callback); - } - - @Override - public void unsubscribe() { - thermostat.unsubscribeCoolingThresholdTemperature(); - } - - @Override - protected CompletableFuture getDoubleValue() { - return thermostat.getCoolingThresholdTemperature(); - } - - @Override - protected void setValue(Double value) throws Exception { - thermostat.setCoolingThresholdTemperature(value); - } - +public class CoolingThresholdTemperatureCharacteristic extends AbstractTemperatureCharacteristic { + + private final CoolingThermostat thermostat; + + public CoolingThresholdTemperatureCharacteristic(CoolingThermostat thermostat) { + super( + "0000000D-0000-1000-8000-0026BB765291", + true, + "Temperature above which cooling will be active", + thermostat); + this.thermostat = thermostat; + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + thermostat.subscribeCoolingThresholdTemperature(callback); + } + + @Override + public void unsubscribe() { + thermostat.unsubscribeCoolingThresholdTemperature(); + } + + @Override + protected CompletableFuture getDoubleValue() { + return thermostat.getCoolingThresholdTemperature(); + } + + @Override + protected void setValue(Double value) throws Exception { + thermostat.setCoolingThresholdTemperature(value); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/CurrentHeatingCoolingModeCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/CurrentHeatingCoolingModeCharacteristic.java index a6ba57899..0d5d7aaf3 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/CurrentHeatingCoolingModeCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/CurrentHeatingCoolingModeCharacteristic.java @@ -1,39 +1,37 @@ package com.beowulfe.hap.impl.characteristics.thermostat; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.properties.ThermostatMode; import com.beowulfe.hap.accessories.thermostat.BasicThermostat; +import java.util.concurrent.CompletableFuture; -public class CurrentHeatingCoolingModeCharacteristic extends - AbstractHeatingCoolingModeCharacteristic { - - private final BasicThermostat thermostat; +public class CurrentHeatingCoolingModeCharacteristic + extends AbstractHeatingCoolingModeCharacteristic { - public CurrentHeatingCoolingModeCharacteristic(BasicThermostat thermostat) { - super("0000000F-0000-1000-8000-0026BB765291", false, "Current Mode"); - this.thermostat = thermostat; - } + private final BasicThermostat thermostat; - @Override - protected void setModeValue(ThermostatMode mode) throws Exception { - //Not writable - } + public CurrentHeatingCoolingModeCharacteristic(BasicThermostat thermostat) { + super("0000000F-0000-1000-8000-0026BB765291", false, "Current Mode"); + this.thermostat = thermostat; + } - @Override - protected CompletableFuture getModeValue() { - return thermostat.getCurrentMode(); - } + @Override + protected void setModeValue(ThermostatMode mode) throws Exception { + // Not writable + } - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - thermostat.subscribeCurrentMode(callback); - } + @Override + protected CompletableFuture getModeValue() { + return thermostat.getCurrentMode(); + } - @Override - public void unsubscribe() { - thermostat.unsubscribeCurrentMode(); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + thermostat.subscribeCurrentMode(callback); + } + @Override + public void unsubscribe() { + thermostat.unsubscribeCurrentMode(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/CurrentTemperatureCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/CurrentTemperatureCharacteristic.java index cf065cf6c..af8b3ca62 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/CurrentTemperatureCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/CurrentTemperatureCharacteristic.java @@ -1,38 +1,35 @@ package com.beowulfe.hap.impl.characteristics.thermostat; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.TemperatureSensor; +import java.util.concurrent.CompletableFuture; -public class CurrentTemperatureCharacteristic extends - AbstractTemperatureCharacteristic { +public class CurrentTemperatureCharacteristic extends AbstractTemperatureCharacteristic { - private final TemperatureSensor sensor; - - public CurrentTemperatureCharacteristic(TemperatureSensor thermostat) { - super("00000011-0000-1000-8000-0026BB765291", false, "Current Temperature", thermostat); - this.sensor = thermostat; - } + private final TemperatureSensor sensor; - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - sensor.subscribeCurrentTemperature(callback); - } + public CurrentTemperatureCharacteristic(TemperatureSensor thermostat) { + super("00000011-0000-1000-8000-0026BB765291", false, "Current Temperature", thermostat); + this.sensor = thermostat; + } - @Override - public void unsubscribe() { - sensor.unsubscribeCurrentTemperature(); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + sensor.subscribeCurrentTemperature(callback); + } - @Override - protected CompletableFuture getDoubleValue() { - return sensor.getCurrentTemperature(); - } + @Override + public void unsubscribe() { + sensor.unsubscribeCurrentTemperature(); + } - @Override - protected void setValue(Double value) throws Exception { - //Not writable - } + @Override + protected CompletableFuture getDoubleValue() { + return sensor.getCurrentTemperature(); + } + @Override + protected void setValue(Double value) throws Exception { + // Not writable + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/HeatingThresholdTemperatureCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/HeatingThresholdTemperatureCharacteristic.java index 756a749fa..aa0427c52 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/HeatingThresholdTemperatureCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/HeatingThresholdTemperatureCharacteristic.java @@ -1,38 +1,39 @@ package com.beowulfe.hap.impl.characteristics.thermostat; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.thermostat.HeatingThermostat; +import java.util.concurrent.CompletableFuture; -public class HeatingThresholdTemperatureCharacteristic extends - AbstractTemperatureCharacteristic { - - private final HeatingThermostat thermostat; - - public HeatingThresholdTemperatureCharacteristic(HeatingThermostat thermostat) { - super("00000012-0000-1000-8000-0026BB765291", true, "Temperature below which heating will be active", thermostat); - this.thermostat = thermostat; - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - thermostat.subscribeHeatingThresholdTemperature(callback); - } - - @Override - public void unsubscribe() { - thermostat.unsubscribeHeatingThresholdTemperature(); - } - - @Override - protected CompletableFuture getDoubleValue() { - return thermostat.getHeatingThresholdTemperature(); - } - - @Override - protected void setValue(Double value) throws Exception { - thermostat.setHeatingThresholdTemperature(value); - } - +public class HeatingThresholdTemperatureCharacteristic extends AbstractTemperatureCharacteristic { + + private final HeatingThermostat thermostat; + + public HeatingThresholdTemperatureCharacteristic(HeatingThermostat thermostat) { + super( + "00000012-0000-1000-8000-0026BB765291", + true, + "Temperature below which heating will be active", + thermostat); + this.thermostat = thermostat; + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + thermostat.subscribeHeatingThresholdTemperature(callback); + } + + @Override + public void unsubscribe() { + thermostat.unsubscribeHeatingThresholdTemperature(); + } + + @Override + protected CompletableFuture getDoubleValue() { + return thermostat.getHeatingThresholdTemperature(); + } + + @Override + protected void setValue(Double value) throws Exception { + thermostat.setHeatingThresholdTemperature(value); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/TargetHeatingCoolingModeCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/TargetHeatingCoolingModeCharacteristic.java index f12725dea..de126ccb2 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/TargetHeatingCoolingModeCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/TargetHeatingCoolingModeCharacteristic.java @@ -1,39 +1,37 @@ package com.beowulfe.hap.impl.characteristics.thermostat; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.properties.ThermostatMode; import com.beowulfe.hap.accessories.thermostat.BasicThermostat; +import java.util.concurrent.CompletableFuture; -public class TargetHeatingCoolingModeCharacteristic extends - AbstractHeatingCoolingModeCharacteristic { +public class TargetHeatingCoolingModeCharacteristic + extends AbstractHeatingCoolingModeCharacteristic { - private final BasicThermostat thermostat; - - public TargetHeatingCoolingModeCharacteristic(BasicThermostat thermostat) { - super("00000033-0000-1000-8000-0026BB765291", true, "Target Mode"); - this.thermostat = thermostat; - } + private final BasicThermostat thermostat; - @Override - protected void setModeValue(ThermostatMode mode) throws Exception { - thermostat.setTargetMode(mode); - } + public TargetHeatingCoolingModeCharacteristic(BasicThermostat thermostat) { + super("00000033-0000-1000-8000-0026BB765291", true, "Target Mode"); + this.thermostat = thermostat; + } - @Override - protected CompletableFuture getModeValue() { - return thermostat.getTargetMode(); - } + @Override + protected void setModeValue(ThermostatMode mode) throws Exception { + thermostat.setTargetMode(mode); + } - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - thermostat.subscribeTargetMode(callback); - } + @Override + protected CompletableFuture getModeValue() { + return thermostat.getTargetMode(); + } - @Override - public void unsubscribe() { - thermostat.unsubscribeTargetMode(); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + thermostat.subscribeTargetMode(callback); + } + @Override + public void unsubscribe() { + thermostat.unsubscribeTargetMode(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/TargetTemperatureCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/TargetTemperatureCharacteristic.java index a1140a92f..278ea7ea0 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/TargetTemperatureCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/TargetTemperatureCharacteristic.java @@ -1,38 +1,35 @@ package com.beowulfe.hap.impl.characteristics.thermostat; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.thermostat.BasicThermostat; +import java.util.concurrent.CompletableFuture; -public class TargetTemperatureCharacteristic extends - AbstractTemperatureCharacteristic { - - private final BasicThermostat thermostat; +public class TargetTemperatureCharacteristic extends AbstractTemperatureCharacteristic { - public TargetTemperatureCharacteristic(BasicThermostat thermostat) { - super("00000035-0000-1000-8000-0026BB765291", true, "Target Temperature", thermostat); - this.thermostat = thermostat; - } + private final BasicThermostat thermostat; - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - thermostat.subscribeTargetTemperature(callback); - } + public TargetTemperatureCharacteristic(BasicThermostat thermostat) { + super("00000035-0000-1000-8000-0026BB765291", true, "Target Temperature", thermostat); + this.thermostat = thermostat; + } - @Override - public void unsubscribe() { - thermostat.unsubscribeTargetTemperature(); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + thermostat.subscribeTargetTemperature(callback); + } - @Override - protected CompletableFuture getDoubleValue() { - return thermostat.getTargetTemperature(); - } + @Override + public void unsubscribe() { + thermostat.unsubscribeTargetTemperature(); + } - @Override - protected void setValue(Double value) throws Exception { - thermostat.setTargetTemperature(value); - } + @Override + protected CompletableFuture getDoubleValue() { + return thermostat.getTargetTemperature(); + } + @Override + protected void setValue(Double value) throws Exception { + thermostat.setTargetTemperature(value); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/TemperatureUnitsCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/TemperatureUnitsCharacteristic.java index 87dd0a2bc..2a884a966 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/TemperatureUnitsCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/thermostat/TemperatureUnitsCharacteristic.java @@ -1,27 +1,25 @@ package com.beowulfe.hap.impl.characteristics.thermostat; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.accessories.thermostat.BasicThermostat; import com.beowulfe.hap.characteristics.EnumCharacteristic; +import java.util.concurrent.CompletableFuture; public class TemperatureUnitsCharacteristic extends EnumCharacteristic { - private final BasicThermostat thermostat; - - public TemperatureUnitsCharacteristic(BasicThermostat thermostat) { - super("00000036-0000-1000-8000-0026BB765291", false, true, "The temperature unit", 1); - this.thermostat = thermostat; - } + private final BasicThermostat thermostat; - @Override - protected void setValue(Integer value) throws Exception { - //Not writable - } + public TemperatureUnitsCharacteristic(BasicThermostat thermostat) { + super("00000036-0000-1000-8000-0026BB765291", false, true, "The temperature unit", 1); + this.thermostat = thermostat; + } - @Override - protected CompletableFuture getValue() { - return CompletableFuture.completedFuture(thermostat.getTemperatureUnit().getCode()); - } + @Override + protected void setValue(Integer value) throws Exception { + // Not writable + } + @Override + protected CompletableFuture getValue() { + return CompletableFuture.completedFuture(thermostat.getTemperatureUnit().getCode()); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/valve/SetDurationCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/valve/SetDurationCharacteristic.java index da77109de..320420ae7 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/valve/SetDurationCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/valve/SetDurationCharacteristic.java @@ -1,38 +1,37 @@ package com.beowulfe.hap.impl.characteristics.valve; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.ValveWithTimer; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.IntegerCharacteristic; +import java.util.concurrent.CompletableFuture; -public class SetDurationCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { - private final ValveWithTimer valve; - - public SetDurationCharacteristic(ValveWithTimer valve) { - super("000000D3-0000-1000-8000-0026BB765291", true, true, "Set Duration", 0, 3600, "seconds"); - this.valve = valve; - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - valve.subscribeSetDuration(callback); - } - - @Override - public void unsubscribe() { - valve.unsubscribeSetDuration(); - } - - @Override - protected void setValue(Integer value) throws Exception { - valve.setSetDuration(value); - } - - @Override - protected CompletableFuture getValue() { - return valve.getSetDuration(); - } - +public class SetDurationCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + private final ValveWithTimer valve; + + public SetDurationCharacteristic(ValveWithTimer valve) { + super("000000D3-0000-1000-8000-0026BB765291", true, true, "Set Duration", 0, 3600, "seconds"); + this.valve = valve; + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + valve.subscribeSetDuration(callback); + } + + @Override + public void unsubscribe() { + valve.unsubscribeSetDuration(); + } + + @Override + protected void setValue(Integer value) throws Exception { + valve.setSetDuration(value); + } + + @Override + protected CompletableFuture getValue() { + return valve.getSetDuration(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/valve/ValveTypeCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/valve/ValveTypeCharacteristic.java index e0ef14e62..23fcc9b25 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/valve/ValveTypeCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/valve/ValveTypeCharacteristic.java @@ -1,38 +1,37 @@ package com.beowulfe.hap.impl.characteristics.valve; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.Valve; import com.beowulfe.hap.characteristics.EnumCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; +import java.util.concurrent.CompletableFuture; public class ValveTypeCharacteristic extends EnumCharacteristic implements EventableCharacteristic { - private final Valve valve; + private final Valve valve; - public ValveTypeCharacteristic(Valve valve) { - super("000000D5-0000-1000-8000-0026BB765291", false, true, "Valve Type", 3); - this.valve = valve; - } + public ValveTypeCharacteristic(Valve valve) { + super("000000D5-0000-1000-8000-0026BB765291", false, true, "Valve Type", 3); + this.valve = valve; + } - @Override - protected void setValue(Integer value) throws Exception { - // Read only - } + @Override + protected void setValue(Integer value) throws Exception { + // Read only + } - @Override - protected CompletableFuture getValue() { - return valve.getValveType().thenApply(v -> v.getCode()); - } + @Override + protected CompletableFuture getValue() { + return valve.getValveType().thenApply(v -> v.getCode()); + } - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - valve.subscribeValveType(callback); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + valve.subscribeValveType(callback); + } - @Override - public void unsubscribe() { - valve.unsubscribeValveType(); - } + @Override + public void unsubscribe() { + valve.unsubscribeValveType(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java index ba269096c..0e43f61f2 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java @@ -1,39 +1,45 @@ package com.beowulfe.hap.impl.characteristics.windowcovering; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.HorizontalTiltingWindowCovering; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.IntegerCharacteristic; +import java.util.concurrent.CompletableFuture; -public class CurrentHorizontalTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { - - private final HorizontalTiltingWindowCovering windowCovering; - - public CurrentHorizontalTiltAngleCharacteristic(HorizontalTiltingWindowCovering windowCovering) { - super("0000006C-0000-1000-8000-0026BB765291", false, true, "The current horizontal tilt angle", -90, 90, "Arc Degree"); - this.windowCovering = windowCovering; - } - - @Override - protected void setValue(Integer value) throws Exception { - //Read Only - } - - @Override - protected CompletableFuture getValue() { - return windowCovering.getCurrentHorizontalTiltAngle(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - windowCovering.subscribeCurrentHorizontalTiltAngle(callback); - } - - @Override - public void unsubscribe() { - windowCovering.unsubscribeCurrentHorizontalTiltAngle(); - } - +public class CurrentHorizontalTiltAngleCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + private final HorizontalTiltingWindowCovering windowCovering; + + public CurrentHorizontalTiltAngleCharacteristic(HorizontalTiltingWindowCovering windowCovering) { + super( + "0000006C-0000-1000-8000-0026BB765291", + false, + true, + "The current horizontal tilt angle", + -90, + 90, + "Arc Degree"); + this.windowCovering = windowCovering; + } + + @Override + protected void setValue(Integer value) throws Exception { + // Read Only + } + + @Override + protected CompletableFuture getValue() { + return windowCovering.getCurrentHorizontalTiltAngle(); + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + windowCovering.subscribeCurrentHorizontalTiltAngle(callback); + } + + @Override + public void unsubscribe() { + windowCovering.unsubscribeCurrentHorizontalTiltAngle(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/CurrentPositionCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/CurrentPositionCharacteristic.java index c05d0ead1..0ce752232 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/CurrentPositionCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/CurrentPositionCharacteristic.java @@ -1,39 +1,38 @@ package com.beowulfe.hap.impl.characteristics.windowcovering; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.WindowCovering; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.IntegerCharacteristic; +import java.util.concurrent.CompletableFuture; -public class CurrentPositionCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { +public class CurrentPositionCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { - private final WindowCovering windowCovering; - - public CurrentPositionCharacteristic(WindowCovering windowCovering) { - super("0000006D-0000-1000-8000-0026BB765291", false, true, "The current position", 0, 100, "%"); - this.windowCovering = windowCovering; - } + private final WindowCovering windowCovering; - @Override - protected void setValue(Integer value) throws Exception { - //Read Only - } + public CurrentPositionCharacteristic(WindowCovering windowCovering) { + super("0000006D-0000-1000-8000-0026BB765291", false, true, "The current position", 0, 100, "%"); + this.windowCovering = windowCovering; + } - @Override - protected CompletableFuture getValue() { - return windowCovering.getCurrentPosition(); - } + @Override + protected void setValue(Integer value) throws Exception { + // Read Only + } - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - windowCovering.subscribeCurrentPosition(callback); - } + @Override + protected CompletableFuture getValue() { + return windowCovering.getCurrentPosition(); + } - @Override - public void unsubscribe() { - windowCovering.unsubscribeCurrentPosition(); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + windowCovering.subscribeCurrentPosition(callback); + } + @Override + public void unsubscribe() { + windowCovering.unsubscribeCurrentPosition(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/CurrentVerticalTiltAngleCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/CurrentVerticalTiltAngleCharacteristic.java index c86a5dca1..19b3d8bd8 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/CurrentVerticalTiltAngleCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/CurrentVerticalTiltAngleCharacteristic.java @@ -1,39 +1,45 @@ package com.beowulfe.hap.impl.characteristics.windowcovering; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.VerticalTiltingWindowCovering; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.IntegerCharacteristic; +import java.util.concurrent.CompletableFuture; -public class CurrentVerticalTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { - - private final VerticalTiltingWindowCovering windowCovering; - - public CurrentVerticalTiltAngleCharacteristic(VerticalTiltingWindowCovering windowCovering) { - super("0000006E-0000-1000-8000-0026BB765291", false, true, "The current vertical tilt angle", -90, 90, "Arc Degree"); - this.windowCovering = windowCovering; - } - - @Override - protected void setValue(Integer value) throws Exception { - //Read Only - } - - @Override - protected CompletableFuture getValue() { - return windowCovering.getCurrentVerticalTiltAngle(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - windowCovering.subscribeCurrentVerticalTiltAngle(callback); - } - - @Override - public void unsubscribe() { - windowCovering.unsubscribeCurrentVerticalTiltAngle(); - } - +public class CurrentVerticalTiltAngleCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + private final VerticalTiltingWindowCovering windowCovering; + + public CurrentVerticalTiltAngleCharacteristic(VerticalTiltingWindowCovering windowCovering) { + super( + "0000006E-0000-1000-8000-0026BB765291", + false, + true, + "The current vertical tilt angle", + -90, + 90, + "Arc Degree"); + this.windowCovering = windowCovering; + } + + @Override + protected void setValue(Integer value) throws Exception { + // Read Only + } + + @Override + protected CompletableFuture getValue() { + return windowCovering.getCurrentVerticalTiltAngle(); + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + windowCovering.subscribeCurrentVerticalTiltAngle(callback); + } + + @Override + public void unsubscribe() { + windowCovering.unsubscribeCurrentVerticalTiltAngle(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/HoldPositionCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/HoldPositionCharacteristic.java index 3895befc7..3dccf5252 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/HoldPositionCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/HoldPositionCharacteristic.java @@ -1,28 +1,26 @@ package com.beowulfe.hap.impl.characteristics.windowcovering; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.accessories.WindowCovering; import com.beowulfe.hap.characteristics.BooleanCharacteristic; +import java.util.concurrent.CompletableFuture; public class HoldPositionCharacteristic extends BooleanCharacteristic { - private final WindowCovering windowCovering; - - public HoldPositionCharacteristic(WindowCovering windowCovering) { - super("0000006F-0000-1000-8000-0026BB765291", true, false, "Whether or not to hold position"); - this.windowCovering = windowCovering; - } + private final WindowCovering windowCovering; - @Override - protected void setValue(Boolean value) throws Exception { - this.windowCovering.setHoldPosition(value); - } + public HoldPositionCharacteristic(WindowCovering windowCovering) { + super("0000006F-0000-1000-8000-0026BB765291", true, false, "Whether or not to hold position"); + this.windowCovering = windowCovering; + } - @Override - protected CompletableFuture getValue() { - //Write only - return CompletableFuture.completedFuture(null); - } + @Override + protected void setValue(Boolean value) throws Exception { + this.windowCovering.setHoldPosition(value); + } + @Override + protected CompletableFuture getValue() { + // Write only + return CompletableFuture.completedFuture(null); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/PositionStateCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/PositionStateCharacteristic.java index cbde47717..ccf159a1e 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/PositionStateCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/PositionStateCharacteristic.java @@ -1,39 +1,38 @@ package com.beowulfe.hap.impl.characteristics.windowcovering; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.WindowCovering; import com.beowulfe.hap.characteristics.EnumCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; +import java.util.concurrent.CompletableFuture; -public class PositionStateCharacteristic extends EnumCharacteristic implements EventableCharacteristic { +public class PositionStateCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { - private final WindowCovering windowCovering; - - public PositionStateCharacteristic(WindowCovering windowCovering) { - super("00000072-0000-1000-8000-0026BB765291", false, true, "The position state", 2); - this.windowCovering = windowCovering; - } + private final WindowCovering windowCovering; - @Override - protected void setValue(Integer value) throws Exception { - //Read only - } + public PositionStateCharacteristic(WindowCovering windowCovering) { + super("00000072-0000-1000-8000-0026BB765291", false, true, "The position state", 2); + this.windowCovering = windowCovering; + } - @Override - protected CompletableFuture getValue() { - return windowCovering.getPositionState().thenApply(v -> v.getCode()); - } + @Override + protected void setValue(Integer value) throws Exception { + // Read only + } - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - windowCovering.subscribePositionState(callback); - } + @Override + protected CompletableFuture getValue() { + return windowCovering.getPositionState().thenApply(v -> v.getCode()); + } - @Override - public void unsubscribe() { - windowCovering.unsubscribePositionState(); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + windowCovering.subscribePositionState(callback); + } + @Override + public void unsubscribe() { + windowCovering.unsubscribePositionState(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/TargetHorizontalTiltAngleCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/TargetHorizontalTiltAngleCharacteristic.java index a9bf57fbd..95411d6f2 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/TargetHorizontalTiltAngleCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/TargetHorizontalTiltAngleCharacteristic.java @@ -1,39 +1,45 @@ package com.beowulfe.hap.impl.characteristics.windowcovering; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.HorizontalTiltingWindowCovering; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.IntegerCharacteristic; +import java.util.concurrent.CompletableFuture; -public class TargetHorizontalTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { - - private final HorizontalTiltingWindowCovering windowCovering; - - public TargetHorizontalTiltAngleCharacteristic(HorizontalTiltingWindowCovering windowCovering) { - super("0000007B-0000-1000-8000-0026BB765291", true, true, "The target horizontal tilt angle", -90, 90, "Arc Degree"); - this.windowCovering = windowCovering; - } - - @Override - protected void setValue(Integer value) throws Exception { - windowCovering.setTargetHorizontalTiltAngle(value); - } - - @Override - protected CompletableFuture getValue() { - return windowCovering.getTargetHorizontalTiltAngle(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - windowCovering.subscribeTargetHorizontalTiltAngle(callback); - } - - @Override - public void unsubscribe() { - windowCovering.unsubscribeTargetHorizontalTiltAngle(); - } - +public class TargetHorizontalTiltAngleCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + private final HorizontalTiltingWindowCovering windowCovering; + + public TargetHorizontalTiltAngleCharacteristic(HorizontalTiltingWindowCovering windowCovering) { + super( + "0000007B-0000-1000-8000-0026BB765291", + true, + true, + "The target horizontal tilt angle", + -90, + 90, + "Arc Degree"); + this.windowCovering = windowCovering; + } + + @Override + protected void setValue(Integer value) throws Exception { + windowCovering.setTargetHorizontalTiltAngle(value); + } + + @Override + protected CompletableFuture getValue() { + return windowCovering.getTargetHorizontalTiltAngle(); + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + windowCovering.subscribeTargetHorizontalTiltAngle(callback); + } + + @Override + public void unsubscribe() { + windowCovering.unsubscribeTargetHorizontalTiltAngle(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/TargetPositionCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/TargetPositionCharacteristic.java index 19756be78..ceaf76e29 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/TargetPositionCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/TargetPositionCharacteristic.java @@ -1,39 +1,38 @@ package com.beowulfe.hap.impl.characteristics.windowcovering; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.WindowCovering; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.IntegerCharacteristic; +import java.util.concurrent.CompletableFuture; -public class TargetPositionCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { +public class TargetPositionCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { - private final WindowCovering windowCovering; - - public TargetPositionCharacteristic(WindowCovering windowCovering) { - super("0000007C-0000-1000-8000-0026BB765291", true, true, "The target position", 0, 100, "%"); - this.windowCovering = windowCovering; - } + private final WindowCovering windowCovering; - @Override - protected void setValue(Integer value) throws Exception { - windowCovering.setTargetPosition(value); - } + public TargetPositionCharacteristic(WindowCovering windowCovering) { + super("0000007C-0000-1000-8000-0026BB765291", true, true, "The target position", 0, 100, "%"); + this.windowCovering = windowCovering; + } - @Override - protected CompletableFuture getValue() { - return windowCovering.getTargetPosition(); - } + @Override + protected void setValue(Integer value) throws Exception { + windowCovering.setTargetPosition(value); + } - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - windowCovering.subscribeTargetPosition(callback); - } + @Override + protected CompletableFuture getValue() { + return windowCovering.getTargetPosition(); + } - @Override - public void unsubscribe() { - windowCovering.unsubscribeTargetPosition(); - } + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + windowCovering.subscribeTargetPosition(callback); + } + @Override + public void unsubscribe() { + windowCovering.unsubscribeTargetPosition(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/TargetVerticalTiltAngleCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/TargetVerticalTiltAngleCharacteristic.java index 8a3f30cae..fd0b5ccfe 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/TargetVerticalTiltAngleCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/TargetVerticalTiltAngleCharacteristic.java @@ -1,39 +1,45 @@ package com.beowulfe.hap.impl.characteristics.windowcovering; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.HomekitCharacteristicChangeCallback; import com.beowulfe.hap.accessories.VerticalTiltingWindowCovering; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.IntegerCharacteristic; +import java.util.concurrent.CompletableFuture; -public class TargetVerticalTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { - - private final VerticalTiltingWindowCovering windowCovering; - - public TargetVerticalTiltAngleCharacteristic(VerticalTiltingWindowCovering windowCovering) { - super("0000007D-0000-1000-8000-0026BB765291", true, true, "The target vertical tilt angle", -90, 90, "Arc Degree"); - this.windowCovering = windowCovering; - } - - @Override - protected void setValue(Integer value) throws Exception { - windowCovering.setTargetVerticalTiltAngle(value); - } - - @Override - protected CompletableFuture getValue() { - return windowCovering.getTargetVerticalTiltAngle(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - windowCovering.subscribeTargetVerticalTiltAngle(callback); - } - - @Override - public void unsubscribe() { - windowCovering.unsubscribeTargetVerticalTiltAngle(); - } - +public class TargetVerticalTiltAngleCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + private final VerticalTiltingWindowCovering windowCovering; + + public TargetVerticalTiltAngleCharacteristic(VerticalTiltingWindowCovering windowCovering) { + super( + "0000007D-0000-1000-8000-0026BB765291", + true, + true, + "The target vertical tilt angle", + -90, + 90, + "Arc Degree"); + this.windowCovering = windowCovering; + } + + @Override + protected void setValue(Integer value) throws Exception { + windowCovering.setTargetVerticalTiltAngle(value); + } + + @Override + protected CompletableFuture getValue() { + return windowCovering.getTargetVerticalTiltAngle(); + } + + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + windowCovering.subscribeTargetVerticalTiltAngle(callback); + } + + @Override + public void unsubscribe() { + windowCovering.unsubscribeTargetVerticalTiltAngle(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/connections/ConnectionImpl.java b/src/main/java/com/beowulfe/hap/impl/connections/ConnectionImpl.java index b85215ba0..8b4f2cb8e 100644 --- a/src/main/java/com/beowulfe/hap/impl/connections/ConnectionImpl.java +++ b/src/main/java/com/beowulfe/hap/impl/connections/ConnectionImpl.java @@ -1,142 +1,146 @@ package com.beowulfe.hap.impl.connections; +import com.beowulfe.hap.HomekitAuthInfo; +import com.beowulfe.hap.impl.HomekitRegistry; +import com.beowulfe.hap.impl.crypto.ChachaDecoder; +import com.beowulfe.hap.impl.crypto.ChachaEncoder; +import com.beowulfe.hap.impl.http.*; +import com.beowulfe.hap.impl.jmdns.JmdnsHomekitAdvertiser; +import com.beowulfe.hap.impl.pairing.UpgradeResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Collection; import java.util.function.Consumer; - import org.bouncycastle.util.Pack; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.beowulfe.hap.HomekitAuthInfo; -import com.beowulfe.hap.impl.HomekitRegistry; -import com.beowulfe.hap.impl.crypto.ChachaDecoder; -import com.beowulfe.hap.impl.crypto.ChachaEncoder; -import com.beowulfe.hap.impl.http.*; -import com.beowulfe.hap.impl.jmdns.JmdnsHomekitAdvertiser; -import com.beowulfe.hap.impl.pairing.UpgradeResponse; - class ConnectionImpl implements HomekitClientConnection { - private final HttpSession httpSession; - private LengthPrefixedByteArrayProcessor binaryProcessor; - private int inboundBinaryMessageCount = 0; - private int outboundBinaryMessageCount = 0; - private byte[] readKey; - private byte[] writeKey; - private boolean isUpgraded = false; - private final Consumer outOfBandMessageCallback; - private final SubscriptionManager subscriptions; - - private final static Logger LOGGER = LoggerFactory.getLogger(HomekitClientConnection.class); - - public ConnectionImpl(HomekitAuthInfo authInfo, HomekitRegistry registry, - Consumer outOfBandMessageCallback, SubscriptionManager subscriptions, - JmdnsHomekitAdvertiser advertiser) { - httpSession = new HttpSession(authInfo, registry, subscriptions, this, advertiser); - this.outOfBandMessageCallback = outOfBandMessageCallback; - this.subscriptions = subscriptions; - } + private final HttpSession httpSession; + private LengthPrefixedByteArrayProcessor binaryProcessor; + private int inboundBinaryMessageCount = 0; + private int outboundBinaryMessageCount = 0; + private byte[] readKey; + private byte[] writeKey; + private boolean isUpgraded = false; + private final Consumer outOfBandMessageCallback; + private final SubscriptionManager subscriptions; + + private static final Logger LOGGER = LoggerFactory.getLogger(HomekitClientConnection.class); + + public ConnectionImpl( + HomekitAuthInfo authInfo, + HomekitRegistry registry, + Consumer outOfBandMessageCallback, + SubscriptionManager subscriptions, + JmdnsHomekitAdvertiser advertiser) { + httpSession = new HttpSession(authInfo, registry, subscriptions, this, advertiser); + this.outOfBandMessageCallback = outOfBandMessageCallback; + this.subscriptions = subscriptions; + } + + @Override + public synchronized HttpResponse handleRequest(HttpRequest request) throws IOException { + return doHandleRequest(request); + } + + private HttpResponse doHandleRequest(HttpRequest request) throws IOException { + HttpResponse response = + isUpgraded + ? httpSession.handleAuthenticatedRequest(request) + : httpSession.handleRequest(request); + if (response instanceof UpgradeResponse) { + isUpgraded = true; + readKey = ((UpgradeResponse) response).getReadKey().array(); + writeKey = ((UpgradeResponse) response).getWriteKey().array(); + } + LOGGER.info(response.getStatusCode() + " " + request.getUri()); + return response; + } - @Override - public synchronized HttpResponse handleRequest(HttpRequest request) throws IOException { - return doHandleRequest(request); - } - - private HttpResponse doHandleRequest(HttpRequest request) throws IOException { - HttpResponse response = isUpgraded ? - httpSession.handleAuthenticatedRequest(request) : httpSession.handleRequest(request); - if (response instanceof UpgradeResponse) { - isUpgraded = true; - readKey = ((UpgradeResponse) response).getReadKey().array(); - writeKey = ((UpgradeResponse) response).getWriteKey().array(); - } - LOGGER.info(response.getStatusCode()+" "+request.getUri()); - return response; - } - - @Override - public byte[] decryptRequest(byte[] ciphertext) { - if (!isUpgraded) { - throw new RuntimeException("Cannot handle binary before connection is upgraded"); - } - if (binaryProcessor == null) { - binaryProcessor = new LengthPrefixedByteArrayProcessor(); - } - Collection res = binaryProcessor.handle(ciphertext); - if (res.isEmpty()) { - return new byte[0]; - } else { - try(ByteArrayOutputStream decrypted = new ByteArrayOutputStream()) { - res.stream().map(msg -> decrypt(msg)) - .forEach(bytes -> { - try { - decrypted.write(bytes); - } catch (Exception e) { - throw new RuntimeException(e); - } - }); - return decrypted.toByteArray(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } - + @Override + public byte[] decryptRequest(byte[] ciphertext) { + if (!isUpgraded) { + throw new RuntimeException("Cannot handle binary before connection is upgraded"); + } + if (binaryProcessor == null) { + binaryProcessor = new LengthPrefixedByteArrayProcessor(); + } + Collection res = binaryProcessor.handle(ciphertext); + if (res.isEmpty()) { + return new byte[0]; + } else { + try (ByteArrayOutputStream decrypted = new ByteArrayOutputStream()) { + res.stream() + .map(msg -> decrypt(msg)) + .forEach( + bytes -> { + try { + decrypted.write(bytes); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + return decrypted.toByteArray(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } - @Override - public byte[] encryptResponse(byte[] response) throws IOException { - int offset=0; - try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - while(offset < response.length) { - short length = (short) Math.min(response.length - offset, 0x400); - byte[] lengthBytes = ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN) - .putShort(length).array(); - baos.write(lengthBytes); - - byte[] nonce = Pack.longToLittleEndian(outboundBinaryMessageCount++); - byte[] plaintext; - if (response.length == length) { - plaintext = response; - } else { - plaintext = new byte[length]; - System.arraycopy(response, offset, plaintext, 0, length); - } - offset += length; - baos.write(new ChachaEncoder(writeKey, nonce).encodeCiphertext(plaintext, lengthBytes)); - } - return baos.toByteArray(); - } - } - - private byte[] decrypt(byte[] msg) { - byte[] mac = new byte[16]; - byte[] ciphertext = new byte[msg.length - 16]; - System.arraycopy(msg, 0, ciphertext, 0, msg.length - 16); - System.arraycopy(msg, msg.length - 16, mac, 0, 16); - byte[] additionalData = ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN) - .putShort((short) (msg.length - 16)).array(); - try { - byte[] nonce = Pack.longToLittleEndian(inboundBinaryMessageCount++); - return new ChachaDecoder(readKey, nonce) - .decodeCiphertext(mac, additionalData, ciphertext); - } catch (IOException e) { - throw new RuntimeException(e); - } - } + @Override + public byte[] encryptResponse(byte[] response) throws IOException { + int offset = 0; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + while (offset < response.length) { + short length = (short) Math.min(response.length - offset, 0x400); + byte[] lengthBytes = + ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort(length).array(); + baos.write(lengthBytes); - @Override - public void close() { - subscriptions.removeConnection(this); - } + byte[] nonce = Pack.longToLittleEndian(outboundBinaryMessageCount++); + byte[] plaintext; + if (response.length == length) { + plaintext = response; + } else { + plaintext = new byte[length]; + System.arraycopy(response, offset, plaintext, 0, length); + } + offset += length; + baos.write(new ChachaEncoder(writeKey, nonce).encodeCiphertext(plaintext, lengthBytes)); + } + return baos.toByteArray(); + } + } - @Override - public void outOfBand(HttpResponse message) { - outOfBandMessageCallback.accept(message); - } + private byte[] decrypt(byte[] msg) { + byte[] mac = new byte[16]; + byte[] ciphertext = new byte[msg.length - 16]; + System.arraycopy(msg, 0, ciphertext, 0, msg.length - 16); + System.arraycopy(msg, msg.length - 16, mac, 0, 16); + byte[] additionalData = + ByteBuffer.allocate(2) + .order(ByteOrder.LITTLE_ENDIAN) + .putShort((short) (msg.length - 16)) + .array(); + try { + byte[] nonce = Pack.longToLittleEndian(inboundBinaryMessageCount++); + return new ChachaDecoder(readKey, nonce).decodeCiphertext(mac, additionalData, ciphertext); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + @Override + public void close() { + subscriptions.removeConnection(this); + } + @Override + public void outOfBand(HttpResponse message) { + outOfBandMessageCallback.accept(message); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/connections/HomekitClientConnectionFactoryImpl.java b/src/main/java/com/beowulfe/hap/impl/connections/HomekitClientConnectionFactoryImpl.java index 0d0492633..3d5eb0169 100644 --- a/src/main/java/com/beowulfe/hap/impl/connections/HomekitClientConnectionFactoryImpl.java +++ b/src/main/java/com/beowulfe/hap/impl/connections/HomekitClientConnectionFactoryImpl.java @@ -1,34 +1,34 @@ package com.beowulfe.hap.impl.connections; -import java.util.function.Consumer; - import com.beowulfe.hap.HomekitAuthInfo; import com.beowulfe.hap.impl.HomekitRegistry; import com.beowulfe.hap.impl.http.HomekitClientConnection; import com.beowulfe.hap.impl.http.HomekitClientConnectionFactory; import com.beowulfe.hap.impl.http.HttpResponse; import com.beowulfe.hap.impl.jmdns.JmdnsHomekitAdvertiser; +import java.util.function.Consumer; + +public class HomekitClientConnectionFactoryImpl implements HomekitClientConnectionFactory { -public class HomekitClientConnectionFactoryImpl implements HomekitClientConnectionFactory{ + private final HomekitAuthInfo authInfo; + private final HomekitRegistry registry; + private final SubscriptionManager subscriptions; + private final JmdnsHomekitAdvertiser advertiser; - private final HomekitAuthInfo authInfo; - private final HomekitRegistry registry; - private final SubscriptionManager subscriptions; - private final JmdnsHomekitAdvertiser advertiser; - - public HomekitClientConnectionFactoryImpl(HomekitAuthInfo authInfo, - HomekitRegistry registry, SubscriptionManager subscriptions, JmdnsHomekitAdvertiser advertiser) { - this.registry = registry; - this.authInfo = authInfo; - this.subscriptions = subscriptions; - this.advertiser = advertiser; - } - - @Override - public HomekitClientConnection createConnection(Consumer outOfBandMessageCallback) { - return new ConnectionImpl(authInfo, registry, outOfBandMessageCallback, subscriptions, advertiser); - } + public HomekitClientConnectionFactoryImpl( + HomekitAuthInfo authInfo, + HomekitRegistry registry, + SubscriptionManager subscriptions, + JmdnsHomekitAdvertiser advertiser) { + this.registry = registry; + this.authInfo = authInfo; + this.subscriptions = subscriptions; + this.advertiser = advertiser; + } - - + @Override + public HomekitClientConnection createConnection(Consumer outOfBandMessageCallback) { + return new ConnectionImpl( + authInfo, registry, outOfBandMessageCallback, subscriptions, advertiser); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/connections/HttpSession.java b/src/main/java/com/beowulfe/hap/impl/connections/HttpSession.java index 15cee94e7..2f4e72386 100644 --- a/src/main/java/com/beowulfe/hap/impl/connections/HttpSession.java +++ b/src/main/java/com/beowulfe/hap/impl/connections/HttpSession.java @@ -1,11 +1,5 @@ package com.beowulfe.hap.impl.connections; -import java.io.IOException; -import java.net.InetAddress; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.beowulfe.hap.HomekitAccessory; import com.beowulfe.hap.HomekitAuthInfo; import com.beowulfe.hap.impl.HomekitRegistry; @@ -20,153 +14,160 @@ import com.beowulfe.hap.impl.pairing.PairingUpdateController; import com.beowulfe.hap.impl.responses.InternalServerErrorResponse; import com.beowulfe.hap.impl.responses.NotFoundResponse; +import java.io.IOException; +import java.net.InetAddress; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; class HttpSession { - - private volatile PairingManager pairingManager; - private volatile PairVerificationManager pairVerificationManager; - private volatile AccessoryController accessoryController; - private volatile CharacteristicsController characteristicsController; - - private final HomekitAuthInfo authInfo; - private final HomekitRegistry registry; - private final SubscriptionManager subscriptions; - private final HomekitClientConnection connection; - private final JmdnsHomekitAdvertiser advertiser; - - private final static Logger logger = LoggerFactory.getLogger(HttpSession.class); - - public HttpSession(HomekitAuthInfo authInfo, HomekitRegistry registry, SubscriptionManager subscriptions, - HomekitClientConnection connection, JmdnsHomekitAdvertiser advertiser) { - this.authInfo = authInfo; - this.registry = registry; - this.subscriptions = subscriptions; - this.connection = connection; - this.advertiser = advertiser; - } - - public HttpResponse handleRequest(HttpRequest request) throws IOException { - switch(request.getUri()) { - case "/pair-setup": - return handlePairSetup(request); - - case "/pair-verify": - return handlePairVerify(request); - - default: - if (registry.isAllowUnauthenticatedRequests()) { - return handleAuthenticatedRequest(request); - } else { - logger.info("Unrecognized request for "+request.getUri()); - return new NotFoundResponse(); - } - } - } - - public HttpResponse handleAuthenticatedRequest(HttpRequest request) throws IOException { - try { - switch(request.getUri()) { - case "/accessories": - return getAccessoryController().listing(); - - case "/characteristics": - switch(request.getMethod()) { - case PUT: - return getCharacteristicsController().put(request, connection); - - default: - logger.info("Unrecognized method for "+request.getUri()); - return new NotFoundResponse(); - } - - case "/pairings": - return new PairingUpdateController(authInfo, advertiser).handle(request); - - default: - if (request.getUri().startsWith("/characteristics?")) { - return getCharacteristicsController().get(request); - } - logger.info("Unrecognized request for "+request.getUri()); - return new NotFoundResponse(); - } - } catch (Exception e) { - logger.error("Could not handle request", e); - return new InternalServerErrorResponse(e); - } - } - - private HttpResponse handlePairSetup(HttpRequest request) { - if (pairingManager == null) { - synchronized(HttpSession.class) { - if (pairingManager == null) { - pairingManager = new PairingManager(authInfo, registry, advertiser); - } - } - } - try { - return pairingManager.handle(request); - } catch (Exception e) { - logger.error("Exception encountered during pairing", e); - return new InternalServerErrorResponse(e); - } - } - - private HttpResponse handlePairVerify(HttpRequest request) { - if (pairVerificationManager == null) { - synchronized(HttpSession.class) { - if (pairVerificationManager == null) { - pairVerificationManager = new PairVerificationManager(authInfo, registry); - } - } - } - try { - return pairVerificationManager.handle(request); - } catch (Exception e) { - logger.error("Excepton encountered while verifying pairing", e); - return new InternalServerErrorResponse(e); - } - } - - private synchronized AccessoryController getAccessoryController() { - if (accessoryController == null) { - accessoryController = new AccessoryController(registry); - } - return accessoryController; - } - - private synchronized CharacteristicsController getCharacteristicsController() { - if (characteristicsController == null) { - characteristicsController = new CharacteristicsController(registry, subscriptions); - } - return characteristicsController; - } - - public static class SessionKey { - private final InetAddress address; - private final HomekitAccessory accessory; - - public SessionKey(InetAddress address, HomekitAccessory accessory) { - this.address = address; - this.accessory = accessory; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof SessionKey) { - return address.equals(((SessionKey) obj).address) && - accessory.equals(((SessionKey) obj).accessory); - } else { - return false; - } - } - - @Override - public int hashCode() { - int hash = 1; - hash = hash * 31 + address.hashCode(); - hash = hash * 31 + accessory.hashCode(); - return hash; - } - } + private volatile PairingManager pairingManager; + private volatile PairVerificationManager pairVerificationManager; + private volatile AccessoryController accessoryController; + private volatile CharacteristicsController characteristicsController; + + private final HomekitAuthInfo authInfo; + private final HomekitRegistry registry; + private final SubscriptionManager subscriptions; + private final HomekitClientConnection connection; + private final JmdnsHomekitAdvertiser advertiser; + + private static final Logger logger = LoggerFactory.getLogger(HttpSession.class); + + public HttpSession( + HomekitAuthInfo authInfo, + HomekitRegistry registry, + SubscriptionManager subscriptions, + HomekitClientConnection connection, + JmdnsHomekitAdvertiser advertiser) { + this.authInfo = authInfo; + this.registry = registry; + this.subscriptions = subscriptions; + this.connection = connection; + this.advertiser = advertiser; + } + + public HttpResponse handleRequest(HttpRequest request) throws IOException { + switch (request.getUri()) { + case "/pair-setup": + return handlePairSetup(request); + + case "/pair-verify": + return handlePairVerify(request); + + default: + if (registry.isAllowUnauthenticatedRequests()) { + return handleAuthenticatedRequest(request); + } else { + logger.info("Unrecognized request for " + request.getUri()); + return new NotFoundResponse(); + } + } + } + + public HttpResponse handleAuthenticatedRequest(HttpRequest request) throws IOException { + try { + switch (request.getUri()) { + case "/accessories": + return getAccessoryController().listing(); + + case "/characteristics": + switch (request.getMethod()) { + case PUT: + return getCharacteristicsController().put(request, connection); + + default: + logger.info("Unrecognized method for " + request.getUri()); + return new NotFoundResponse(); + } + + case "/pairings": + return new PairingUpdateController(authInfo, advertiser).handle(request); + + default: + if (request.getUri().startsWith("/characteristics?")) { + return getCharacteristicsController().get(request); + } + logger.info("Unrecognized request for " + request.getUri()); + return new NotFoundResponse(); + } + } catch (Exception e) { + logger.error("Could not handle request", e); + return new InternalServerErrorResponse(e); + } + } + + private HttpResponse handlePairSetup(HttpRequest request) { + if (pairingManager == null) { + synchronized (HttpSession.class) { + if (pairingManager == null) { + pairingManager = new PairingManager(authInfo, registry, advertiser); + } + } + } + try { + return pairingManager.handle(request); + } catch (Exception e) { + logger.error("Exception encountered during pairing", e); + return new InternalServerErrorResponse(e); + } + } + + private HttpResponse handlePairVerify(HttpRequest request) { + if (pairVerificationManager == null) { + synchronized (HttpSession.class) { + if (pairVerificationManager == null) { + pairVerificationManager = new PairVerificationManager(authInfo, registry); + } + } + } + try { + return pairVerificationManager.handle(request); + } catch (Exception e) { + logger.error("Excepton encountered while verifying pairing", e); + return new InternalServerErrorResponse(e); + } + } + + private synchronized AccessoryController getAccessoryController() { + if (accessoryController == null) { + accessoryController = new AccessoryController(registry); + } + return accessoryController; + } + + private synchronized CharacteristicsController getCharacteristicsController() { + if (characteristicsController == null) { + characteristicsController = new CharacteristicsController(registry, subscriptions); + } + return characteristicsController; + } + + public static class SessionKey { + private final InetAddress address; + private final HomekitAccessory accessory; + + public SessionKey(InetAddress address, HomekitAccessory accessory) { + this.address = address; + this.accessory = accessory; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof SessionKey) { + return address.equals(((SessionKey) obj).address) + && accessory.equals(((SessionKey) obj).accessory); + } else { + return false; + } + } + + @Override + public int hashCode() { + int hash = 1; + hash = hash * 31 + address.hashCode(); + hash = hash * 31 + accessory.hashCode(); + return hash; + } + } } diff --git a/src/main/java/com/beowulfe/hap/impl/connections/LengthPrefixedByteArrayProcessor.java b/src/main/java/com/beowulfe/hap/impl/connections/LengthPrefixedByteArrayProcessor.java index 0ecb671a9..11cbe6d91 100644 --- a/src/main/java/com/beowulfe/hap/impl/connections/LengthPrefixedByteArrayProcessor.java +++ b/src/main/java/com/beowulfe/hap/impl/connections/LengthPrefixedByteArrayProcessor.java @@ -3,79 +3,86 @@ import java.io.ByteArrayOutputStream; import java.util.Collection; import java.util.LinkedList; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; class LengthPrefixedByteArrayProcessor { - private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - private Byte firstLengthByteBuffer; //Only used if we've received a single byte at the start of a message - private int targetLength = 0; - - private final static Logger LOGGER = LoggerFactory.getLogger(LengthPrefixedByteArrayProcessor.class); + private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + private Byte + firstLengthByteBuffer; // Only used if we've received a single byte at the start of a message + private int targetLength = 0; + + private static final Logger LOGGER = + LoggerFactory.getLogger(LengthPrefixedByteArrayProcessor.class); - public synchronized Collection handle(byte[] data) { - Collection results = new LinkedList<>(); - int pos = 0; - LOGGER.trace("Received message of length {}. Existing buffer is {}", data.length, buffer.size()); - if (buffer.size() == 0) { - while(data.length - pos > 18) { - int targetLength = (data[0] & 0xFF) + (data[1] & 0xFF) * 256 + 16 + 2; - LOGGER.trace("Attempting to read message of length {}", targetLength); - if (data.length >= pos + targetLength) { - byte[] b = new byte[targetLength - 2]; - System.arraycopy(data, pos+2, b, 0, targetLength-2); - results.add(b); - LOGGER.trace("Read complete message"); - pos = pos + targetLength; - } else { - LOGGER.trace("Not enough data available"); - break; - } - } - } - if (data.length > pos) { - LOGGER.trace("Remaining data available"); - step(data, pos, results); - } - LOGGER.trace("Returning {} results", results.size()); - return results; - } - - private void step(byte[] data, int pos, Collection results) { - LOGGER.trace("Performing step operation on buffer of length {} with pos {}", data.length, pos); - if (targetLength == 0 && data.length == 1 + pos) { - firstLengthByteBuffer = data[pos]; - LOGGER.trace("Received a single byte message, storing byte {} for later", firstLengthByteBuffer); - return; - } - if (targetLength == 0) { - if (firstLengthByteBuffer != null) { - targetLength = (firstLengthByteBuffer & 0xFF) + (data[pos] & 0xFF) * 256 + 16; - pos += 1; - LOGGER.trace("Received the second byte after storing the first byte. New length is {}", targetLength); - } else { - targetLength = (data[pos] & 0xFF) + (data[pos+1] & 0xFF) * 256 + 16; - pos += 2; - LOGGER.trace("targetLength is {}", targetLength); - } - } - int toWrite = targetLength - buffer.size(); - if (toWrite <= data.length - pos) { - //We have a complete message - LOGGER.trace("Received a complete message"); - buffer.write(data, pos, toWrite); - results.add(buffer.toByteArray()); - buffer.reset(); - targetLength=0; - if (pos + toWrite > data.length) { - step(data, pos + toWrite, results); - } - } else { - LOGGER.trace("Storing {} bytes in buffer until we receive the complete {}", data.length-pos, targetLength); - buffer.write(data, pos, data.length - pos); - } - } + public synchronized Collection handle(byte[] data) { + Collection results = new LinkedList<>(); + int pos = 0; + LOGGER.trace( + "Received message of length {}. Existing buffer is {}", data.length, buffer.size()); + if (buffer.size() == 0) { + while (data.length - pos > 18) { + int targetLength = (data[0] & 0xFF) + (data[1] & 0xFF) * 256 + 16 + 2; + LOGGER.trace("Attempting to read message of length {}", targetLength); + if (data.length >= pos + targetLength) { + byte[] b = new byte[targetLength - 2]; + System.arraycopy(data, pos + 2, b, 0, targetLength - 2); + results.add(b); + LOGGER.trace("Read complete message"); + pos = pos + targetLength; + } else { + LOGGER.trace("Not enough data available"); + break; + } + } + } + if (data.length > pos) { + LOGGER.trace("Remaining data available"); + step(data, pos, results); + } + LOGGER.trace("Returning {} results", results.size()); + return results; + } + private void step(byte[] data, int pos, Collection results) { + LOGGER.trace("Performing step operation on buffer of length {} with pos {}", data.length, pos); + if (targetLength == 0 && data.length == 1 + pos) { + firstLengthByteBuffer = data[pos]; + LOGGER.trace( + "Received a single byte message, storing byte {} for later", firstLengthByteBuffer); + return; + } + if (targetLength == 0) { + if (firstLengthByteBuffer != null) { + targetLength = (firstLengthByteBuffer & 0xFF) + (data[pos] & 0xFF) * 256 + 16; + pos += 1; + LOGGER.trace( + "Received the second byte after storing the first byte. New length is {}", + targetLength); + } else { + targetLength = (data[pos] & 0xFF) + (data[pos + 1] & 0xFF) * 256 + 16; + pos += 2; + LOGGER.trace("targetLength is {}", targetLength); + } + } + int toWrite = targetLength - buffer.size(); + if (toWrite <= data.length - pos) { + // We have a complete message + LOGGER.trace("Received a complete message"); + buffer.write(data, pos, toWrite); + results.add(buffer.toByteArray()); + buffer.reset(); + targetLength = 0; + if (pos + toWrite > data.length) { + step(data, pos + toWrite, results); + } + } else { + LOGGER.trace( + "Storing {} bytes in buffer until we receive the complete {}", + data.length - pos, + targetLength); + buffer.write(data, pos, data.length - pos); + } + } } diff --git a/src/main/java/com/beowulfe/hap/impl/connections/SubscriptionManager.java b/src/main/java/com/beowulfe/hap/impl/connections/SubscriptionManager.java index 621c9111f..5ccbf306a 100644 --- a/src/main/java/com/beowulfe/hap/impl/connections/SubscriptionManager.java +++ b/src/main/java/com/beowulfe/hap/impl/connections/SubscriptionManager.java @@ -1,92 +1,101 @@ package com.beowulfe.hap.impl.connections; +import com.beowulfe.hap.characteristics.EventableCharacteristic; +import com.beowulfe.hap.impl.http.HomekitClientConnection; +import com.beowulfe.hap.impl.http.HttpResponse; +import com.beowulfe.hap.impl.json.EventController; import java.util.Collections; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.beowulfe.hap.characteristics.EventableCharacteristic; -import com.beowulfe.hap.impl.http.HomekitClientConnection; -import com.beowulfe.hap.impl.http.HttpResponse; -import com.beowulfe.hap.impl.json.EventController; - public class SubscriptionManager { - private final static Logger LOGGER = LoggerFactory.getLogger(SubscriptionManager.class); - - private final ConcurrentMap> subscriptions = new ConcurrentHashMap<>(); - private final ConcurrentMap> reverse = new ConcurrentHashMap<>(); + private static final Logger LOGGER = LoggerFactory.getLogger(SubscriptionManager.class); + + private final ConcurrentMap> subscriptions = + new ConcurrentHashMap<>(); + private final ConcurrentMap> reverse = + new ConcurrentHashMap<>(); + + public synchronized void addSubscription( + int aid, + int iid, + EventableCharacteristic characteristic, + HomekitClientConnection connection) { + synchronized (this) { + if (!subscriptions.containsKey(characteristic)) { + subscriptions.putIfAbsent(characteristic, newSet()); + } + subscriptions.get(characteristic).add(connection); + if (subscriptions.get(characteristic).size() == 1) { + characteristic.subscribe( + () -> { + publish(aid, iid, characteristic); + }); + } + + if (!reverse.containsKey(connection)) { + reverse.putIfAbsent(connection, newSet()); + } + reverse.get(connection).add(characteristic); + LOGGER.info( + "Added subscription to " + characteristic.getClass() + " for " + connection.hashCode()); + } + try { + connection.outOfBand(new EventController().getMessage(aid, iid, characteristic)); + } catch (Exception e) { + LOGGER.error("Could not send initial state in response to subscribe event", e); + } + } + + public synchronized void removeSubscription( + EventableCharacteristic characteristic, HomekitClientConnection connection) { + Set subscriptions = this.subscriptions.get(characteristic); + if (subscriptions != null) { + subscriptions.remove(connection); + if (subscriptions.size() == 0) { + characteristic.unsubscribe(); + } + } + + Set reverse = this.reverse.get(connection); + if (reverse != null) { + reverse.remove(characteristic); + } + LOGGER.info( + "Removed subscription to " + characteristic.getClass() + " for " + connection.hashCode()); + } + + public synchronized void removeConnection(HomekitClientConnection connection) { + Set characteristics = reverse.remove(connection); + if (characteristics != null) { + for (EventableCharacteristic characteristic : characteristics) { + Set characteristicSubscriptions = + subscriptions.get(characteristic); + characteristicSubscriptions.remove(connection); + if (characteristicSubscriptions.isEmpty()) { + characteristic.unsubscribe(); + } + } + } + } + + private Set newSet() { + return Collections.newSetFromMap(new ConcurrentHashMap()); + } - public synchronized void addSubscription(int aid, int iid, EventableCharacteristic characteristic, HomekitClientConnection connection) { - synchronized(this) { - if (!subscriptions.containsKey(characteristic)) { - subscriptions.putIfAbsent(characteristic, newSet()); - } - subscriptions.get(characteristic).add(connection); - if (subscriptions.get(characteristic).size() == 1) { - characteristic.subscribe(() -> { - publish(aid, iid, characteristic); - }); - } - - if (!reverse.containsKey(connection)) { - reverse.putIfAbsent(connection, newSet()); - } - reverse.get(connection).add(characteristic); - LOGGER.info("Added subscription to "+characteristic.getClass()+" for "+connection.hashCode()); - } - try { - connection.outOfBand(new EventController().getMessage(aid, iid, characteristic)); - } catch (Exception e) { - LOGGER.error("Could not send initial state in response to subscribe event", e); - } - } - - public synchronized void removeSubscription(EventableCharacteristic characteristic, HomekitClientConnection connection) { - Set subscriptions = this.subscriptions.get(characteristic); - if (subscriptions != null) { - subscriptions.remove(connection); - if (subscriptions.size() == 0) { - characteristic.unsubscribe(); - } - } - - Set reverse = this.reverse.get(connection); - if (reverse != null) { - reverse.remove(characteristic); - } - LOGGER.info("Removed subscription to "+characteristic.getClass()+" for "+connection.hashCode()); - } - - public synchronized void removeConnection(HomekitClientConnection connection) { - Set characteristics = reverse.remove(connection); - if (characteristics != null) { - for (EventableCharacteristic characteristic: characteristics) { - Set characteristicSubscriptions = subscriptions.get(characteristic); - characteristicSubscriptions.remove(connection); - if (characteristicSubscriptions.isEmpty()) { - characteristic.unsubscribe(); - } - } - } - } - - private Set newSet() { - return Collections.newSetFromMap(new ConcurrentHashMap()); - } - - public void publish(int accessoryId, int iid, EventableCharacteristic changed) { - try { - HttpResponse message = new EventController().getMessage(accessoryId, iid, changed); - LOGGER.info("Publishing changes for "+accessoryId); - for (HomekitClientConnection connection: subscriptions.get(changed)) { - connection.outOfBand(message); - } - } catch (Exception e) { - LOGGER.error("Failed to create new event message", e); - } - } + public void publish(int accessoryId, int iid, EventableCharacteristic changed) { + try { + HttpResponse message = new EventController().getMessage(accessoryId, iid, changed); + LOGGER.info("Publishing changes for " + accessoryId); + for (HomekitClientConnection connection : subscriptions.get(changed)) { + connection.outOfBand(message); + } + } catch (Exception e) { + LOGGER.error("Failed to create new event message", e); + } + } } diff --git a/src/main/java/com/beowulfe/hap/impl/crypto/ChachaDecoder.java b/src/main/java/com/beowulfe/hap/impl/crypto/ChachaDecoder.java index 4e8615330..e91bdd14f 100644 --- a/src/main/java/com/beowulfe/hap/impl/crypto/ChachaDecoder.java +++ b/src/main/java/com/beowulfe/hap/impl/crypto/ChachaDecoder.java @@ -1,7 +1,6 @@ package com.beowulfe.hap.impl.crypto; import java.io.IOException; - import org.bouncycastle.crypto.engines.ChaChaEngine; import org.bouncycastle.crypto.generators.Poly1305KeyGenerator; import org.bouncycastle.crypto.params.KeyParameter; @@ -12,50 +11,44 @@ public class ChachaDecoder { - private final ChaChaEngine decryptCipher; - - public ChachaDecoder(byte[] key, byte[] nonce) throws IOException { - - this.decryptCipher = new ChaChaEngine(20); - - this.decryptCipher.init(false, new ParametersWithIV(new KeyParameter(key), nonce)); - - - } + private final ChaChaEngine decryptCipher; - public byte[] decodeCiphertext(byte[] receivedMAC, byte[] additionalData, byte[] ciphertext) - throws IOException { + public ChachaDecoder(byte[] key, byte[] nonce) throws IOException { - KeyParameter macKey = initRecordMAC(decryptCipher); + this.decryptCipher = new ChaChaEngine(20); - byte[] calculatedMAC = PolyKeyCreator.create(macKey, additionalData, ciphertext); + this.decryptCipher.init(false, new ParametersWithIV(new KeyParameter(key), nonce)); + } - if (!Arrays.constantTimeAreEqual(calculatedMAC, receivedMAC)) - { - throw new TlsFatalAlert(AlertDescription.bad_record_mac); - } + public byte[] decodeCiphertext(byte[] receivedMAC, byte[] additionalData, byte[] ciphertext) + throws IOException { - byte[] output = new byte[ciphertext.length]; - decryptCipher.processBytes(ciphertext, 0, ciphertext.length, output, 0); + KeyParameter macKey = initRecordMAC(decryptCipher); - return output; - } - - public byte[] decodeCiphertext(byte[] receivedMAC, byte[] ciphertext) throws IOException - { - return decodeCiphertext(receivedMAC, null, ciphertext); - } - - private KeyParameter initRecordMAC(ChaChaEngine cipher) - { - byte[] firstBlock = new byte[64]; - cipher.processBytes(firstBlock, 0, firstBlock.length, firstBlock, 0); - - // NOTE: The BC implementation puts 'r' after 'k' - System.arraycopy(firstBlock, 0, firstBlock, 32, 16); - KeyParameter macKey = new KeyParameter(firstBlock, 16, 32); - Poly1305KeyGenerator.clamp(macKey.getKey()); - return macKey; + byte[] calculatedMAC = PolyKeyCreator.create(macKey, additionalData, ciphertext); + + if (!Arrays.constantTimeAreEqual(calculatedMAC, receivedMAC)) { + throw new TlsFatalAlert(AlertDescription.bad_record_mac); } - + + byte[] output = new byte[ciphertext.length]; + decryptCipher.processBytes(ciphertext, 0, ciphertext.length, output, 0); + + return output; + } + + public byte[] decodeCiphertext(byte[] receivedMAC, byte[] ciphertext) throws IOException { + return decodeCiphertext(receivedMAC, null, ciphertext); + } + + private KeyParameter initRecordMAC(ChaChaEngine cipher) { + byte[] firstBlock = new byte[64]; + cipher.processBytes(firstBlock, 0, firstBlock.length, firstBlock, 0); + + // NOTE: The BC implementation puts 'r' after 'k' + System.arraycopy(firstBlock, 0, firstBlock, 32, 16); + KeyParameter macKey = new KeyParameter(firstBlock, 16, 32); + Poly1305KeyGenerator.clamp(macKey.getKey()); + return macKey; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/crypto/ChachaEncoder.java b/src/main/java/com/beowulfe/hap/impl/crypto/ChachaEncoder.java index deb3c69c6..809f6ad1f 100644 --- a/src/main/java/com/beowulfe/hap/impl/crypto/ChachaEncoder.java +++ b/src/main/java/com/beowulfe/hap/impl/crypto/ChachaEncoder.java @@ -1,7 +1,6 @@ package com.beowulfe.hap.impl.crypto; import java.io.IOException; - import org.bouncycastle.crypto.engines.ChaChaEngine; import org.bouncycastle.crypto.generators.Poly1305KeyGenerator; import org.bouncycastle.crypto.params.KeyParameter; @@ -9,45 +8,41 @@ public class ChachaEncoder { - private final ChaChaEngine encryptCipher; - - public ChachaEncoder(byte[] key, byte[] nonce) throws IOException { - - this.encryptCipher = new ChaChaEngine(20); - - this.encryptCipher.init(true, new ParametersWithIV(new KeyParameter(key), nonce)); - - - } - - public byte[] encodeCiphertext(byte[] plaintext) throws IOException { - return encodeCiphertext(plaintext, null); - } - - public byte[] encodeCiphertext(byte[] plaintext, byte[] additionalData) throws IOException { - KeyParameter macKey = initRecordMAC(encryptCipher); - - byte[] ciphertext = new byte[plaintext.length]; - encryptCipher.processBytes(plaintext, 0, plaintext.length, ciphertext, 0); - - byte[] calculatedMAC = PolyKeyCreator.create(macKey, additionalData, ciphertext); - - byte[] ret = new byte[ciphertext.length + 16]; - System.arraycopy(ciphertext, 0, ret, 0, ciphertext.length); - System.arraycopy(calculatedMAC, 0, ret, ciphertext.length, 16); - return ret; - } - - private KeyParameter initRecordMAC(ChaChaEngine cipher) - { - byte[] firstBlock = new byte[64]; - cipher.processBytes(firstBlock, 0, firstBlock.length, firstBlock, 0); - - // NOTE: The BC implementation puts 'r' after 'k' - System.arraycopy(firstBlock, 0, firstBlock, 32, 16); - KeyParameter macKey = new KeyParameter(firstBlock, 16, 32); - Poly1305KeyGenerator.clamp(macKey.getKey()); - return macKey; - } - + private final ChaChaEngine encryptCipher; + + public ChachaEncoder(byte[] key, byte[] nonce) throws IOException { + + this.encryptCipher = new ChaChaEngine(20); + + this.encryptCipher.init(true, new ParametersWithIV(new KeyParameter(key), nonce)); + } + + public byte[] encodeCiphertext(byte[] plaintext) throws IOException { + return encodeCiphertext(plaintext, null); + } + + public byte[] encodeCiphertext(byte[] plaintext, byte[] additionalData) throws IOException { + KeyParameter macKey = initRecordMAC(encryptCipher); + + byte[] ciphertext = new byte[plaintext.length]; + encryptCipher.processBytes(plaintext, 0, plaintext.length, ciphertext, 0); + + byte[] calculatedMAC = PolyKeyCreator.create(macKey, additionalData, ciphertext); + + byte[] ret = new byte[ciphertext.length + 16]; + System.arraycopy(ciphertext, 0, ret, 0, ciphertext.length); + System.arraycopy(calculatedMAC, 0, ret, ciphertext.length, 16); + return ret; + } + + private KeyParameter initRecordMAC(ChaChaEngine cipher) { + byte[] firstBlock = new byte[64]; + cipher.processBytes(firstBlock, 0, firstBlock.length, firstBlock, 0); + + // NOTE: The BC implementation puts 'r' after 'k' + System.arraycopy(firstBlock, 0, firstBlock, 32, 16); + KeyParameter macKey = new KeyParameter(firstBlock, 16, 32); + Poly1305KeyGenerator.clamp(macKey.getKey()); + return macKey; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/crypto/EdsaSigner.java b/src/main/java/com/beowulfe/hap/impl/crypto/EdsaSigner.java index bab1d3a63..1ea917067 100644 --- a/src/main/java/com/beowulfe/hap/impl/crypto/EdsaSigner.java +++ b/src/main/java/com/beowulfe/hap/impl/crypto/EdsaSigner.java @@ -5,7 +5,6 @@ import java.security.NoSuchAlgorithmException; import java.security.Signature; import java.security.SignatureException; - import net.i2p.crypto.eddsa.EdDSAEngine; import net.i2p.crypto.eddsa.EdDSAPrivateKey; import net.i2p.crypto.eddsa.EdDSAPublicKey; @@ -15,28 +14,27 @@ import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec; public class EdsaSigner { - - private final EdDSAPublicKey publicKey; - private final EdDSAPrivateKey privateKey; - public EdsaSigner(byte[] privateKeyBytes) { - EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("ed25519-sha-512"); - EdDSAPrivateKeySpec privateKeySpec = new EdDSAPrivateKeySpec(privateKeyBytes, spec); - EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privateKeySpec.getA(), spec); - publicKey = new EdDSAPublicKey(pubKeySpec); - privateKey = new EdDSAPrivateKey(privateKeySpec); - } - - public byte[] getPublicKey() { - return publicKey.getAbyte(); - } - - public byte[] sign(byte[] material) throws NoSuchAlgorithmException, InvalidKeyException, - SignatureException { - Signature sgr = new EdDSAEngine(MessageDigest.getInstance("SHA-512")); - sgr.initSign(privateKey); - sgr.update(material); - return sgr.sign(); - } + private final EdDSAPublicKey publicKey; + private final EdDSAPrivateKey privateKey; + + public EdsaSigner(byte[] privateKeyBytes) { + EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("ed25519-sha-512"); + EdDSAPrivateKeySpec privateKeySpec = new EdDSAPrivateKeySpec(privateKeyBytes, spec); + EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privateKeySpec.getA(), spec); + publicKey = new EdDSAPublicKey(pubKeySpec); + privateKey = new EdDSAPrivateKey(privateKeySpec); + } + + public byte[] getPublicKey() { + return publicKey.getAbyte(); + } + public byte[] sign(byte[] material) + throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { + Signature sgr = new EdDSAEngine(MessageDigest.getInstance("SHA-512")); + sgr.initSign(privateKey); + sgr.update(material); + return sgr.sign(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/crypto/EdsaVerifier.java b/src/main/java/com/beowulfe/hap/impl/crypto/EdsaVerifier.java index c1bbbd9be..3d3570b44 100644 --- a/src/main/java/com/beowulfe/hap/impl/crypto/EdsaVerifier.java +++ b/src/main/java/com/beowulfe/hap/impl/crypto/EdsaVerifier.java @@ -3,7 +3,6 @@ import java.security.MessageDigest; import java.security.PublicKey; import java.security.Signature; - import net.i2p.crypto.eddsa.EdDSAEngine; import net.i2p.crypto.eddsa.EdDSAPublicKey; import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable; @@ -11,21 +10,20 @@ import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec; public class EdsaVerifier { - - private final PublicKey publicKey; - public EdsaVerifier(byte[] publicKey) { - EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("ed25519-sha-512"); - EdDSAPublicKeySpec pubKey = new EdDSAPublicKeySpec(publicKey, spec); - this.publicKey = new EdDSAPublicKey(pubKey); - } - - public boolean verify(byte[] data, byte[] signature) throws Exception { - Signature sgr = new EdDSAEngine(MessageDigest.getInstance("SHA-512")); - sgr.initVerify(publicKey); - sgr.update(data); - - return sgr.verify(signature); - } + private final PublicKey publicKey; + + public EdsaVerifier(byte[] publicKey) { + EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("ed25519-sha-512"); + EdDSAPublicKeySpec pubKey = new EdDSAPublicKeySpec(publicKey, spec); + this.publicKey = new EdDSAPublicKey(pubKey); + } + + public boolean verify(byte[] data, byte[] signature) throws Exception { + Signature sgr = new EdDSAEngine(MessageDigest.getInstance("SHA-512")); + sgr.initVerify(publicKey); + sgr.update(data); + return sgr.verify(signature); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/crypto/PolyKeyCreator.java b/src/main/java/com/beowulfe/hap/impl/crypto/PolyKeyCreator.java index 9557a803a..d5e65a67f 100644 --- a/src/main/java/com/beowulfe/hap/impl/crypto/PolyKeyCreator.java +++ b/src/main/java/com/beowulfe/hap/impl/crypto/PolyKeyCreator.java @@ -6,37 +6,37 @@ class PolyKeyCreator { - public static byte[] create(KeyParameter macKey, byte[] additionalData, byte[] ciphertext) { - Poly1305 poly = new Poly1305(); - poly.init(macKey); - - if (additionalData != null) { - poly.update(additionalData, 0, additionalData.length); - if (additionalData.length % 16 != 0) { - int round = 16-(additionalData.length%16); - poly.update(new byte[round], 0, round); - } - } - - poly.update(ciphertext, 0, ciphertext.length); - if (ciphertext.length % 16 != 0) { - int round = 16-(ciphertext.length%16); - poly.update(new byte[round], 0, round); - } - - //additional data length - byte[] additionalDataLength; - if (additionalData != null) { - additionalDataLength = Pack.longToLittleEndian(additionalData.length); - } else { - additionalDataLength = new byte[8]; - } - poly.update(additionalDataLength, 0, 8); - byte[] ciphertextLength = Pack.longToLittleEndian(ciphertext.length); - poly.update(ciphertextLength, 0, 8); - - byte[] calculatedMAC = new byte[poly.getMacSize()]; - poly.doFinal(calculatedMAC, 0); - return calculatedMAC; - } + public static byte[] create(KeyParameter macKey, byte[] additionalData, byte[] ciphertext) { + Poly1305 poly = new Poly1305(); + poly.init(macKey); + + if (additionalData != null) { + poly.update(additionalData, 0, additionalData.length); + if (additionalData.length % 16 != 0) { + int round = 16 - (additionalData.length % 16); + poly.update(new byte[round], 0, round); + } + } + + poly.update(ciphertext, 0, ciphertext.length); + if (ciphertext.length % 16 != 0) { + int round = 16 - (ciphertext.length % 16); + poly.update(new byte[round], 0, round); + } + + // additional data length + byte[] additionalDataLength; + if (additionalData != null) { + additionalDataLength = Pack.longToLittleEndian(additionalData.length); + } else { + additionalDataLength = new byte[8]; + } + poly.update(additionalDataLength, 0, 8); + byte[] ciphertextLength = Pack.longToLittleEndian(ciphertext.length); + poly.update(ciphertextLength, 0, 8); + + byte[] calculatedMAC = new byte[poly.getMacSize()]; + poly.doFinal(calculatedMAC, 0); + return calculatedMAC; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/http/HomekitClientConnection.java b/src/main/java/com/beowulfe/hap/impl/http/HomekitClientConnection.java index 663834ee5..1ec53496c 100644 --- a/src/main/java/com/beowulfe/hap/impl/http/HomekitClientConnection.java +++ b/src/main/java/com/beowulfe/hap/impl/http/HomekitClientConnection.java @@ -4,14 +4,13 @@ public interface HomekitClientConnection { - HttpResponse handleRequest(HttpRequest request) throws IOException; + HttpResponse handleRequest(HttpRequest request) throws IOException; - byte[] decryptRequest(byte[] ciphertext); - - byte[] encryptResponse(byte[] plaintext) throws IOException; - - void close(); + byte[] decryptRequest(byte[] ciphertext); - void outOfBand(HttpResponse message); - + byte[] encryptResponse(byte[] plaintext) throws IOException; + + void close(); + + void outOfBand(HttpResponse message); } diff --git a/src/main/java/com/beowulfe/hap/impl/http/HomekitClientConnectionFactory.java b/src/main/java/com/beowulfe/hap/impl/http/HomekitClientConnectionFactory.java index 1e2c434f1..e5236d0e8 100644 --- a/src/main/java/com/beowulfe/hap/impl/http/HomekitClientConnectionFactory.java +++ b/src/main/java/com/beowulfe/hap/impl/http/HomekitClientConnectionFactory.java @@ -4,6 +4,5 @@ public interface HomekitClientConnectionFactory { - HomekitClientConnection createConnection(Consumer outOfBandMessageCallback); - + HomekitClientConnection createConnection(Consumer outOfBandMessageCallback); } diff --git a/src/main/java/com/beowulfe/hap/impl/http/HttpMethod.java b/src/main/java/com/beowulfe/hap/impl/http/HttpMethod.java index 7653b8336..4c91a80f7 100644 --- a/src/main/java/com/beowulfe/hap/impl/http/HttpMethod.java +++ b/src/main/java/com/beowulfe/hap/impl/http/HttpMethod.java @@ -1,8 +1,7 @@ package com.beowulfe.hap.impl.http; public enum HttpMethod { - - GET, - POST, - PUT + GET, + POST, + PUT } diff --git a/src/main/java/com/beowulfe/hap/impl/http/HttpRequest.java b/src/main/java/com/beowulfe/hap/impl/http/HttpRequest.java index 22498a049..f6989d81b 100644 --- a/src/main/java/com/beowulfe/hap/impl/http/HttpRequest.java +++ b/src/main/java/com/beowulfe/hap/impl/http/HttpRequest.java @@ -2,8 +2,9 @@ public interface HttpRequest { - String getUri(); - byte[] getBody(); - HttpMethod getMethod(); - + String getUri(); + + byte[] getBody(); + + HttpMethod getMethod(); } diff --git a/src/main/java/com/beowulfe/hap/impl/http/HttpResponse.java b/src/main/java/com/beowulfe/hap/impl/http/HttpResponse.java index 03f0a5448..328f8e795 100644 --- a/src/main/java/com/beowulfe/hap/impl/http/HttpResponse.java +++ b/src/main/java/com/beowulfe/hap/impl/http/HttpResponse.java @@ -6,24 +6,26 @@ public interface HttpResponse { - int getStatusCode(); - - default ByteBuffer getBody() { - return ByteBuffer.allocate(0); - } - - default HttpVersion getVersion() { - return HttpVersion.HTTP_1_1; - } - - default Map getHeaders() { - return Collections.emptyMap(); - } - - default boolean doUpgrade() { return false; } - - public enum HttpVersion { - HTTP_1_1, - EVENT_1_0 - } + int getStatusCode(); + + default ByteBuffer getBody() { + return ByteBuffer.allocate(0); + } + + default HttpVersion getVersion() { + return HttpVersion.HTTP_1_1; + } + + default Map getHeaders() { + return Collections.emptyMap(); + } + + default boolean doUpgrade() { + return false; + } + + public enum HttpVersion { + HTTP_1_1, + EVENT_1_0 + } } diff --git a/src/main/java/com/beowulfe/hap/impl/http/impl/AccessoryHandler.java b/src/main/java/com/beowulfe/hap/impl/http/impl/AccessoryHandler.java index 2a3e3715c..f8673eef2 100644 --- a/src/main/java/com/beowulfe/hap/impl/http/impl/AccessoryHandler.java +++ b/src/main/java/com/beowulfe/hap/impl/http/impl/AccessoryHandler.java @@ -9,90 +9,96 @@ import io.netty.channel.ChannelPipeline; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.nio.charset.StandardCharsets; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; class AccessoryHandler extends SimpleChannelInboundHandler { - private final static Logger LOGGER = LoggerFactory.getLogger(AccessoryHandler.class); - - private HomekitClientConnection connection; - private final HomekitClientConnectionFactory homekitClientConnectionFactory; - - public AccessoryHandler(HomekitClientConnectionFactory homekitClientConnectionFactory) { - this.homekitClientConnectionFactory = homekitClientConnectionFactory; - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - final Channel channel = ctx.pipeline().channel(); - this.connection = homekitClientConnectionFactory.createConnection(response -> { - if (!channel.isActive()) { return; } - channel.writeAndFlush(NettyResponseUtil.createResponse(response)); - }); - LOGGER.info("New homekit connection from "+ctx.channel().remoteAddress().toString()); - super.channelActive(ctx); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - LOGGER.info("Terminated homekit connection from "+ctx.channel().remoteAddress().toString()); - super.channelInactive(ctx); - } + private static final Logger LOGGER = LoggerFactory.getLogger(AccessoryHandler.class); + + private HomekitClientConnection connection; + private final HomekitClientConnectionFactory homekitClientConnectionFactory; + + public AccessoryHandler(HomekitClientConnectionFactory homekitClientConnectionFactory) { + this.homekitClientConnectionFactory = homekitClientConnectionFactory; + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + final Channel channel = ctx.pipeline().channel(); + this.connection = + homekitClientConnectionFactory.createConnection( + response -> { + if (!channel.isActive()) { + return; + } + channel.writeAndFlush(NettyResponseUtil.createResponse(response)); + }); + LOGGER.info("New homekit connection from " + ctx.channel().remoteAddress().toString()); + super.channelActive(ctx); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + LOGGER.info("Terminated homekit connection from " + ctx.channel().remoteAddress().toString()); + super.channelInactive(ctx); + } + + @Override + public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception { + try { + HttpResponse response = connection.handleRequest(new FullRequestHttpRequestImpl(req)); + if (response.doUpgrade()) { + ChannelPipeline pipeline = ctx.channel().pipeline(); + pipeline.addBefore( + ServerInitializer.HTTP_HANDLER_NAME, "binary", new BinaryHandler(connection)); + } + sendResponse(response, ctx); + } catch (Exception e) { + LOGGER.error("Error handling homekit http request", e); + sendResponse(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Error: " + e.getMessage(), ctx); + } + } + + private void sendResponse( + HttpResponseStatus status, String responseBody, ChannelHandlerContext ctx) { + if (responseBody == null) { + responseBody = ""; + } + FullHttpResponse response = + new DefaultFullHttpResponse( + HttpVersion.HTTP_1_1, + status, + Unpooled.copiedBuffer(responseBody.getBytes(StandardCharsets.UTF_8))); + response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/plain"); + response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes()); + response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); + ctx.write(response); + ctx.flush(); + } + + private void sendResponse(HttpResponse homekitResponse, ChannelHandlerContext ctx) { + FullHttpResponse response = NettyResponseUtil.createResponse(homekitResponse); + ctx.write(response); + ctx.flush(); + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + ctx.flush(); + super.channelReadComplete(ctx); + } - @Override - public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) - throws Exception { - try { - HttpResponse response = connection.handleRequest(new FullRequestHttpRequestImpl(req)); - if (response.doUpgrade()) { - ChannelPipeline pipeline = ctx.channel().pipeline(); - pipeline.addBefore(ServerInitializer.HTTP_HANDLER_NAME, "binary", new BinaryHandler(connection)); - } - sendResponse(response, ctx); - } catch (Exception e) { - LOGGER.error("Error handling homekit http request", e); - sendResponse(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Error: "+e.getMessage(), ctx); - } - } - - private void sendResponse(HttpResponseStatus status, String responseBody, ChannelHandlerContext ctx) { - if (responseBody == null) { - responseBody = ""; - } - FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, - Unpooled.copiedBuffer(responseBody.getBytes(StandardCharsets.UTF_8))); - response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/plain"); - response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes()); - response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); - ctx.write(response); - ctx.flush(); - } - - private void sendResponse(HttpResponse homekitResponse, ChannelHandlerContext ctx) { - FullHttpResponse response = NettyResponseUtil.createResponse(homekitResponse); - ctx.write(response); - ctx.flush(); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - ctx.flush(); - super.channelReadComplete(ctx); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) - throws Exception { - boolean errorLevel = !(cause instanceof IOException); - if (errorLevel) { - LOGGER.error("Exception caught in web handler", cause); - } else { - LOGGER.debug("Exception caught in web handler", cause); - } - ctx.close(); - } + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + boolean errorLevel = !(cause instanceof IOException); + if (errorLevel) { + LOGGER.error("Exception caught in web handler", cause); + } else { + LOGGER.debug("Exception caught in web handler", cause); + } + ctx.close(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/http/impl/BinaryHandler.java b/src/main/java/com/beowulfe/hap/impl/http/impl/BinaryHandler.java index 4e75f592d..7723d090e 100644 --- a/src/main/java/com/beowulfe/hap/impl/http/impl/BinaryHandler.java +++ b/src/main/java/com/beowulfe/hap/impl/http/impl/BinaryHandler.java @@ -5,71 +5,70 @@ import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageCodec; -import org.apache.commons.io.HexDump; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; +import org.apache.commons.io.HexDump; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class BinaryHandler extends ByteToMessageCodec { - - private final static Logger logger = LoggerFactory.getLogger(BinaryHandler.class); - private final HomekitClientConnection connection; - private boolean started = false; - - public BinaryHandler(HomekitClientConnection connection) { - this.connection = connection; - } + private static final Logger logger = LoggerFactory.getLogger(BinaryHandler.class); + + private final HomekitClientConnection connection; + private boolean started = false; + + public BinaryHandler(HomekitClientConnection connection) { + this.connection = connection; + } + + @Override + protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception { + if (started) { + byte[] b = new byte[msg.readableBytes()]; + msg.readBytes(b); + traceData("Sending data", b, ctx); + out.writeBytes(connection.encryptResponse(b)); + } else { + out.writeBytes(msg); + } + } - @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) - throws Exception { - if (started) { - byte[] b = new byte[msg.readableBytes()]; - msg.readBytes(b); - traceData("Sending data", b, ctx); - out.writeBytes(connection.encryptResponse(b)); - } else { - out.writeBytes(msg); - } - } + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + byte[] b = new byte[in.readableBytes()]; + in.readBytes(b); + byte[] decrypted = connection.decryptRequest(b); + traceData("Received data", decrypted, ctx); + out.add(Unpooled.copiedBuffer(decrypted)); + started = true; + } - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, - List out) throws Exception { - byte[] b = new byte[in.readableBytes()]; - in.readBytes(b); - byte[] decrypted = connection.decryptRequest(b); - traceData("Received data", decrypted, ctx); - out.add(Unpooled.copiedBuffer(decrypted)); - started = true; - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) - throws Exception { - boolean errorLevel = !(cause instanceof IOException); - if (errorLevel) { - logger.error("Exception in binary handler", cause); - } else { - logger.debug("Exception in binary handler", cause); - } - super.exceptionCaught(ctx, cause); - } - - private void traceData(String msg, byte[] b, ChannelHandlerContext ctx) throws Exception { - if (logger.isTraceEnabled() && b.length > 0) { - try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { - HexDump.dump(b, 0, stream, 0); - stream.flush(); - logger.trace(String.format("%s [%s]:%n%s%n", msg, ctx.channel().remoteAddress().toString(), - stream.toString(StandardCharsets.UTF_8.name()))); - } - } - } + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + boolean errorLevel = !(cause instanceof IOException); + if (errorLevel) { + logger.error("Exception in binary handler", cause); + } else { + logger.debug("Exception in binary handler", cause); + } + super.exceptionCaught(ctx, cause); + } + private void traceData(String msg, byte[] b, ChannelHandlerContext ctx) throws Exception { + if (logger.isTraceEnabled() && b.length > 0) { + try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { + HexDump.dump(b, 0, stream, 0); + stream.flush(); + logger.trace( + String.format( + "%s [%s]:%n%s%n", + msg, + ctx.channel().remoteAddress().toString(), + stream.toString(StandardCharsets.UTF_8.name()))); + } + } + } } diff --git a/src/main/java/com/beowulfe/hap/impl/http/impl/DefaultHttpRequestImpl.java b/src/main/java/com/beowulfe/hap/impl/http/impl/DefaultHttpRequestImpl.java index f9fde2226..5725ec985 100644 --- a/src/main/java/com/beowulfe/hap/impl/http/impl/DefaultHttpRequestImpl.java +++ b/src/main/java/com/beowulfe/hap/impl/http/impl/DefaultHttpRequestImpl.java @@ -5,34 +5,33 @@ class DefaultHttpRequestImpl implements HttpRequest { - private final io.netty.handler.codec.http.HttpRequest request; - - public DefaultHttpRequestImpl(io.netty.handler.codec.http.HttpRequest request) { - this.request = request; - } - - @Override - public String getUri() { - return request.getUri(); - } + private final io.netty.handler.codec.http.HttpRequest request; - @Override - public byte[] getBody() { - return new byte[0]; - } - - @Override - public HttpMethod getMethod() { - io.netty.handler.codec.http.HttpMethod method = request.getMethod(); - if (method.equals(io.netty.handler.codec.http.HttpMethod.GET)) { - return HttpMethod.GET; - } else if (method.equals(io.netty.handler.codec.http.HttpMethod.POST)) { - return HttpMethod.POST; - } else if (method.equals(io.netty.handler.codec.http.HttpMethod.PUT)) { - return HttpMethod.PUT; - } else { - throw new RuntimeException("Unrecognized method: "+method.toString()); - } - } + public DefaultHttpRequestImpl(io.netty.handler.codec.http.HttpRequest request) { + this.request = request; + } + @Override + public String getUri() { + return request.getUri(); + } + + @Override + public byte[] getBody() { + return new byte[0]; + } + + @Override + public HttpMethod getMethod() { + io.netty.handler.codec.http.HttpMethod method = request.getMethod(); + if (method.equals(io.netty.handler.codec.http.HttpMethod.GET)) { + return HttpMethod.GET; + } else if (method.equals(io.netty.handler.codec.http.HttpMethod.POST)) { + return HttpMethod.POST; + } else if (method.equals(io.netty.handler.codec.http.HttpMethod.PUT)) { + return HttpMethod.PUT; + } else { + throw new RuntimeException("Unrecognized method: " + method.toString()); + } + } } diff --git a/src/main/java/com/beowulfe/hap/impl/http/impl/FullRequestHttpRequestImpl.java b/src/main/java/com/beowulfe/hap/impl/http/impl/FullRequestHttpRequestImpl.java index 2a455fdfb..5acaad387 100644 --- a/src/main/java/com/beowulfe/hap/impl/http/impl/FullRequestHttpRequestImpl.java +++ b/src/main/java/com/beowulfe/hap/impl/http/impl/FullRequestHttpRequestImpl.java @@ -3,20 +3,18 @@ import io.netty.handler.codec.http.FullHttpRequest; class FullRequestHttpRequestImpl extends DefaultHttpRequestImpl { - - private final FullHttpRequest nettyRequest; - - public FullRequestHttpRequestImpl(FullHttpRequest nettyRequest) { - super(nettyRequest); - this.nettyRequest = nettyRequest; - } + private final FullHttpRequest nettyRequest; - @Override - public byte[] getBody() { - byte[] ret = new byte[nettyRequest.content().readableBytes()]; - nettyRequest.content().readBytes(ret); - return ret; - } + public FullRequestHttpRequestImpl(FullHttpRequest nettyRequest) { + super(nettyRequest); + this.nettyRequest = nettyRequest; + } + @Override + public byte[] getBody() { + byte[] ret = new byte[nettyRequest.content().readableBytes()]; + nettyRequest.content().readBytes(ret); + return ret; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/http/impl/HomekitHttpServer.java b/src/main/java/com/beowulfe/hap/impl/http/impl/HomekitHttpServer.java index 547114c26..189b82e46 100644 --- a/src/main/java/com/beowulfe/hap/impl/http/impl/HomekitHttpServer.java +++ b/src/main/java/com/beowulfe/hap/impl/http/impl/HomekitHttpServer.java @@ -1,41 +1,39 @@ package com.beowulfe.hap.impl.http.impl; -import java.util.concurrent.CompletableFuture; - import com.beowulfe.hap.impl.HomekitWebHandler; import com.beowulfe.hap.impl.http.HomekitClientConnectionFactory; +import java.util.concurrent.CompletableFuture; public class HomekitHttpServer implements HomekitWebHandler { - private NettyHomekitHttpService service = null; - private final int port; - private final int nThreads; - - @Override - public void stop() { - if (this.service != null) { - this.service.shutdown(); - } - } - - public HomekitHttpServer(int port, int nThreads) { - this.port = port; - this.nThreads = nThreads; - } - - @Override - public CompletableFuture start(HomekitClientConnectionFactory clientConnectionFactory) { - if (service == null) { - this.service = NettyHomekitHttpService.create(port, nThreads); - return this.service.create(clientConnectionFactory); - } else { - throw new RuntimeException("HomekitHttpServer can only be started once"); - } - } - - @Override - public void resetConnections() { - service.resetConnections(); - } - + private NettyHomekitHttpService service = null; + private final int port; + private final int nThreads; + + @Override + public void stop() { + if (this.service != null) { + this.service.shutdown(); + } + } + + public HomekitHttpServer(int port, int nThreads) { + this.port = port; + this.nThreads = nThreads; + } + + @Override + public CompletableFuture start(HomekitClientConnectionFactory clientConnectionFactory) { + if (service == null) { + this.service = NettyHomekitHttpService.create(port, nThreads); + return this.service.create(clientConnectionFactory); + } else { + throw new RuntimeException("HomekitHttpServer can only be started once"); + } + } + + @Override + public void resetConnections() { + service.resetConnections(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/http/impl/HttpResponseEncoderAggregate.java b/src/main/java/com/beowulfe/hap/impl/http/impl/HttpResponseEncoderAggregate.java index d1ae6d278..34460127d 100644 --- a/src/main/java/com/beowulfe/hap/impl/http/impl/HttpResponseEncoderAggregate.java +++ b/src/main/java/com/beowulfe/hap/impl/http/impl/HttpResponseEncoderAggregate.java @@ -3,24 +3,21 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.HttpResponseEncoder; - import java.util.Iterator; import java.util.List; public class HttpResponseEncoderAggregate extends HttpResponseEncoder { - @Override - protected void encode(ChannelHandlerContext ctx, Object msg, - List out) throws Exception { - super.encode(ctx, msg, out); - if (out.size() > 0) { - Iterator i = out.iterator(); - ByteBuf b = (ByteBuf) i.next(); - while (i.hasNext()) { - b.writeBytes((ByteBuf) i.next()); - i.remove(); - } - } - } - + @Override + protected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception { + super.encode(ctx, msg, out); + if (out.size() > 0) { + Iterator i = out.iterator(); + ByteBuf b = (ByteBuf) i.next(); + while (i.hasNext()) { + b.writeBytes((ByteBuf) i.next()); + i.remove(); + } + } + } } diff --git a/src/main/java/com/beowulfe/hap/impl/http/impl/LoggingHandler.java b/src/main/java/com/beowulfe/hap/impl/http/impl/LoggingHandler.java index c6fc56a15..eee9b2e9b 100644 --- a/src/main/java/com/beowulfe/hap/impl/http/impl/LoggingHandler.java +++ b/src/main/java/com/beowulfe/hap/impl/http/impl/LoggingHandler.java @@ -2,47 +2,49 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.*; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; - import org.apache.commons.io.HexDump; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LoggingHandler extends ChannelDuplexHandler { - - private final static Logger logger = LoggerFactory.getLogger(NettyHomekitHttpService.class); - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) - throws Exception { - if (logger.isTraceEnabled() && msg instanceof ByteBuf) { - logBytes("READ", (ByteBuf) msg, ctx); - } - super.channelRead(ctx, msg); - } - - @Override - public void write(ChannelHandlerContext ctx, Object msg, - ChannelPromise promise) throws Exception { - if (logger.isTraceEnabled() && msg instanceof ByteBuf) { - logBytes("WRITE", (ByteBuf) msg, ctx); - } - super.write(ctx, msg, promise); - } - - private void logBytes(String type, ByteBuf buf, ChannelHandlerContext ctx) throws IOException { - if (buf.readableBytes() > 0) { - try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { - byte[] bytes = new byte[buf.readableBytes()]; - buf.getBytes(0, bytes, 0, bytes.length); - HexDump.dump(bytes, 0, stream, 0); - stream.flush(); - logger.trace(String.format("%s %s [%s]:%n%s%n", type, buf, ctx.channel().remoteAddress().toString(), - stream.toString(StandardCharsets.UTF_8.name()))); - } - } - } + private static final Logger logger = LoggerFactory.getLogger(NettyHomekitHttpService.class); + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if (logger.isTraceEnabled() && msg instanceof ByteBuf) { + logBytes("READ", (ByteBuf) msg, ctx); + } + super.channelRead(ctx, msg); + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) + throws Exception { + if (logger.isTraceEnabled() && msg instanceof ByteBuf) { + logBytes("WRITE", (ByteBuf) msg, ctx); + } + super.write(ctx, msg, promise); + } + + private void logBytes(String type, ByteBuf buf, ChannelHandlerContext ctx) throws IOException { + if (buf.readableBytes() > 0) { + try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { + byte[] bytes = new byte[buf.readableBytes()]; + buf.getBytes(0, bytes, 0, bytes.length); + HexDump.dump(bytes, 0, stream, 0); + stream.flush(); + logger.trace( + String.format( + "%s %s [%s]:%n%s%n", + type, + buf, + ctx.channel().remoteAddress().toString(), + stream.toString(StandardCharsets.UTF_8.name()))); + } + } + } } diff --git a/src/main/java/com/beowulfe/hap/impl/http/impl/NettyHomekitHttpService.java b/src/main/java/com/beowulfe/hap/impl/http/impl/NettyHomekitHttpService.java index 4dd8bd166..1247c3936 100644 --- a/src/main/java/com/beowulfe/hap/impl/http/impl/NettyHomekitHttpService.java +++ b/src/main/java/com/beowulfe/hap/impl/http/impl/NettyHomekitHttpService.java @@ -1,5 +1,6 @@ package com.beowulfe.hap.impl.http.impl; +import com.beowulfe.hap.impl.http.HomekitClientConnectionFactory; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.group.ChannelGroup; @@ -9,76 +10,73 @@ import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.util.concurrent.*; - import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.concurrent.CompletableFuture; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.beowulfe.hap.impl.http.HomekitClientConnectionFactory; - class NettyHomekitHttpService { - private final EventLoopGroup bossGroup; - private final EventLoopGroup workerGroup; - - private final static Logger logger = LoggerFactory.getLogger(NettyHomekitHttpService.class); - private final ChannelGroup allChannels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); - private final int port; - private final int nThreads; - - public static NettyHomekitHttpService create(int port, int nThreads) { - return new NettyHomekitHttpService(port, nThreads); - } - - private NettyHomekitHttpService(int port, int nThreads) { - bossGroup = new NioEventLoopGroup(); - workerGroup = new NioEventLoopGroup(); - this.port = port; - this.nThreads = nThreads; - } - - public CompletableFuture create(HomekitClientConnectionFactory connectionFactory) { - final CompletableFuture portFuture = new CompletableFuture(); - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new ServerInitializer(connectionFactory, allChannels, nThreads)) - .option(ChannelOption.SO_BACKLOG, 128) - .childOption(ChannelOption.SO_KEEPALIVE, true); - final ChannelFuture bindFuture = b.bind(port); - bindFuture.addListener(new GenericFutureListener>() { + private final EventLoopGroup bossGroup; + private final EventLoopGroup workerGroup; + + private static final Logger logger = LoggerFactory.getLogger(NettyHomekitHttpService.class); + private final ChannelGroup allChannels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); + private final int port; + private final int nThreads; + + public static NettyHomekitHttpService create(int port, int nThreads) { + return new NettyHomekitHttpService(port, nThreads); + } + + private NettyHomekitHttpService(int port, int nThreads) { + bossGroup = new NioEventLoopGroup(); + workerGroup = new NioEventLoopGroup(); + this.port = port; + this.nThreads = nThreads; + } + + public CompletableFuture create(HomekitClientConnectionFactory connectionFactory) { + final CompletableFuture portFuture = new CompletableFuture(); + ServerBootstrap b = new ServerBootstrap(); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .handler(new LoggingHandler(LogLevel.INFO)) + .childHandler(new ServerInitializer(connectionFactory, allChannels, nThreads)) + .option(ChannelOption.SO_BACKLOG, 128) + .childOption(ChannelOption.SO_KEEPALIVE, true); + final ChannelFuture bindFuture = b.bind(port); + bindFuture.addListener( + new GenericFutureListener>() { - @Override - public void operationComplete(Future future) - throws Exception { - try { - future.get(); - SocketAddress socketAddress = bindFuture.channel().localAddress(); - if (socketAddress instanceof InetSocketAddress) { - logger.info("Bound homekit listener to "+socketAddress.toString()); - portFuture.complete(((InetSocketAddress) socketAddress).getPort()); - } else { - throw new RuntimeException("Unknown socket address type: "+socketAddress.getClass().getName()); - } - } catch (Exception e) { - portFuture.completeExceptionally(e); - } - } - }); - return portFuture; - } + @Override + public void operationComplete(Future future) throws Exception { + try { + future.get(); + SocketAddress socketAddress = bindFuture.channel().localAddress(); + if (socketAddress instanceof InetSocketAddress) { + logger.info("Bound homekit listener to " + socketAddress.toString()); + portFuture.complete(((InetSocketAddress) socketAddress).getPort()); + } else { + throw new RuntimeException( + "Unknown socket address type: " + socketAddress.getClass().getName()); + } + } catch (Exception e) { + portFuture.completeExceptionally(e); + } + } + }); + return portFuture; + } - public void shutdown() { - workerGroup.shutdownGracefully(); - bossGroup.shutdownGracefully(); - } + public void shutdown() { + workerGroup.shutdownGracefully(); + bossGroup.shutdownGracefully(); + } - public void resetConnections() { - logger.info("Resetting connections"); - allChannels.close(); - } + public void resetConnections() { + logger.info("Resetting connections"); + allChannels.close(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/http/impl/NettyResponseUtil.java b/src/main/java/com/beowulfe/hap/impl/http/impl/NettyResponseUtil.java index 3eaafcc58..71e897427 100644 --- a/src/main/java/com/beowulfe/hap/impl/http/impl/NettyResponseUtil.java +++ b/src/main/java/com/beowulfe/hap/impl/http/impl/NettyResponseUtil.java @@ -1,32 +1,32 @@ package com.beowulfe.hap.impl.http.impl; +import com.beowulfe.hap.impl.http.HttpResponse; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; - import java.util.Map.Entry; -import com.beowulfe.hap.impl.http.HttpResponse; - class NettyResponseUtil { - private static final HttpVersion EVENT_VERSION = new HttpVersion("EVENT", 1, 0, true); - - public static FullHttpResponse createResponse(HttpResponse homekitResponse) { - - FullHttpResponse response = new DefaultFullHttpResponse( - homekitResponse.getVersion() == HttpResponse.HttpVersion.EVENT_1_0 ? EVENT_VERSION : HttpVersion.HTTP_1_1, - HttpResponseStatus.valueOf(homekitResponse.getStatusCode()), - Unpooled.copiedBuffer(homekitResponse.getBody())); - for (Entry header: homekitResponse.getHeaders().entrySet()) { - response.headers().add(header.getKey(), header.getValue()); - } - response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes()); - response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); - return response; - } + private static final HttpVersion EVENT_VERSION = new HttpVersion("EVENT", 1, 0, true); + + public static FullHttpResponse createResponse(HttpResponse homekitResponse) { + FullHttpResponse response = + new DefaultFullHttpResponse( + homekitResponse.getVersion() == HttpResponse.HttpVersion.EVENT_1_0 + ? EVENT_VERSION + : HttpVersion.HTTP_1_1, + HttpResponseStatus.valueOf(homekitResponse.getStatusCode()), + Unpooled.copiedBuffer(homekitResponse.getBody())); + for (Entry header : homekitResponse.getHeaders().entrySet()) { + response.headers().add(header.getKey(), header.getValue()); + } + response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes()); + response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); + return response; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/http/impl/ServerInitializer.java b/src/main/java/com/beowulfe/hap/impl/http/impl/ServerInitializer.java index 031909917..5026bf2cf 100644 --- a/src/main/java/com/beowulfe/hap/impl/http/impl/ServerInitializer.java +++ b/src/main/java/com/beowulfe/hap/impl/http/impl/ServerInitializer.java @@ -1,5 +1,6 @@ package com.beowulfe.hap.impl.http.impl; +import com.beowulfe.hap.impl.http.HomekitClientConnectionFactory; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.group.ChannelGroup; @@ -9,33 +10,31 @@ import io.netty.util.concurrent.DefaultEventExecutorGroup; import io.netty.util.concurrent.EventExecutorGroup; -import com.beowulfe.hap.impl.http.HomekitClientConnectionFactory; - class ServerInitializer extends ChannelInitializer { - private static final int MAX_POST = 1000000; - - public static final String HTTP_HANDLER_NAME = "http"; - - private final HomekitClientConnectionFactory homekit; - private final ChannelGroup allChannels; - private final EventExecutorGroup blockingExecutorGroup; - - public ServerInitializer(HomekitClientConnectionFactory homekit, ChannelGroup allChannels, int nThreads) { - this.homekit = homekit; - this.allChannels = allChannels; - this.blockingExecutorGroup = new DefaultEventExecutorGroup(nThreads); - } - - @Override - protected void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline pipeline = ch.pipeline(); - pipeline.addLast(new LoggingHandler()); - pipeline.addLast(HTTP_HANDLER_NAME, new HttpResponseEncoderAggregate()); - pipeline.addLast(new HttpRequestDecoder()); - pipeline.addLast(new HttpObjectAggregator(MAX_POST)); - pipeline.addLast(blockingExecutorGroup, new AccessoryHandler(homekit)); - allChannels.add(ch); - } + private static final int MAX_POST = 1000000; + + public static final String HTTP_HANDLER_NAME = "http"; + + private final HomekitClientConnectionFactory homekit; + private final ChannelGroup allChannels; + private final EventExecutorGroup blockingExecutorGroup; + + public ServerInitializer( + HomekitClientConnectionFactory homekit, ChannelGroup allChannels, int nThreads) { + this.homekit = homekit; + this.allChannels = allChannels; + this.blockingExecutorGroup = new DefaultEventExecutorGroup(nThreads); + } + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast(new LoggingHandler()); + pipeline.addLast(HTTP_HANDLER_NAME, new HttpResponseEncoderAggregate()); + pipeline.addLast(new HttpRequestDecoder()); + pipeline.addLast(new HttpObjectAggregator(MAX_POST)); + pipeline.addLast(blockingExecutorGroup, new AccessoryHandler(homekit)); + allChannels.add(ch); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/jmdns/JmdnsHomekitAdvertiser.java b/src/main/java/com/beowulfe/hap/impl/jmdns/JmdnsHomekitAdvertiser.java index 5520c9c3f..8eea15ce8 100644 --- a/src/main/java/com/beowulfe/hap/impl/jmdns/JmdnsHomekitAdvertiser.java +++ b/src/main/java/com/beowulfe/hap/impl/jmdns/JmdnsHomekitAdvertiser.java @@ -5,88 +5,89 @@ import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; - import javax.jmdns.JmDNS; import javax.jmdns.ServiceInfo; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class JmdnsHomekitAdvertiser { - - private static final String SERVICE_TYPE = "_hap._tcp.local."; - - private final JmDNS jmdns; - private boolean discoverable = true; - private final static Logger logger = LoggerFactory.getLogger(JmdnsHomekitAdvertiser.class); - private boolean isAdvertising = false; - - private String label; - private String mac; - private int port; - private int configurationIndex; - - public JmdnsHomekitAdvertiser(InetAddress localAddress) throws UnknownHostException, IOException { - jmdns = JmDNS.create(localAddress); - } - public synchronized void advertise(String label, String mac, int port, int configurationIndex) throws Exception { - if (isAdvertising) { - throw new IllegalStateException("Homekit advertiser is already running"); - } - this.label = label; - this.mac = mac; - this.port = port; - this.configurationIndex = configurationIndex; - - logger.info("Advertising accessory "+label); - - registerService(); - - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - logger.info("Stopping advertising in response to shutdown."); - jmdns.unregisterAllServices(); - })); - isAdvertising = true; - } - - public synchronized void stop() { - jmdns.unregisterAllServices(); - } - - public synchronized void setDiscoverable(boolean discoverable) throws IOException { - if (this.discoverable != discoverable) { - this.discoverable = discoverable; - if (isAdvertising) { - logger.info("Re-creating service due to change in discoverability to "+discoverable); - jmdns.unregisterAllServices(); - registerService(); - } - } - } - - public synchronized void setConfigurationIndex(int revision) throws IOException { - if (this.configurationIndex != revision) { - this.configurationIndex = revision; - if (isAdvertising) { - logger.info("Re-creating service due to change in configuration index to "+revision); - jmdns.unregisterAllServices(); - registerService(); - } - } - } - - private void registerService() throws IOException { - logger.info("Registering "+SERVICE_TYPE+" on port "+port); - Map props = new HashMap<>(); - props.put("sf", discoverable ? "1" : "0"); - props.put("id", mac); - props.put("md", label); - props.put("c#", Integer.toString(configurationIndex)); - props.put("s#", "1"); - props.put("ff", "0"); - props.put("ci", "1"); - jmdns.registerService(ServiceInfo.create(SERVICE_TYPE, label, port, 1, 1, props)); - } - + private static final String SERVICE_TYPE = "_hap._tcp.local."; + + private final JmDNS jmdns; + private boolean discoverable = true; + private static final Logger logger = LoggerFactory.getLogger(JmdnsHomekitAdvertiser.class); + private boolean isAdvertising = false; + + private String label; + private String mac; + private int port; + private int configurationIndex; + + public JmdnsHomekitAdvertiser(InetAddress localAddress) throws UnknownHostException, IOException { + jmdns = JmDNS.create(localAddress); + } + + public synchronized void advertise(String label, String mac, int port, int configurationIndex) + throws Exception { + if (isAdvertising) { + throw new IllegalStateException("Homekit advertiser is already running"); + } + this.label = label; + this.mac = mac; + this.port = port; + this.configurationIndex = configurationIndex; + + logger.info("Advertising accessory " + label); + + registerService(); + + Runtime.getRuntime() + .addShutdownHook( + new Thread( + () -> { + logger.info("Stopping advertising in response to shutdown."); + jmdns.unregisterAllServices(); + })); + isAdvertising = true; + } + + public synchronized void stop() { + jmdns.unregisterAllServices(); + } + + public synchronized void setDiscoverable(boolean discoverable) throws IOException { + if (this.discoverable != discoverable) { + this.discoverable = discoverable; + if (isAdvertising) { + logger.info("Re-creating service due to change in discoverability to " + discoverable); + jmdns.unregisterAllServices(); + registerService(); + } + } + } + + public synchronized void setConfigurationIndex(int revision) throws IOException { + if (this.configurationIndex != revision) { + this.configurationIndex = revision; + if (isAdvertising) { + logger.info("Re-creating service due to change in configuration index to " + revision); + jmdns.unregisterAllServices(); + registerService(); + } + } + } + + private void registerService() throws IOException { + logger.info("Registering " + SERVICE_TYPE + " on port " + port); + Map props = new HashMap<>(); + props.put("sf", discoverable ? "1" : "0"); + props.put("id", mac); + props.put("md", label); + props.put("c#", Integer.toString(configurationIndex)); + props.put("s#", "1"); + props.put("ff", "0"); + props.put("ci", "1"); + jmdns.registerService(ServiceInfo.create(SERVICE_TYPE, label, port, 1, 1, props)); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/json/AccessoryController.java b/src/main/java/com/beowulfe/hap/impl/json/AccessoryController.java index 0f92bc1ea..67304e615 100644 --- a/src/main/java/com/beowulfe/hap/impl/json/AccessoryController.java +++ b/src/main/java/com/beowulfe/hap/impl/json/AccessoryController.java @@ -1,5 +1,10 @@ package com.beowulfe.hap.impl.json; +import com.beowulfe.hap.HomekitAccessory; +import com.beowulfe.hap.Service; +import com.beowulfe.hap.characteristics.Characteristic; +import com.beowulfe.hap.impl.HomekitRegistry; +import com.beowulfe.hap.impl.http.HttpResponse; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Collection; @@ -8,77 +13,78 @@ import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; - import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.json.JsonObject; import javax.json.JsonObjectBuilder; -import com.beowulfe.hap.HomekitAccessory; -import com.beowulfe.hap.Service; -import com.beowulfe.hap.characteristics.Characteristic; -import com.beowulfe.hap.impl.HomekitRegistry; -import com.beowulfe.hap.impl.http.HttpResponse; - public class AccessoryController { - private final HomekitRegistry registry; - - public AccessoryController(HomekitRegistry registry) { - this.registry = registry; - } + private final HomekitRegistry registry; + + public AccessoryController(HomekitRegistry registry) { + this.registry = registry; + } + + public HttpResponse listing() throws Exception { + JsonArrayBuilder accessories = Json.createArrayBuilder(); + + Map>> accessoryServiceFutures = new HashMap<>(); + for (HomekitAccessory accessory : registry.getAccessories()) { + int iid = 0; + List> serviceFutures = new ArrayList<>(); + for (Service service : registry.getServices(accessory.getId())) { + serviceFutures.add(toJson(service, iid)); + iid += service.getCharacteristics().size() + 1; + } + accessoryServiceFutures.put(accessory.getId(), serviceFutures); + } + + Map serviceArrayBuilders = new HashMap<>(); + for (Entry>> entry : + accessoryServiceFutures.entrySet()) { + JsonArrayBuilder arr = Json.createArrayBuilder(); + for (CompletableFuture future : entry.getValue()) { + arr.add(future.join()); + } + serviceArrayBuilders.put(entry.getKey(), arr); + } + + for (HomekitAccessory accessory : registry.getAccessories()) { + accessories.add( + Json.createObjectBuilder() + .add("aid", accessory.getId()) + .add("services", serviceArrayBuilders.get(accessory.getId()))); + } + + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + Json.createWriter(baos) + .write(Json.createObjectBuilder().add("accessories", accessories).build()); + return new HapJsonResponse(baos.toByteArray()); + } + } + + private CompletableFuture toJson(Service service, int interfaceId) throws Exception { + JsonObjectBuilder builder = + Json.createObjectBuilder().add("iid", ++interfaceId).add("type", service.getType()); + List characteristics = service.getCharacteristics(); + Collection> characteristicFutures = + new ArrayList<>(characteristics.size()); + for (Characteristic characteristic : characteristics) { + characteristicFutures.add(characteristic.toJson(++interfaceId)); + } - public HttpResponse listing() throws Exception { - JsonArrayBuilder accessories = Json.createArrayBuilder(); - - Map>> accessoryServiceFutures = new HashMap<>(); - for (HomekitAccessory accessory: registry.getAccessories()) { - int iid = 0; - List> serviceFutures = new ArrayList<>(); - for (Service service: registry.getServices(accessory.getId())) { - serviceFutures.add(toJson(service, iid)); - iid += service.getCharacteristics().size() + 1; - } - accessoryServiceFutures.put(accessory.getId(), serviceFutures); - } - - Map serviceArrayBuilders = new HashMap<>(); - for (Entry>> entry: accessoryServiceFutures.entrySet()) { - JsonArrayBuilder arr = Json.createArrayBuilder(); - for (CompletableFuture future: entry.getValue()) { - arr.add(future.join()); - } - serviceArrayBuilders.put(entry.getKey(), arr); - } - - for (HomekitAccessory accessory: registry.getAccessories()) { - accessories.add(Json.createObjectBuilder().add("aid", accessory.getId()).add("services", serviceArrayBuilders.get(accessory.getId()))); - } - - try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - Json.createWriter(baos).write( - Json.createObjectBuilder().add("accessories", accessories).build() - ); - return new HapJsonResponse(baos.toByteArray()); - } - } - - private CompletableFuture toJson(Service service, int interfaceId) throws Exception { - JsonObjectBuilder builder = Json.createObjectBuilder() - .add("iid", ++interfaceId) - .add("type", service.getType()); - List characteristics = service.getCharacteristics(); - Collection> characteristicFutures = new ArrayList<>(characteristics.size()); - for (Characteristic characteristic: characteristics) { - characteristicFutures.add(characteristic.toJson(++interfaceId)); - } - - return CompletableFuture.allOf(characteristicFutures.toArray(new CompletableFuture[characteristicFutures.size()])) - .thenApply(v -> { - JsonArrayBuilder jsonCharacteristics = Json.createArrayBuilder(); - characteristicFutures.stream().map(future -> future.join()).forEach(c -> jsonCharacteristics.add(c)); - builder.add("characteristics", jsonCharacteristics); - return builder.build(); - }); - } + return CompletableFuture.allOf( + characteristicFutures.toArray(new CompletableFuture[characteristicFutures.size()])) + .thenApply( + v -> { + JsonArrayBuilder jsonCharacteristics = Json.createArrayBuilder(); + characteristicFutures + .stream() + .map(future -> future.join()) + .forEach(c -> jsonCharacteristics.add(c)); + builder.add("characteristics", jsonCharacteristics); + return builder.build(); + }); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/json/CharacteristicsController.java b/src/main/java/com/beowulfe/hap/impl/json/CharacteristicsController.java index 343ddd0fa..820cab5c8 100644 --- a/src/main/java/com/beowulfe/hap/impl/json/CharacteristicsController.java +++ b/src/main/java/com/beowulfe/hap/impl/json/CharacteristicsController.java @@ -8,84 +8,89 @@ import com.beowulfe.hap.impl.http.HttpRequest; import com.beowulfe.hap.impl.http.HttpResponse; import com.beowulfe.hap.impl.responses.NotFoundResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.json.*; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Map; +import javax.json.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class CharacteristicsController { - - Logger logger = LoggerFactory.getLogger(CharacteristicsController.class); - private final HomekitRegistry registry; - private final SubscriptionManager subscriptions; - - public CharacteristicsController(HomekitRegistry registry, SubscriptionManager subscriptions) { - this.registry = registry; - this.subscriptions = subscriptions; - } + Logger logger = LoggerFactory.getLogger(CharacteristicsController.class); + + private final HomekitRegistry registry; + private final SubscriptionManager subscriptions; + + public CharacteristicsController(HomekitRegistry registry, SubscriptionManager subscriptions) { + this.registry = registry; + this.subscriptions = subscriptions; + } - public HttpResponse get(HttpRequest request) throws Exception { - String uri = request.getUri(); - // Characteristics are requested with /characteristics?id=1.1,2.1,3.1 - String query = uri.substring("/characteristics?id=".length()); - String[] ids = query.split(","); - JsonArrayBuilder characteristics = Json.createArrayBuilder(); - for (String id : ids) { - String[] parts = id.split("\\."); - if (parts.length != 2) { - logger.error("Unexpected characteristics request: " + uri); - return new NotFoundResponse(); - } - int aid = Integer.parseInt(parts[0]); - int iid = Integer.parseInt(parts[1]); - JsonObjectBuilder characteristic = Json.createObjectBuilder(); - Map characteristicMap = registry.getCharacteristics(aid); - if (!characteristicMap.isEmpty()) { - Characteristic targetCharacteristic = characteristicMap.get(iid); - if (targetCharacteristic != null) { - targetCharacteristic.supplyValue(characteristic); + public HttpResponse get(HttpRequest request) throws Exception { + String uri = request.getUri(); + // Characteristics are requested with /characteristics?id=1.1,2.1,3.1 + String query = uri.substring("/characteristics?id=".length()); + String[] ids = query.split(","); + JsonArrayBuilder characteristics = Json.createArrayBuilder(); + for (String id : ids) { + String[] parts = id.split("\\."); + if (parts.length != 2) { + logger.error("Unexpected characteristics request: " + uri); + return new NotFoundResponse(); + } + int aid = Integer.parseInt(parts[0]); + int iid = Integer.parseInt(parts[1]); + JsonObjectBuilder characteristic = Json.createObjectBuilder(); + Map characteristicMap = registry.getCharacteristics(aid); + if (!characteristicMap.isEmpty()) { + Characteristic targetCharacteristic = characteristicMap.get(iid); + if (targetCharacteristic != null) { + targetCharacteristic.supplyValue(characteristic); - characteristics.add(characteristic.add("aid", aid).add("iid", iid).build()); - } else { - logger.warn("Accessory " + aid + " does not have characteristic " + iid + "Request: " + uri); - } - } else { - logger.warn("Accessory " + aid + " has no characteristics or does not exist. Request: " + uri); - } - } - try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - Json.createWriter(baos).write(Json.createObjectBuilder().add("characteristics", characteristics.build()).build()); - return new HapJsonResponse(baos.toByteArray()); - } - } + characteristics.add(characteristic.add("aid", aid).add("iid", iid).build()); + } else { + logger.warn( + "Accessory " + aid + " does not have characteristic " + iid + "Request: " + uri); + } + } else { + logger.warn( + "Accessory " + aid + " has no characteristics or does not exist. Request: " + uri); + } + } + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + Json.createWriter(baos) + .write( + Json.createObjectBuilder().add("characteristics", characteristics.build()).build()); + return new HapJsonResponse(baos.toByteArray()); + } + } - public HttpResponse put(HttpRequest request, HomekitClientConnection connection) throws Exception { - try(ByteArrayInputStream bais = new ByteArrayInputStream(request.getBody())) { - JsonArray jsonCharacteristics = Json.createReader(bais) - .readObject().getJsonArray("characteristics"); - for (JsonValue value: jsonCharacteristics) { - JsonObject jsonCharacteristic = (JsonObject) value; - int aid = jsonCharacteristic.getInt("aid"); - int iid = jsonCharacteristic.getInt("iid"); - Characteristic characteristic = registry.getCharacteristics(aid).get(iid); - - if (jsonCharacteristic.containsKey("value")) { - characteristic.setValue(jsonCharacteristic.get("value")); - } - if (jsonCharacteristic.containsKey("ev") && characteristic instanceof EventableCharacteristic) { - if (jsonCharacteristic.getBoolean("ev")) { - subscriptions.addSubscription(aid, iid, (EventableCharacteristic) characteristic, connection); - } else { - subscriptions.removeSubscription((EventableCharacteristic) characteristic, connection); - } - } - } - } - return new HapJsonNoContentResponse(); - } + public HttpResponse put(HttpRequest request, HomekitClientConnection connection) + throws Exception { + try (ByteArrayInputStream bais = new ByteArrayInputStream(request.getBody())) { + JsonArray jsonCharacteristics = + Json.createReader(bais).readObject().getJsonArray("characteristics"); + for (JsonValue value : jsonCharacteristics) { + JsonObject jsonCharacteristic = (JsonObject) value; + int aid = jsonCharacteristic.getInt("aid"); + int iid = jsonCharacteristic.getInt("iid"); + Characteristic characteristic = registry.getCharacteristics(aid).get(iid); + if (jsonCharacteristic.containsKey("value")) { + characteristic.setValue(jsonCharacteristic.get("value")); + } + if (jsonCharacteristic.containsKey("ev") + && characteristic instanceof EventableCharacteristic) { + if (jsonCharacteristic.getBoolean("ev")) { + subscriptions.addSubscription( + aid, iid, (EventableCharacteristic) characteristic, connection); + } else { + subscriptions.removeSubscription((EventableCharacteristic) characteristic, connection); + } + } + } + } + return new HapJsonNoContentResponse(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/json/EventController.java b/src/main/java/com/beowulfe/hap/impl/json/EventController.java index b660a77ba..c1cd3d22c 100644 --- a/src/main/java/com/beowulfe/hap/impl/json/EventController.java +++ b/src/main/java/com/beowulfe/hap/impl/json/EventController.java @@ -1,35 +1,32 @@ package com.beowulfe.hap.impl.json; +import com.beowulfe.hap.characteristics.EventableCharacteristic; +import com.beowulfe.hap.impl.http.HttpResponse; import java.io.ByteArrayOutputStream; - import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.json.JsonObject; import javax.json.JsonObjectBuilder; -import com.beowulfe.hap.characteristics.EventableCharacteristic; -import com.beowulfe.hap.impl.http.HttpResponse; - public class EventController { - public HttpResponse getMessage(int accessoryId, int iid, EventableCharacteristic changed) throws Exception { - JsonArrayBuilder characteristics = Json.createArrayBuilder(); - - JsonObjectBuilder characteristicBuilder = Json.createObjectBuilder(); - characteristicBuilder.add("aid", accessoryId); - characteristicBuilder.add("iid", iid); - changed.supplyValue(characteristicBuilder); - characteristics.add(characteristicBuilder.build()); - - JsonObject data = Json.createObjectBuilder().add("characteristics", characteristics).build(); - - try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - Json.createWriter(baos).write(data); - byte[] dataBytes = baos.toByteArray(); - - return new EventResponse(dataBytes); - } - - } + public HttpResponse getMessage(int accessoryId, int iid, EventableCharacteristic changed) + throws Exception { + JsonArrayBuilder characteristics = Json.createArrayBuilder(); + + JsonObjectBuilder characteristicBuilder = Json.createObjectBuilder(); + characteristicBuilder.add("aid", accessoryId); + characteristicBuilder.add("iid", iid); + changed.supplyValue(characteristicBuilder); + characteristics.add(characteristicBuilder.build()); + + JsonObject data = Json.createObjectBuilder().add("characteristics", characteristics).build(); + + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + Json.createWriter(baos).write(data); + byte[] dataBytes = baos.toByteArray(); + return new EventResponse(dataBytes); + } + } } diff --git a/src/main/java/com/beowulfe/hap/impl/json/EventResponse.java b/src/main/java/com/beowulfe/hap/impl/json/EventResponse.java index 2cb06ec23..5ece1de25 100644 --- a/src/main/java/com/beowulfe/hap/impl/json/EventResponse.java +++ b/src/main/java/com/beowulfe/hap/impl/json/EventResponse.java @@ -1,14 +1,13 @@ package com.beowulfe.hap.impl.json; - public class EventResponse extends HapJsonResponse { - public EventResponse(byte[] body) { - super(body); - } - - @Override - public HttpVersion getVersion() { - return HttpVersion.EVENT_1_0; - } + public EventResponse(byte[] body) { + super(body); + } + + @Override + public HttpVersion getVersion() { + return HttpVersion.EVENT_1_0; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/json/HapJsonNoContentResponse.java b/src/main/java/com/beowulfe/hap/impl/json/HapJsonNoContentResponse.java index c21f8efe1..a339bb5bc 100644 --- a/src/main/java/com/beowulfe/hap/impl/json/HapJsonNoContentResponse.java +++ b/src/main/java/com/beowulfe/hap/impl/json/HapJsonNoContentResponse.java @@ -1,13 +1,13 @@ package com.beowulfe.hap.impl.json; -class HapJsonNoContentResponse extends HapJsonResponse{ +class HapJsonNoContentResponse extends HapJsonResponse { - public HapJsonNoContentResponse() { - super(new byte[0]); - } + public HapJsonNoContentResponse() { + super(new byte[0]); + } - @Override - public int getStatusCode() { - return 204; - } + @Override + public int getStatusCode() { + return 204; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/json/HapJsonResponse.java b/src/main/java/com/beowulfe/hap/impl/json/HapJsonResponse.java index b7282a94c..a4f8a6cc9 100644 --- a/src/main/java/com/beowulfe/hap/impl/json/HapJsonResponse.java +++ b/src/main/java/com/beowulfe/hap/impl/json/HapJsonResponse.java @@ -1,28 +1,28 @@ package com.beowulfe.hap.impl.json; +import com.beowulfe.hap.impl.responses.OkResponse; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import com.beowulfe.hap.impl.responses.OkResponse; - class HapJsonResponse extends OkResponse { - - private static final Map headers = Collections.unmodifiableMap( - new HashMap() { - private static final long serialVersionUID = 1L; - { - put("Content-type", "application/hap+json"); - } - }); - public HapJsonResponse(byte[] body) { - super(body); - } - - @Override - public Map getHeaders() { - return headers; - } + private static final Map headers = + Collections.unmodifiableMap( + new HashMap() { + private static final long serialVersionUID = 1L; + + { + put("Content-type", "application/hap+json"); + } + }); + + public HapJsonResponse(byte[] body) { + super(body); + } + @Override + public Map getHeaders() { + return headers; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/pairing/ByteUtils.java b/src/main/java/com/beowulfe/hap/impl/pairing/ByteUtils.java index 1d8eba7f8..0ce9e8d4d 100644 --- a/src/main/java/com/beowulfe/hap/impl/pairing/ByteUtils.java +++ b/src/main/java/com/beowulfe/hap/impl/pairing/ByteUtils.java @@ -8,36 +8,36 @@ class ByteUtils { - public static byte[] joinBytes(byte[]... piece) { - int pos = 0; - int length = 0; - for(int i=0; i 0) - { - output.write(buffer, 0, bytesRead); - remaining -= bytesRead; - } - } + public static byte[] joinBytes(byte[]... piece) { + int pos = 0; + int length = 0; + for (int i = 0; i < piece.length; i++) { + length += piece[i].length; + } + byte[] ret = new byte[length]; + for (int i = 0; i < piece.length; i++) { + System.arraycopy(piece[i], 0, ret, pos, piece[i].length); + pos += piece[i].length; + } + return ret; + } + + public static byte[] toByteArray(BigInteger i) { + byte[] array = i.toByteArray(); + if (array[0] == 0) { + array = Arrays.copyOfRange(array, 1, array.length); + } + return array; + } + + public static void copyStream(InputStream input, OutputStream output, int length) + throws IOException { + byte[] buffer = new byte[length]; + int remaining = length; + int bytesRead; + while ((bytesRead = input.read(buffer, 0, remaining)) != -1 && remaining > 0) { + output.write(buffer, 0, bytesRead); + remaining -= bytesRead; + } + } } diff --git a/src/main/java/com/beowulfe/hap/impl/pairing/ClientEvidenceRoutineImpl.java b/src/main/java/com/beowulfe/hap/impl/pairing/ClientEvidenceRoutineImpl.java index c8c9ccce7..1afd877bb 100644 --- a/src/main/java/com/beowulfe/hap/impl/pairing/ClientEvidenceRoutineImpl.java +++ b/src/main/java/com/beowulfe/hap/impl/pairing/ClientEvidenceRoutineImpl.java @@ -1,63 +1,61 @@ package com.beowulfe.hap.impl.pairing; +import com.nimbusds.srp6.*; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import com.nimbusds.srp6.*; - class ClientEvidenceRoutineImpl implements ClientEvidenceRoutine { - public ClientEvidenceRoutineImpl() { - // TODO Auto-generated constructor stub - } - - /** - * Calculates M1 according to the following formula: - * - * M1 = H(H(N) xor H(g) || H(username) || s || A || B || H(S)) - */ - @Override - public BigInteger computeClientEvidence(SRP6CryptoParams cryptoParams, - SRP6ClientEvidenceContext ctx) { - - MessageDigest digest; - try { - digest = MessageDigest.getInstance(cryptoParams.H); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("Could not locate requested algorithm", e); - } - digest.update(SrpHandler.bigIntegerToUnsignedByteArray(cryptoParams.N)); - byte[] hN = digest.digest(); - - digest.update(SrpHandler.bigIntegerToUnsignedByteArray(cryptoParams.g)); - byte[] hg = digest.digest(); - - byte[] hNhg = xor(hN, hg); - - digest.update(ctx.userID.getBytes(StandardCharsets.UTF_8)); - byte[] hu = digest.digest(); - - digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.S)); - byte[] hS = digest.digest(); - - digest.update(hNhg); - digest.update(hu); - digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.s)); - digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.A)); - digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.B)); - digest.update(hS); - BigInteger ret = new BigInteger(1, digest.digest()); - return ret; - } - - private static byte[] xor(byte[] b1, byte[] b2) { - byte[] result = new byte[b1.length]; - for (int i=0; iM1 = H(H(N) xor H(g) || H(username) || s || A || B || H(S)) + */ + @Override + public BigInteger computeClientEvidence( + SRP6CryptoParams cryptoParams, SRP6ClientEvidenceContext ctx) { + + MessageDigest digest; + try { + digest = MessageDigest.getInstance(cryptoParams.H); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Could not locate requested algorithm", e); + } + digest.update(SrpHandler.bigIntegerToUnsignedByteArray(cryptoParams.N)); + byte[] hN = digest.digest(); + + digest.update(SrpHandler.bigIntegerToUnsignedByteArray(cryptoParams.g)); + byte[] hg = digest.digest(); + + byte[] hNhg = xor(hN, hg); + + digest.update(ctx.userID.getBytes(StandardCharsets.UTF_8)); + byte[] hu = digest.digest(); + + digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.S)); + byte[] hS = digest.digest(); + + digest.update(hNhg); + digest.update(hu); + digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.s)); + digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.A)); + digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.B)); + digest.update(hS); + BigInteger ret = new BigInteger(1, digest.digest()); + return ret; + } + private static byte[] xor(byte[] b1, byte[] b2) { + byte[] result = new byte[b1.length]; + for (int i = 0; i < b1.length; i++) { + result[i] = (byte) (b1[i] ^ b2[i]); + } + return result; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/pairing/FinalPairHandler.java b/src/main/java/com/beowulfe/hap/impl/pairing/FinalPairHandler.java index 3ba60e4be..1e37d1fa8 100644 --- a/src/main/java/com/beowulfe/hap/impl/pairing/FinalPairHandler.java +++ b/src/main/java/com/beowulfe/hap/impl/pairing/FinalPairHandler.java @@ -1,11 +1,5 @@ package com.beowulfe.hap.impl.pairing; -import java.nio.charset.StandardCharsets; - -import org.bouncycastle.crypto.digests.SHA512Digest; -import org.bouncycastle.crypto.generators.HKDFBytesGenerator; -import org.bouncycastle.crypto.params.HKDFParameters; - import com.beowulfe.hap.HomekitAuthInfo; import com.beowulfe.hap.impl.crypto.*; import com.beowulfe.hap.impl.http.HttpResponse; @@ -13,86 +7,101 @@ import com.beowulfe.hap.impl.pairing.PairSetupRequest.Stage3Request; import com.beowulfe.hap.impl.pairing.TypeLengthValueUtils.DecodeResult; import com.beowulfe.hap.impl.pairing.TypeLengthValueUtils.Encoder; +import java.nio.charset.StandardCharsets; +import org.bouncycastle.crypto.digests.SHA512Digest; +import org.bouncycastle.crypto.generators.HKDFBytesGenerator; +import org.bouncycastle.crypto.params.HKDFParameters; class FinalPairHandler { - - private final byte[] k; - private final HomekitAuthInfo authInfo; - private final JmdnsHomekitAdvertiser advertiser; - - private byte[] hkdf_enc_key; - - public FinalPairHandler(byte[] k, HomekitAuthInfo authInfo, JmdnsHomekitAdvertiser advertiser) { - this.k = k; - this.authInfo = authInfo; - this.advertiser = advertiser; - } - - public HttpResponse handle(PairSetupRequest req) throws Exception { - HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA512Digest()); - hkdf.init(new HKDFParameters(k, "Pair-Setup-Encrypt-Salt".getBytes(StandardCharsets.UTF_8), - "Pair-Setup-Encrypt-Info".getBytes(StandardCharsets.UTF_8))); - byte[] okm = hkdf_enc_key = new byte[32]; - hkdf.generateBytes(okm, 0, 32); - - return decrypt((Stage3Request) req, okm); - } - - private HttpResponse decrypt(Stage3Request req, byte[] key) throws Exception { - ChachaDecoder chacha = new ChachaDecoder(key, "PS-Msg05".getBytes(StandardCharsets.UTF_8)); - byte[] plaintext = chacha.decodeCiphertext(req.getAuthTagData(), req.getMessageData()); - - DecodeResult d = TypeLengthValueUtils.decode(plaintext); - byte[] username = d.getBytes(MessageType.USERNAME); - byte[] ltpk = d.getBytes(MessageType.PUBLIC_KEY); - byte[] proof = d.getBytes(MessageType.SIGNATURE); - return createUser(username, ltpk, proof); - } - - private HttpResponse createUser(byte[] username, byte[] ltpk, byte[] proof) throws Exception { - HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA512Digest()); - hkdf.init(new HKDFParameters(k, "Pair-Setup-Controller-Sign-Salt".getBytes(StandardCharsets.UTF_8), - "Pair-Setup-Controller-Sign-Info".getBytes(StandardCharsets.UTF_8))); - byte[] okm = new byte[32]; - hkdf.generateBytes(okm, 0, 32); - - byte[] completeData = ByteUtils.joinBytes(okm, username, ltpk); - - if (!new EdsaVerifier(ltpk).verify(completeData, proof)) { - throw new Exception("Invalid signature"); - } - authInfo.createUser(authInfo.getMac()+new String(username, StandardCharsets.UTF_8), ltpk); - advertiser.setDiscoverable(false); - return createResponse(); - } - - private HttpResponse createResponse() throws Exception { - HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA512Digest()); - hkdf.init(new HKDFParameters(k, "Pair-Setup-Accessory-Sign-Salt".getBytes(StandardCharsets.UTF_8), - "Pair-Setup-Accessory-Sign-Info".getBytes(StandardCharsets.UTF_8))); - byte[] okm = new byte[32]; - hkdf.generateBytes(okm, 0, 32); - - EdsaSigner signer = new EdsaSigner(authInfo.getPrivateKey()); - - byte[] material = ByteUtils.joinBytes(okm, authInfo.getMac().getBytes(StandardCharsets.UTF_8), signer.getPublicKey()); - - byte[] proof = signer.sign(material); - - Encoder encoder = TypeLengthValueUtils.getEncoder(); - encoder.add(MessageType.USERNAME, authInfo.getMac().getBytes(StandardCharsets.UTF_8)); - encoder.add(MessageType.PUBLIC_KEY, signer.getPublicKey()); - encoder.add(MessageType.SIGNATURE, proof); - byte[] plaintext = encoder.toByteArray(); - - ChachaEncoder chacha = new ChachaEncoder(hkdf_enc_key, "PS-Msg06".getBytes(StandardCharsets.UTF_8)); - byte[] ciphertext = chacha.encodeCiphertext(plaintext); - - encoder = TypeLengthValueUtils.getEncoder(); - encoder.add(MessageType.STATE, (short) 6); - encoder.add(MessageType.ENCRYPTED_DATA, ciphertext); - - return new PairingResponse(encoder.toByteArray()); - } + private final byte[] k; + private final HomekitAuthInfo authInfo; + private final JmdnsHomekitAdvertiser advertiser; + + private byte[] hkdf_enc_key; + + public FinalPairHandler(byte[] k, HomekitAuthInfo authInfo, JmdnsHomekitAdvertiser advertiser) { + this.k = k; + this.authInfo = authInfo; + this.advertiser = advertiser; + } + + public HttpResponse handle(PairSetupRequest req) throws Exception { + HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA512Digest()); + hkdf.init( + new HKDFParameters( + k, + "Pair-Setup-Encrypt-Salt".getBytes(StandardCharsets.UTF_8), + "Pair-Setup-Encrypt-Info".getBytes(StandardCharsets.UTF_8))); + byte[] okm = hkdf_enc_key = new byte[32]; + hkdf.generateBytes(okm, 0, 32); + + return decrypt((Stage3Request) req, okm); + } + + private HttpResponse decrypt(Stage3Request req, byte[] key) throws Exception { + ChachaDecoder chacha = new ChachaDecoder(key, "PS-Msg05".getBytes(StandardCharsets.UTF_8)); + byte[] plaintext = chacha.decodeCiphertext(req.getAuthTagData(), req.getMessageData()); + + DecodeResult d = TypeLengthValueUtils.decode(plaintext); + byte[] username = d.getBytes(MessageType.USERNAME); + byte[] ltpk = d.getBytes(MessageType.PUBLIC_KEY); + byte[] proof = d.getBytes(MessageType.SIGNATURE); + return createUser(username, ltpk, proof); + } + + private HttpResponse createUser(byte[] username, byte[] ltpk, byte[] proof) throws Exception { + HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA512Digest()); + hkdf.init( + new HKDFParameters( + k, + "Pair-Setup-Controller-Sign-Salt".getBytes(StandardCharsets.UTF_8), + "Pair-Setup-Controller-Sign-Info".getBytes(StandardCharsets.UTF_8))); + byte[] okm = new byte[32]; + hkdf.generateBytes(okm, 0, 32); + + byte[] completeData = ByteUtils.joinBytes(okm, username, ltpk); + + if (!new EdsaVerifier(ltpk).verify(completeData, proof)) { + throw new Exception("Invalid signature"); + } + authInfo.createUser(authInfo.getMac() + new String(username, StandardCharsets.UTF_8), ltpk); + advertiser.setDiscoverable(false); + return createResponse(); + } + + private HttpResponse createResponse() throws Exception { + HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA512Digest()); + hkdf.init( + new HKDFParameters( + k, + "Pair-Setup-Accessory-Sign-Salt".getBytes(StandardCharsets.UTF_8), + "Pair-Setup-Accessory-Sign-Info".getBytes(StandardCharsets.UTF_8))); + byte[] okm = new byte[32]; + hkdf.generateBytes(okm, 0, 32); + + EdsaSigner signer = new EdsaSigner(authInfo.getPrivateKey()); + + byte[] material = + ByteUtils.joinBytes( + okm, authInfo.getMac().getBytes(StandardCharsets.UTF_8), signer.getPublicKey()); + + byte[] proof = signer.sign(material); + + Encoder encoder = TypeLengthValueUtils.getEncoder(); + encoder.add(MessageType.USERNAME, authInfo.getMac().getBytes(StandardCharsets.UTF_8)); + encoder.add(MessageType.PUBLIC_KEY, signer.getPublicKey()); + encoder.add(MessageType.SIGNATURE, proof); + byte[] plaintext = encoder.toByteArray(); + + ChachaEncoder chacha = + new ChachaEncoder(hkdf_enc_key, "PS-Msg06".getBytes(StandardCharsets.UTF_8)); + byte[] ciphertext = chacha.encodeCiphertext(plaintext); + + encoder = TypeLengthValueUtils.getEncoder(); + encoder.add(MessageType.STATE, (short) 6); + encoder.add(MessageType.ENCRYPTED_DATA, ciphertext); + + return new PairingResponse(encoder.toByteArray()); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/pairing/HomekitSRP6Routines.java b/src/main/java/com/beowulfe/hap/impl/pairing/HomekitSRP6Routines.java index d919f24cd..a63135f3f 100644 --- a/src/main/java/com/beowulfe/hap/impl/pairing/HomekitSRP6Routines.java +++ b/src/main/java/com/beowulfe/hap/impl/pairing/HomekitSRP6Routines.java @@ -4,62 +4,55 @@ import java.security.SecureRandom; /** - * This class is modified from the nimbus SRP library to provide methods that are compatible - * with some pecularities of Homekit. Namely, the need for a 3072 bit private value - * + * This class is modified from the nimbus SRP library to provide methods that are compatible with + * some pecularities of Homekit. Namely, the need for a 3072 bit private value + * * @author Vladimir Dzhuvinov */ public class HomekitSRP6Routines { - public static BigInteger generatePrivateValue(BigInteger N, SecureRandom random) { - final int minBits = Math.min(3072, N.bitLength() / 2); - - BigInteger min = BigInteger.ONE.shiftLeft(minBits - 1); - BigInteger max = N.subtract(BigInteger.ONE); - - return createRandomBigIntegerInRange(min, max, random); - } - - /** - * Returns a random big integer in the specified range [min, max]. - * - * @param min The least value that may be generated. Must not be - * {@code null}. - * @param max The greatest value that may be generated. Must not be - * {@code null}. - * @param random Source of randomness. Must not be {@code null}. - * - * @return A random big integer in the range [min, max]. - */ - protected static BigInteger createRandomBigIntegerInRange(final BigInteger min, - final BigInteger max, - final SecureRandom random) { - - final int cmp = min.compareTo(max); - - if (cmp >= 0) { - - if (cmp > 0) - throw new IllegalArgumentException("'min' may not be greater than 'max'"); - - return min; - } - - if (min.bitLength() > max.bitLength() / 2) - return createRandomBigIntegerInRange(BigInteger.ZERO, max.subtract(min), random).add(min); - - final int MAX_ITERATIONS = 1000; - - for (int i = 0; i < MAX_ITERATIONS; ++i) { - - BigInteger x = new BigInteger(max.bitLength(), random); - - if (x.compareTo(min) >= 0 && x.compareTo(max) <= 0) - return x; - } - - // fall back to a faster (restricted) method - return new BigInteger(max.subtract(min).bitLength() - 1, random).add(min); - } + public static BigInteger generatePrivateValue(BigInteger N, SecureRandom random) { + final int minBits = Math.min(3072, N.bitLength() / 2); + BigInteger min = BigInteger.ONE.shiftLeft(minBits - 1); + BigInteger max = N.subtract(BigInteger.ONE); + + return createRandomBigIntegerInRange(min, max, random); + } + + /** + * Returns a random big integer in the specified range [min, max]. + * + * @param min The least value that may be generated. Must not be {@code null}. + * @param max The greatest value that may be generated. Must not be {@code null}. + * @param random Source of randomness. Must not be {@code null}. + * @return A random big integer in the range [min, max]. + */ + protected static BigInteger createRandomBigIntegerInRange( + final BigInteger min, final BigInteger max, final SecureRandom random) { + + final int cmp = min.compareTo(max); + + if (cmp >= 0) { + + if (cmp > 0) throw new IllegalArgumentException("'min' may not be greater than 'max'"); + + return min; + } + + if (min.bitLength() > max.bitLength() / 2) + return createRandomBigIntegerInRange(BigInteger.ZERO, max.subtract(min), random).add(min); + + final int MAX_ITERATIONS = 1000; + + for (int i = 0; i < MAX_ITERATIONS; ++i) { + + BigInteger x = new BigInteger(max.bitLength(), random); + + if (x.compareTo(min) >= 0 && x.compareTo(max) <= 0) return x; + } + + // fall back to a faster (restricted) method + return new BigInteger(max.subtract(min).bitLength() - 1, random).add(min); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/pairing/HomekitSRP6ServerSession.java b/src/main/java/com/beowulfe/hap/impl/pairing/HomekitSRP6ServerSession.java index d2cd948cd..1c24e8fad 100644 --- a/src/main/java/com/beowulfe/hap/impl/pairing/HomekitSRP6ServerSession.java +++ b/src/main/java/com/beowulfe/hap/impl/pairing/HomekitSRP6ServerSession.java @@ -1,8 +1,5 @@ package com.beowulfe.hap.impl.pairing; - -import java.math.BigInteger; - import com.nimbusds.srp6.SRP6ClientEvidenceContext; import com.nimbusds.srp6.SRP6CryptoParams; import com.nimbusds.srp6.SRP6Exception; @@ -10,350 +7,290 @@ import com.nimbusds.srp6.SRP6ServerEvidenceContext; import com.nimbusds.srp6.SRP6Session; import com.nimbusds.srp6.URoutineContext; - +import java.math.BigInteger; /** - * This is a slightly modified version of the SRP6ServerSession class included - * with nimbus. The only change made for homekit compatability is a change to the - * size of the b key. Homekit pairing fails if b is not 3072 bytes. - * - * Stateful server-side Secure Remote Password (SRP-6a) authentication session. - * Handles the computing and storing of SRP-6a variables between the protocol - * steps as well as timeouts. + * This is a slightly modified version of the SRP6ServerSession class included with nimbus. The only + * change made for homekit compatability is a change to the size of the b key. Homekit pairing fails + * if b is not 3072 bytes. + * + *

Stateful server-side Secure Remote Password (SRP-6a) authentication session. Handles the + * computing and storing of SRP-6a variables between the protocol steps as well as timeouts. * *

Usage: * *

    - *
  • Create a new SRP-6a server session for each client authentication - * attempt. - *
  • If you wish to use custom routines for the server evidence message - * 'M1' and / or the client evidence message 'M2' specify them at this - * point. - *
  • Proceed to {@link #step1 step one} on receiving a valid user identity - * 'I' from the authenticating client. Respond with the server public - * value 'B' and password salt 's'. If the SRP-6a crypto parameters 'N', - * 'g' and 'H' were not agreed in advance between server and client - * append them to the response. - *
  • Proceed to {@link #step2 step two} on receiving the public client - * value 'A' and evidence message 'M1'. If the client credentials are - * valid signal success and return the server evidence message 'M2'. The - * established session key 'S' may be {@link #getSessionKey retrieved} to - * encrypt further communication with the client. Else signal an - * authentication failure to the client. + *
  • Create a new SRP-6a server session for each client authentication attempt. + *
  • If you wish to use custom routines for the server evidence message 'M1' and / or the client + * evidence message 'M2' specify them at this point. + *
  • Proceed to {@link #step1 step one} on receiving a valid user identity 'I' from the + * authenticating client. Respond with the server public value 'B' and password salt 's'. If + * the SRP-6a crypto parameters 'N', 'g' and 'H' were not agreed in advance between server and + * client append them to the response. + *
  • Proceed to {@link #step2 step two} on receiving the public client value 'A' and evidence + * message 'M1'. If the client credentials are valid signal success and return the server + * evidence message 'M2'. The established session key 'S' may be {@link #getSessionKey + * retrieved} to encrypt further communication with the client. Else signal an authentication + * failure to the client. *
* * @author Vladimir Dzhuvinov */ public class HomekitSRP6ServerSession extends SRP6Session { - - - /** - * Enumerates the states of a server-side SRP-6a authentication session. - */ - public static enum State { - - - /** - * The session is initialised and ready to begin authentication, - * by proceeding to {@link #STEP_1}. - */ - INIT, - - - /** - * The user identity 'I' is received from the client and the - * server has returned its public value 'B' based on the - * matching password verifier 'v'. The session is ready to - * proceed to {@link #STEP_2}. - */ - STEP_1, - - - /** - * The client public key 'A' and evidence message 'M1' are - * received and the server has replied with its own evidence - * message 'M2'. The session is finished (authentication was - * successful or failed). - */ - STEP_2 - } - - - /** - * Indicates a non-existing use identity and implies mock salt 's' and - * verifier 'v' values. - */ - private boolean noSuchUserIdentity = false; - - - /** - * The password verifier 'v'. - */ - private BigInteger v = null; - - - /** - * The server private value 'b'. - */ - private BigInteger b = null; - - - /** - * The current SRP-6a auth state. - */ - private State state; - - - /** - * Creates a new server-side SRP-6a authentication session and sets its - * state to {@link State#INIT}. - * - * @param config The SRP-6a crypto parameters configuration. Must not - * be {@code null}. - * @param timeout The SRP-6a authentication session timeout in seconds. - * If the authenticating counterparty (server or client) - * fails to respond within the specified time the session - * will be closed. If zero timeouts are disabled. - */ - public HomekitSRP6ServerSession(final SRP6CryptoParams config, final int timeout) { - - super(timeout); - - if (config == null) - throw new IllegalArgumentException("The SRP-6a crypto parameters must not be null"); - - this.config = config; - - digest = config.getMessageDigestInstance(); - - if (digest == null) - throw new IllegalArgumentException("Unsupported hash algorithm 'H': " + config.H); - - state = State.INIT; - - updateLastActivityTime(); - } - - - /** - * Creates a new server-side SRP-6a authentication session and sets its - * state to {@link State#INIT}. Session timeouts are disabled. - * - * @param config The SRP-6a crypto parameters configuration. Must not - * be {@code null}. - */ - public HomekitSRP6ServerSession(final SRP6CryptoParams config) { - - this(config, 0); - } - - - /** - * Increments this SRP-6a authentication session to - * {@link State#STEP_1}. - * - *

Argument origin: - * - *

    - *
  • From client: user identity 'I'. - *
  • From server database: matching salt 's' and password verifier - * 'v' values. - *
- * - * @param userID The identity 'I' of the authenticating user. Must not - * be {@code null} or empty. - * @param s The password salt 's'. Must not be {@code null}. - * @param v The password verifier 'v'. Must not be {@code null}. - * - * @return The server public value 'B'. - * - * @throws IllegalStateException If the mehod is invoked in a state - * other than {@link State#INIT}. - */ - public BigInteger step1(final String userID, final BigInteger s, final BigInteger v) { - - // Check arguments - - if (userID == null || userID.trim().isEmpty()) - throw new IllegalArgumentException("The user identity 'I' must not be null or empty"); - - this.userID = userID; - - - if (s == null) - throw new IllegalArgumentException("The salt 's' must not be null"); - - this.s = s; - - - if (v == null) - throw new IllegalArgumentException("The verifier 'v' must not be null"); - - this.v = v; - - - // Check current state - if (state != State.INIT) - throw new IllegalStateException("State violation: Session must be in INIT state"); - - // Generate server private and public values - k = SRP6Routines.computeK(digest, config.N, config.g); - digest.reset(); - - b = HomekitSRP6Routines.generatePrivateValue(config.N, random); - digest.reset(); - - B = SRP6Routines.computePublicServerValue(config.N, config.g, k, v, b); - - state = State.STEP_1; - - updateLastActivityTime(); - - return B; - } - - - /** - * Increments this SRP-6a authentication session to - * {@link State#STEP_1} indicating a non-existing user identity 'I' - * with mock (simulated) salt 's' and password verifier 'v' values. - * - *

This method can be used to avoid informing the client at step one - * that the user identity is bad and throw instead a guaranteed general - * "bad credentials" SRP-6a exception at step two. - * - *

Argument origin: - * - *

    - *
  • From client: user identity 'I'. - *
  • Simulated by server, preferably consistently for the - * specified identity 'I': salt 's' and password verifier 'v' - * values. - *
- * - * @param userID The identity 'I' of the authenticating user. Must not - * be {@code null} or empty. - * @param s The password salt 's'. Must not be {@code null}. - * @param v The password verifier 'v'. Must not be {@code null}. - * - * @return The server public value 'B'. - * - * @throws IllegalStateException If the method is invoked in a state - * other than {@link State#INIT}. - */ - public BigInteger mockStep1(final String userID, final BigInteger s, final BigInteger v) { - - noSuchUserIdentity = true; - - return step1(userID, s, v); - } - - - /** - * Increments this SRP-6a authentication session to - * {@link State#STEP_2}. - * - *

Argument origin: - * - *

    - *
  • From client: public value 'A' and evidence message 'M1'. - *
- * - * @param A The client public value. Must not be {@code null}. - * @param M1 The client evidence message. Must not be {@code null}. - * - * @return The server evidence message 'M2'. - * - * @throws SRP6Exception If the session has timed out, the client public - * value 'A' is invalid or the user credentials - * are invalid. - * - * @throws IllegalStateException If the method is invoked in a state - * other than {@link State#STEP_1}. - */ - public BigInteger step2(final BigInteger A, final BigInteger M1) - throws SRP6Exception { - - // Check arguments - - if (A == null) - throw new IllegalArgumentException("The client public value 'A' must not be null"); - - this.A = A; - - if (M1 == null) - throw new IllegalArgumentException("The client evidence message 'M1' must not be null"); - - this.M1 = M1; - - // Check current state - if (state != State.STEP_1) - throw new IllegalStateException("State violation: Session must be in STEP_1 state"); - - // Check timeout - if (hasTimedOut()) - throw new SRP6Exception("Session timeout", SRP6Exception.CauseType.TIMEOUT); - - // Check A validity - if (! SRP6Routines.isValidPublicValue(config.N, A)) - throw new SRP6Exception("Bad client public value 'A'", SRP6Exception.CauseType.BAD_PUBLIC_VALUE); - - // Check for previous mock step 1 - if (noSuchUserIdentity) - throw new SRP6Exception("Bad client credentials", SRP6Exception.CauseType.BAD_CREDENTIALS); - - if (hashedKeysRoutine != null) { - URoutineContext hashedKeysContext = new URoutineContext(A, B); - u = hashedKeysRoutine.computeU(config, hashedKeysContext); - } else { - u = SRP6Routines.computeU(digest, config.N, A, B); - digest.reset(); - } - - S = SRP6Routines.computeSessionKey(config.N, v, u, A, b); - - // Compute the own client evidence message 'M1' - BigInteger computedM1; - - if (clientEvidenceRoutine != null) { - - // With custom routine - SRP6ClientEvidenceContext ctx = new SRP6ClientEvidenceContext(userID, s, A, B, S); - computedM1 = clientEvidenceRoutine.computeClientEvidence(config, ctx); - } - else { - // With default routine - computedM1 = SRP6Routines.computeClientEvidence(digest, A, B, S); - digest.reset(); - } - - if (! computedM1.equals(M1)) - throw new SRP6Exception("Bad client credentials", SRP6Exception.CauseType.BAD_CREDENTIALS); - - state = State.STEP_2; - - - if (serverEvidenceRoutine != null) { - - // With custom routine - SRP6ServerEvidenceContext ctx = new SRP6ServerEvidenceContext(A, M1, S); - - M2 = serverEvidenceRoutine.computeServerEvidence(config, ctx); - } - - updateLastActivityTime(); - - return M2; - } - - - /** - * Returns the current state of this SRP-6a authentication session. - * - * @return The current state. - */ - public State getState() { - - return state; - } + + /** Enumerates the states of a server-side SRP-6a authentication session. */ + public static enum State { + + /** + * The session is initialised and ready to begin authentication, by proceeding to {@link + * #STEP_1}. + */ + INIT, + + /** + * The user identity 'I' is received from the client and the server has returned its public + * value 'B' based on the matching password verifier 'v'. The session is ready to proceed to + * {@link #STEP_2}. + */ + STEP_1, + + /** + * The client public key 'A' and evidence message 'M1' are received and the server has replied + * with its own evidence message 'M2'. The session is finished (authentication was successful or + * failed). + */ + STEP_2 + } + + /** Indicates a non-existing use identity and implies mock salt 's' and verifier 'v' values. */ + private boolean noSuchUserIdentity = false; + + /** The password verifier 'v'. */ + private BigInteger v = null; + + /** The server private value 'b'. */ + private BigInteger b = null; + + /** The current SRP-6a auth state. */ + private State state; + + /** + * Creates a new server-side SRP-6a authentication session and sets its state to {@link + * State#INIT}. + * + * @param config The SRP-6a crypto parameters configuration. Must not be {@code null}. + * @param timeout The SRP-6a authentication session timeout in seconds. If the authenticating + * counterparty (server or client) fails to respond within the specified time the session will + * be closed. If zero timeouts are disabled. + */ + public HomekitSRP6ServerSession(final SRP6CryptoParams config, final int timeout) { + + super(timeout); + + if (config == null) + throw new IllegalArgumentException("The SRP-6a crypto parameters must not be null"); + + this.config = config; + + digest = config.getMessageDigestInstance(); + + if (digest == null) + throw new IllegalArgumentException("Unsupported hash algorithm 'H': " + config.H); + + state = State.INIT; + + updateLastActivityTime(); + } + + /** + * Creates a new server-side SRP-6a authentication session and sets its state to {@link + * State#INIT}. Session timeouts are disabled. + * + * @param config The SRP-6a crypto parameters configuration. Must not be {@code null}. + */ + public HomekitSRP6ServerSession(final SRP6CryptoParams config) { + + this(config, 0); + } + + /** + * Increments this SRP-6a authentication session to {@link State#STEP_1}. + * + *

Argument origin: + * + *

    + *
  • From client: user identity 'I'. + *
  • From server database: matching salt 's' and password verifier 'v' values. + *
+ * + * @param userID The identity 'I' of the authenticating user. Must not be {@code null} or empty. + * @param s The password salt 's'. Must not be {@code null}. + * @param v The password verifier 'v'. Must not be {@code null}. + * @return The server public value 'B'. + * @throws IllegalStateException If the mehod is invoked in a state other than {@link State#INIT}. + */ + public BigInteger step1(final String userID, final BigInteger s, final BigInteger v) { + + // Check arguments + + if (userID == null || userID.trim().isEmpty()) + throw new IllegalArgumentException("The user identity 'I' must not be null or empty"); + + this.userID = userID; + + if (s == null) throw new IllegalArgumentException("The salt 's' must not be null"); + + this.s = s; + + if (v == null) throw new IllegalArgumentException("The verifier 'v' must not be null"); + + this.v = v; + + // Check current state + if (state != State.INIT) + throw new IllegalStateException("State violation: Session must be in INIT state"); + + // Generate server private and public values + k = SRP6Routines.computeK(digest, config.N, config.g); + digest.reset(); + + b = HomekitSRP6Routines.generatePrivateValue(config.N, random); + digest.reset(); + + B = SRP6Routines.computePublicServerValue(config.N, config.g, k, v, b); + + state = State.STEP_1; + + updateLastActivityTime(); + + return B; + } + + /** + * Increments this SRP-6a authentication session to {@link State#STEP_1} indicating a non-existing + * user identity 'I' with mock (simulated) salt 's' and password verifier 'v' values. + * + *

This method can be used to avoid informing the client at step one that the user identity is + * bad and throw instead a guaranteed general "bad credentials" SRP-6a exception at step two. + * + *

Argument origin: + * + *

    + *
  • From client: user identity 'I'. + *
  • Simulated by server, preferably consistently for the specified identity 'I': salt 's' and + * password verifier 'v' values. + *
+ * + * @param userID The identity 'I' of the authenticating user. Must not be {@code null} or empty. + * @param s The password salt 's'. Must not be {@code null}. + * @param v The password verifier 'v'. Must not be {@code null}. + * @return The server public value 'B'. + * @throws IllegalStateException If the method is invoked in a state other than {@link + * State#INIT}. + */ + public BigInteger mockStep1(final String userID, final BigInteger s, final BigInteger v) { + + noSuchUserIdentity = true; + + return step1(userID, s, v); + } + + /** + * Increments this SRP-6a authentication session to {@link State#STEP_2}. + * + *

Argument origin: + * + *

    + *
  • From client: public value 'A' and evidence message 'M1'. + *
+ * + * @param A The client public value. Must not be {@code null}. + * @param M1 The client evidence message. Must not be {@code null}. + * @return The server evidence message 'M2'. + * @throws SRP6Exception If the session has timed out, the client public value 'A' is invalid or + * the user credentials are invalid. + * @throws IllegalStateException If the method is invoked in a state other than {@link + * State#STEP_1}. + */ + public BigInteger step2(final BigInteger A, final BigInteger M1) throws SRP6Exception { + + // Check arguments + + if (A == null) + throw new IllegalArgumentException("The client public value 'A' must not be null"); + + this.A = A; + + if (M1 == null) + throw new IllegalArgumentException("The client evidence message 'M1' must not be null"); + + this.M1 = M1; + + // Check current state + if (state != State.STEP_1) + throw new IllegalStateException("State violation: Session must be in STEP_1 state"); + + // Check timeout + if (hasTimedOut()) throw new SRP6Exception("Session timeout", SRP6Exception.CauseType.TIMEOUT); + + // Check A validity + if (!SRP6Routines.isValidPublicValue(config.N, A)) + throw new SRP6Exception( + "Bad client public value 'A'", SRP6Exception.CauseType.BAD_PUBLIC_VALUE); + + // Check for previous mock step 1 + if (noSuchUserIdentity) + throw new SRP6Exception("Bad client credentials", SRP6Exception.CauseType.BAD_CREDENTIALS); + + if (hashedKeysRoutine != null) { + URoutineContext hashedKeysContext = new URoutineContext(A, B); + u = hashedKeysRoutine.computeU(config, hashedKeysContext); + } else { + u = SRP6Routines.computeU(digest, config.N, A, B); + digest.reset(); + } + + S = SRP6Routines.computeSessionKey(config.N, v, u, A, b); + + // Compute the own client evidence message 'M1' + BigInteger computedM1; + + if (clientEvidenceRoutine != null) { + + // With custom routine + SRP6ClientEvidenceContext ctx = new SRP6ClientEvidenceContext(userID, s, A, B, S); + computedM1 = clientEvidenceRoutine.computeClientEvidence(config, ctx); + } else { + // With default routine + computedM1 = SRP6Routines.computeClientEvidence(digest, A, B, S); + digest.reset(); + } + + if (!computedM1.equals(M1)) + throw new SRP6Exception("Bad client credentials", SRP6Exception.CauseType.BAD_CREDENTIALS); + + state = State.STEP_2; + + if (serverEvidenceRoutine != null) { + + // With custom routine + SRP6ServerEvidenceContext ctx = new SRP6ServerEvidenceContext(A, M1, S); + + M2 = serverEvidenceRoutine.computeServerEvidence(config, ctx); + } + + updateLastActivityTime(); + + return M2; + } + + /** + * Returns the current state of this SRP-6a authentication session. + * + * @return The current state. + */ + public State getState() { + + return state; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/pairing/MessageType.java b/src/main/java/com/beowulfe/hap/impl/pairing/MessageType.java index 5aec3f00c..b491e8c5a 100644 --- a/src/main/java/com/beowulfe/hap/impl/pairing/MessageType.java +++ b/src/main/java/com/beowulfe/hap/impl/pairing/MessageType.java @@ -1,29 +1,27 @@ package com.beowulfe.hap.impl.pairing; public enum MessageType { + METHOD(0), + USERNAME(1), + SALT(2), + PUBLIC_KEY(3), + PROOF(4), + ENCRYPTED_DATA(5), + STATE(6), + ERROR(7), + SIGNATURE(10); - METHOD(0), - USERNAME(1), - SALT(2), - PUBLIC_KEY(3), - PROOF(4), - ENCRYPTED_DATA(5), - STATE(6), - ERROR(7), - SIGNATURE(10) - ; - - private final short key; - - MessageType(short key) { - this.key = key; - } - - MessageType(int key) { - this.key = (short) key; - } - - public short getKey() { - return key; - } + private final short key; + + MessageType(short key) { + this.key = key; + } + + MessageType(int key) { + this.key = (short) key; + } + + public short getKey() { + return key; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/pairing/PairSetupRequest.java b/src/main/java/com/beowulfe/hap/impl/pairing/PairSetupRequest.java index c7877f04e..2e31b10f6 100644 --- a/src/main/java/com/beowulfe/hap/impl/pairing/PairSetupRequest.java +++ b/src/main/java/com/beowulfe/hap/impl/pairing/PairSetupRequest.java @@ -1,92 +1,88 @@ package com.beowulfe.hap.impl.pairing; -import java.math.BigInteger; - import com.beowulfe.hap.impl.pairing.TypeLengthValueUtils.DecodeResult; +import java.math.BigInteger; abstract class PairSetupRequest { - - private final static short VALUE_STAGE_1 = 1; - private final static short VALUE_STAGE_2 = 3; - private final static short VALUE_STAGE_3 = 5; - - public static PairSetupRequest of(byte[] content) throws Exception { - DecodeResult d = TypeLengthValueUtils.decode(content); - short stage = d.getByte(MessageType.STATE); - switch(stage) { - case VALUE_STAGE_1: - return new Stage1Request(); - - case VALUE_STAGE_2: - return new Stage2Request(d); - - case VALUE_STAGE_3: - return new Stage3Request(d); - - default: - throw new Exception("Unknown pair process stage: "+stage); - } - } - - public abstract Stage getStage(); - - public static class Stage1Request extends PairSetupRequest { - @Override - public Stage getStage() { - return Stage.ONE; - } - } - - public static class Stage2Request extends PairSetupRequest { - - private final BigInteger a; - private final BigInteger m1; - - public Stage2Request(DecodeResult d) { - a = d.getBigInt(MessageType.PUBLIC_KEY); - m1 = d.getBigInt(MessageType.PROOF); - } - - public BigInteger getA() { - return a; - } - - public BigInteger getM1() { - return m1; - } - - @Override - public Stage getStage() { - return Stage.TWO; - } - - } - - static class Stage3Request extends PairSetupRequest { - - private final byte[] messageData; - private final byte[] authTagData; - - public Stage3Request(DecodeResult d) { - messageData = new byte[d.getLength(MessageType.ENCRYPTED_DATA) - 16]; - authTagData = new byte[16]; - d.getBytes(MessageType.ENCRYPTED_DATA, messageData, 0); - d.getBytes(MessageType.ENCRYPTED_DATA, authTagData, messageData.length); - } - - public byte[] getMessageData() { - return messageData; - } - - public byte[] getAuthTagData() { - return authTagData; - } - - @Override - public Stage getStage() { - return Stage.THREE; - } - - } + private static final short VALUE_STAGE_1 = 1; + private static final short VALUE_STAGE_2 = 3; + private static final short VALUE_STAGE_3 = 5; + + public static PairSetupRequest of(byte[] content) throws Exception { + DecodeResult d = TypeLengthValueUtils.decode(content); + short stage = d.getByte(MessageType.STATE); + switch (stage) { + case VALUE_STAGE_1: + return new Stage1Request(); + + case VALUE_STAGE_2: + return new Stage2Request(d); + + case VALUE_STAGE_3: + return new Stage3Request(d); + + default: + throw new Exception("Unknown pair process stage: " + stage); + } + } + + public abstract Stage getStage(); + + public static class Stage1Request extends PairSetupRequest { + @Override + public Stage getStage() { + return Stage.ONE; + } + } + + public static class Stage2Request extends PairSetupRequest { + + private final BigInteger a; + private final BigInteger m1; + + public Stage2Request(DecodeResult d) { + a = d.getBigInt(MessageType.PUBLIC_KEY); + m1 = d.getBigInt(MessageType.PROOF); + } + + public BigInteger getA() { + return a; + } + + public BigInteger getM1() { + return m1; + } + + @Override + public Stage getStage() { + return Stage.TWO; + } + } + + static class Stage3Request extends PairSetupRequest { + + private final byte[] messageData; + private final byte[] authTagData; + + public Stage3Request(DecodeResult d) { + messageData = new byte[d.getLength(MessageType.ENCRYPTED_DATA) - 16]; + authTagData = new byte[16]; + d.getBytes(MessageType.ENCRYPTED_DATA, messageData, 0); + d.getBytes(MessageType.ENCRYPTED_DATA, authTagData, messageData.length); + } + + public byte[] getMessageData() { + return messageData; + } + + public byte[] getAuthTagData() { + return authTagData; + } + + @Override + public Stage getStage() { + return Stage.THREE; + } + } } diff --git a/src/main/java/com/beowulfe/hap/impl/pairing/PairVerificationManager.java b/src/main/java/com/beowulfe/hap/impl/pairing/PairVerificationManager.java index 5a035bf75..57ebf22f3 100644 --- a/src/main/java/com/beowulfe/hap/impl/pairing/PairVerificationManager.java +++ b/src/main/java/com/beowulfe/hap/impl/pairing/PairVerificationManager.java @@ -1,14 +1,5 @@ package com.beowulfe.hap.impl.pairing; -import java.nio.charset.StandardCharsets; -import java.security.SecureRandom; - -import org.bouncycastle.crypto.digests.SHA512Digest; -import org.bouncycastle.crypto.generators.HKDFBytesGenerator; -import org.bouncycastle.crypto.params.HKDFParameters; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.beowulfe.hap.HomekitAuthInfo; import com.beowulfe.hap.impl.HomekitRegistry; import com.beowulfe.hap.impl.crypto.*; @@ -20,124 +11,140 @@ import com.beowulfe.hap.impl.pairing.TypeLengthValueUtils.Encoder; import com.beowulfe.hap.impl.responses.NotFoundResponse; import com.beowulfe.hap.impl.responses.OkResponse; - import djb.Curve25519; +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; +import org.bouncycastle.crypto.digests.SHA512Digest; +import org.bouncycastle.crypto.generators.HKDFBytesGenerator; +import org.bouncycastle.crypto.params.HKDFParameters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class PairVerificationManager { - - private final static Logger logger = LoggerFactory.getLogger(PairVerificationManager.class); - private static volatile SecureRandom secureRandom; - - private final HomekitAuthInfo authInfo; - private final HomekitRegistry registry; - - private byte[] hkdfKey; - private byte[] clientPublicKey; - private byte[] publicKey; - private byte[] sharedSecret; - - public PairVerificationManager(HomekitAuthInfo authInfo, HomekitRegistry registry) { - this.authInfo = authInfo; - this.registry = registry; - } - - public HttpResponse handle(HttpRequest rawRequest) throws Exception { - PairVerificationRequest request = PairVerificationRequest.of(rawRequest.getBody()); - switch(request.getStage()) { - case ONE: - return stage1((Stage1Request) request); - - case TWO: - return stage2((Stage2Request) request); - - default: - return new NotFoundResponse(); - } - } - - private HttpResponse stage1(Stage1Request request) throws Exception { - logger.debug("Starting pair verification for "+registry.getLabel()); - clientPublicKey = request.getClientPublicKey(); - publicKey = new byte[32]; - byte[] privateKey = new byte[32]; - getSecureRandom().nextBytes(privateKey); - Curve25519.keygen(publicKey, null, privateKey); - - sharedSecret = new byte[32]; - Curve25519.curve(sharedSecret, privateKey, clientPublicKey); - - byte[] material = ByteUtils.joinBytes(publicKey, authInfo.getMac().getBytes(StandardCharsets.UTF_8), - clientPublicKey); - - byte[] proof = new EdsaSigner(authInfo.getPrivateKey()).sign(material); - - HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA512Digest()); - hkdf.init(new HKDFParameters(sharedSecret, "Pair-Verify-Encrypt-Salt".getBytes(StandardCharsets.UTF_8), - "Pair-Verify-Encrypt-Info".getBytes(StandardCharsets.UTF_8))); - hkdfKey = new byte[32]; - hkdf.generateBytes(hkdfKey, 0, 32); - - Encoder encoder = TypeLengthValueUtils.getEncoder(); - encoder.add(MessageType.USERNAME, authInfo.getMac().getBytes(StandardCharsets.UTF_8)); - encoder.add(MessageType.SIGNATURE, proof); - byte[] plaintext = encoder.toByteArray(); - - ChachaEncoder chacha = new ChachaEncoder(hkdfKey, "PV-Msg02".getBytes(StandardCharsets.UTF_8)); - byte[] ciphertext = chacha.encodeCiphertext(plaintext); - - encoder = TypeLengthValueUtils.getEncoder(); - encoder.add(MessageType.STATE, (short) 2); - encoder.add(MessageType.ENCRYPTED_DATA, ciphertext); - encoder.add(MessageType.PUBLIC_KEY, publicKey); - return new PairingResponse(encoder.toByteArray()); - } - - private HttpResponse stage2(Stage2Request request) throws Exception { - ChachaDecoder chacha = new ChachaDecoder(hkdfKey, "PV-Msg03".getBytes(StandardCharsets.UTF_8)); - byte[] plaintext = chacha.decodeCiphertext(request.getAuthTagData(), request.getMessageData()); - - DecodeResult d = TypeLengthValueUtils.decode(plaintext); - byte[] clientUsername = d.getBytes(MessageType.USERNAME); - byte[] clientSignature = d.getBytes(MessageType.SIGNATURE); - - byte[] material = ByteUtils.joinBytes(clientPublicKey, clientUsername, publicKey); - - byte[] clientLtpk = authInfo.getUserPublicKey(authInfo.getMac()+new String(clientUsername, StandardCharsets.UTF_8)); - if (clientLtpk == null) { - throw new Exception("Unknown user: "+new String(clientUsername, StandardCharsets.UTF_8)); - } - - Encoder encoder = TypeLengthValueUtils.getEncoder(); - if (new EdsaVerifier(clientLtpk).verify(material, clientSignature)) { - encoder.add(MessageType.STATE, (short) 4); - logger.debug("Completed pair verification for "+registry.getLabel()); - return new UpgradeResponse(encoder.toByteArray(), createKey("Control-Write-Encryption-Key"), - createKey("Control-Read-Encryption-Key")); - } else { - encoder.add(MessageType.ERROR, (short) 4); - logger.warn("Invalid signature. Could not pair "+registry.getLabel()); - return new OkResponse(encoder.toByteArray()); - } - } - - private byte[] createKey(String info) { - HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA512Digest()); - hkdf.init(new HKDFParameters(sharedSecret, "Control-Salt".getBytes(StandardCharsets.UTF_8), - info.getBytes(StandardCharsets.UTF_8))); - byte[] key = new byte[32]; - hkdf.generateBytes(key, 0, 32); - return key; - } - - private static SecureRandom getSecureRandom() { - if (secureRandom == null) { - synchronized(PairVerificationManager.class) { - if (secureRandom == null) { - secureRandom = new SecureRandom(); - } - } - } - return secureRandom; - } + private static final Logger logger = LoggerFactory.getLogger(PairVerificationManager.class); + private static volatile SecureRandom secureRandom; + + private final HomekitAuthInfo authInfo; + private final HomekitRegistry registry; + + private byte[] hkdfKey; + private byte[] clientPublicKey; + private byte[] publicKey; + private byte[] sharedSecret; + + public PairVerificationManager(HomekitAuthInfo authInfo, HomekitRegistry registry) { + this.authInfo = authInfo; + this.registry = registry; + } + + public HttpResponse handle(HttpRequest rawRequest) throws Exception { + PairVerificationRequest request = PairVerificationRequest.of(rawRequest.getBody()); + switch (request.getStage()) { + case ONE: + return stage1((Stage1Request) request); + + case TWO: + return stage2((Stage2Request) request); + + default: + return new NotFoundResponse(); + } + } + + private HttpResponse stage1(Stage1Request request) throws Exception { + logger.debug("Starting pair verification for " + registry.getLabel()); + clientPublicKey = request.getClientPublicKey(); + publicKey = new byte[32]; + byte[] privateKey = new byte[32]; + getSecureRandom().nextBytes(privateKey); + Curve25519.keygen(publicKey, null, privateKey); + + sharedSecret = new byte[32]; + Curve25519.curve(sharedSecret, privateKey, clientPublicKey); + + byte[] material = + ByteUtils.joinBytes( + publicKey, authInfo.getMac().getBytes(StandardCharsets.UTF_8), clientPublicKey); + + byte[] proof = new EdsaSigner(authInfo.getPrivateKey()).sign(material); + + HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA512Digest()); + hkdf.init( + new HKDFParameters( + sharedSecret, + "Pair-Verify-Encrypt-Salt".getBytes(StandardCharsets.UTF_8), + "Pair-Verify-Encrypt-Info".getBytes(StandardCharsets.UTF_8))); + hkdfKey = new byte[32]; + hkdf.generateBytes(hkdfKey, 0, 32); + + Encoder encoder = TypeLengthValueUtils.getEncoder(); + encoder.add(MessageType.USERNAME, authInfo.getMac().getBytes(StandardCharsets.UTF_8)); + encoder.add(MessageType.SIGNATURE, proof); + byte[] plaintext = encoder.toByteArray(); + + ChachaEncoder chacha = new ChachaEncoder(hkdfKey, "PV-Msg02".getBytes(StandardCharsets.UTF_8)); + byte[] ciphertext = chacha.encodeCiphertext(plaintext); + + encoder = TypeLengthValueUtils.getEncoder(); + encoder.add(MessageType.STATE, (short) 2); + encoder.add(MessageType.ENCRYPTED_DATA, ciphertext); + encoder.add(MessageType.PUBLIC_KEY, publicKey); + return new PairingResponse(encoder.toByteArray()); + } + + private HttpResponse stage2(Stage2Request request) throws Exception { + ChachaDecoder chacha = new ChachaDecoder(hkdfKey, "PV-Msg03".getBytes(StandardCharsets.UTF_8)); + byte[] plaintext = chacha.decodeCiphertext(request.getAuthTagData(), request.getMessageData()); + + DecodeResult d = TypeLengthValueUtils.decode(plaintext); + byte[] clientUsername = d.getBytes(MessageType.USERNAME); + byte[] clientSignature = d.getBytes(MessageType.SIGNATURE); + + byte[] material = ByteUtils.joinBytes(clientPublicKey, clientUsername, publicKey); + + byte[] clientLtpk = + authInfo.getUserPublicKey( + authInfo.getMac() + new String(clientUsername, StandardCharsets.UTF_8)); + if (clientLtpk == null) { + throw new Exception("Unknown user: " + new String(clientUsername, StandardCharsets.UTF_8)); + } + + Encoder encoder = TypeLengthValueUtils.getEncoder(); + if (new EdsaVerifier(clientLtpk).verify(material, clientSignature)) { + encoder.add(MessageType.STATE, (short) 4); + logger.debug("Completed pair verification for " + registry.getLabel()); + return new UpgradeResponse( + encoder.toByteArray(), + createKey("Control-Write-Encryption-Key"), + createKey("Control-Read-Encryption-Key")); + } else { + encoder.add(MessageType.ERROR, (short) 4); + logger.warn("Invalid signature. Could not pair " + registry.getLabel()); + return new OkResponse(encoder.toByteArray()); + } + } + + private byte[] createKey(String info) { + HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA512Digest()); + hkdf.init( + new HKDFParameters( + sharedSecret, + "Control-Salt".getBytes(StandardCharsets.UTF_8), + info.getBytes(StandardCharsets.UTF_8))); + byte[] key = new byte[32]; + hkdf.generateBytes(key, 0, 32); + return key; + } + + private static SecureRandom getSecureRandom() { + if (secureRandom == null) { + synchronized (PairVerificationManager.class) { + if (secureRandom == null) { + secureRandom = new SecureRandom(); + } + } + } + return secureRandom; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/pairing/PairVerificationRequest.java b/src/main/java/com/beowulfe/hap/impl/pairing/PairVerificationRequest.java index e9a3eccee..bd35df62b 100644 --- a/src/main/java/com/beowulfe/hap/impl/pairing/PairVerificationRequest.java +++ b/src/main/java/com/beowulfe/hap/impl/pairing/PairVerificationRequest.java @@ -2,73 +2,69 @@ import com.beowulfe.hap.impl.pairing.TypeLengthValueUtils.DecodeResult; - abstract class PairVerificationRequest { - - private final static short VALUE_STAGE_1 = 1; - private final static short VALUE_STAGE_2 = 3; - - static PairVerificationRequest of(byte[] content) throws Exception { - DecodeResult d = TypeLengthValueUtils.decode(content); - short stage = d.getByte(MessageType.STATE); - switch(stage) { - case VALUE_STAGE_1: - return new Stage1Request(d); - - case VALUE_STAGE_2: - return new Stage2Request(d); - - default: - throw new Exception("Unknown pair process stage: "+stage); - } - } - - abstract Stage getStage(); - - static class Stage1Request extends PairVerificationRequest { - - private final byte[] clientPublicKey; - - public Stage1Request(DecodeResult d) { - clientPublicKey = d.getBytes(MessageType.PUBLIC_KEY); - } - - public byte[] getClientPublicKey() { - return clientPublicKey; - } - - @Override - Stage getStage() { - return Stage.ONE; - } - - } - - static class Stage2Request extends PairVerificationRequest { - - private final byte[] messageData; - private final byte[] authTagData; - - public Stage2Request(DecodeResult d) { - messageData = new byte[d.getLength(MessageType.ENCRYPTED_DATA) - 16]; - authTagData = new byte[16]; - d.getBytes(MessageType.ENCRYPTED_DATA, messageData, 0); - d.getBytes(MessageType.ENCRYPTED_DATA, authTagData, messageData.length); - } - - public byte[] getMessageData() { - return messageData; - } - - public byte[] getAuthTagData() { - return authTagData; - } - - @Override - public Stage getStage() { - return Stage.TWO; - } - - } + private static final short VALUE_STAGE_1 = 1; + private static final short VALUE_STAGE_2 = 3; + + static PairVerificationRequest of(byte[] content) throws Exception { + DecodeResult d = TypeLengthValueUtils.decode(content); + short stage = d.getByte(MessageType.STATE); + switch (stage) { + case VALUE_STAGE_1: + return new Stage1Request(d); + + case VALUE_STAGE_2: + return new Stage2Request(d); + + default: + throw new Exception("Unknown pair process stage: " + stage); + } + } + + abstract Stage getStage(); + + static class Stage1Request extends PairVerificationRequest { + + private final byte[] clientPublicKey; + + public Stage1Request(DecodeResult d) { + clientPublicKey = d.getBytes(MessageType.PUBLIC_KEY); + } + + public byte[] getClientPublicKey() { + return clientPublicKey; + } + + @Override + Stage getStage() { + return Stage.ONE; + } + } + + static class Stage2Request extends PairVerificationRequest { + + private final byte[] messageData; + private final byte[] authTagData; + + public Stage2Request(DecodeResult d) { + messageData = new byte[d.getLength(MessageType.ENCRYPTED_DATA) - 16]; + authTagData = new byte[16]; + d.getBytes(MessageType.ENCRYPTED_DATA, messageData, 0); + d.getBytes(MessageType.ENCRYPTED_DATA, authTagData, messageData.length); + } + + public byte[] getMessageData() { + return messageData; + } + + public byte[] getAuthTagData() { + return authTagData; + } + + @Override + public Stage getStage() { + return Stage.TWO; + } + } } diff --git a/src/main/java/com/beowulfe/hap/impl/pairing/PairingManager.java b/src/main/java/com/beowulfe/hap/impl/pairing/PairingManager.java index a4bae90a8..5d775bbc2 100644 --- a/src/main/java/com/beowulfe/hap/impl/pairing/PairingManager.java +++ b/src/main/java/com/beowulfe/hap/impl/pairing/PairingManager.java @@ -1,8 +1,5 @@ package com.beowulfe.hap.impl.pairing; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.beowulfe.hap.HomekitAuthInfo; import com.beowulfe.hap.impl.HomekitRegistry; import com.beowulfe.hap.impl.http.HttpRequest; @@ -10,60 +7,63 @@ import com.beowulfe.hap.impl.jmdns.JmdnsHomekitAdvertiser; import com.beowulfe.hap.impl.responses.NotFoundResponse; import com.beowulfe.hap.impl.responses.UnauthorizedResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class PairingManager { - private final static Logger logger = LoggerFactory.getLogger(PairingManager.class); - - private final HomekitAuthInfo authInfo; - private final HomekitRegistry registry; - private final JmdnsHomekitAdvertiser advertiser; - - private SrpHandler srpHandler; - - public PairingManager(HomekitAuthInfo authInfo, HomekitRegistry registry, JmdnsHomekitAdvertiser advertiser) { - this.authInfo = authInfo; - this.registry = registry; - this.advertiser = advertiser; - } + private static final Logger logger = LoggerFactory.getLogger(PairingManager.class); + + private final HomekitAuthInfo authInfo; + private final HomekitRegistry registry; + private final JmdnsHomekitAdvertiser advertiser; + + private SrpHandler srpHandler; + + public PairingManager( + HomekitAuthInfo authInfo, HomekitRegistry registry, JmdnsHomekitAdvertiser advertiser) { + this.authInfo = authInfo; + this.registry = registry; + this.advertiser = advertiser; + } + + public HttpResponse handle(HttpRequest httpRequest) throws Exception { + PairSetupRequest req = PairSetupRequest.of(httpRequest.getBody()); + + if (req.getStage() == Stage.ONE) { + logger.info("Starting pair for " + registry.getLabel()); + srpHandler = new SrpHandler(authInfo.getPin(), authInfo.getSalt()); + return srpHandler.handle(req); + } else if (req.getStage() == Stage.TWO) { + logger.debug("Entering second stage of pair for " + registry.getLabel()); + if (srpHandler == null) { + logger.warn("Received unexpected stage 2 request for " + registry.getLabel()); + return new UnauthorizedResponse(); + } else { + try { + return srpHandler.handle(req); + } catch (Exception e) { + srpHandler = null; // You don't get to try again - need a new key + logger.error("Exception encountered while processing pairing request", e); + return new UnauthorizedResponse(); + } + } + } else if (req.getStage() == Stage.THREE) { + logger.debug("Entering third stage of pair for " + registry.getLabel()); + if (srpHandler == null) { + logger.warn("Received unexpected stage 3 request for " + registry.getLabel()); + return new UnauthorizedResponse(); + } else { + FinalPairHandler handler = new FinalPairHandler(srpHandler.getK(), authInfo, advertiser); + try { + return handler.handle(req); + } catch (Exception e) { + logger.error("Exception while finalizing pairing", e); + return new UnauthorizedResponse(); + } + } + } - public HttpResponse handle(HttpRequest httpRequest) throws Exception { - PairSetupRequest req = PairSetupRequest.of(httpRequest.getBody()); - - if (req.getStage() == Stage.ONE) { - logger.info("Starting pair for "+registry.getLabel()); - srpHandler = new SrpHandler(authInfo.getPin(), authInfo.getSalt()); - return srpHandler.handle(req); - } else if (req.getStage() == Stage.TWO) { - logger.debug("Entering second stage of pair for "+registry.getLabel()); - if (srpHandler == null) { - logger.warn("Received unexpected stage 2 request for "+registry.getLabel()); - return new UnauthorizedResponse(); - } else { - try { - return srpHandler.handle(req); - } catch (Exception e) { - srpHandler = null; //You don't get to try again - need a new key - logger.error("Exception encountered while processing pairing request", e); - return new UnauthorizedResponse(); - } - } - } else if (req.getStage() == Stage.THREE) { - logger.debug("Entering third stage of pair for "+registry.getLabel()); - if (srpHandler == null) { - logger.warn("Received unexpected stage 3 request for "+registry.getLabel()); - return new UnauthorizedResponse(); - } else { - FinalPairHandler handler = new FinalPairHandler(srpHandler.getK(), authInfo, advertiser); - try { - return handler.handle(req); - } catch (Exception e) { - logger.error("Exception while finalizing pairing", e); - return new UnauthorizedResponse(); - } - } - } - - return new NotFoundResponse(); - } + return new NotFoundResponse(); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/pairing/PairingResponse.java b/src/main/java/com/beowulfe/hap/impl/pairing/PairingResponse.java index 051f8765d..7c80e1721 100644 --- a/src/main/java/com/beowulfe/hap/impl/pairing/PairingResponse.java +++ b/src/main/java/com/beowulfe/hap/impl/pairing/PairingResponse.java @@ -1,29 +1,28 @@ package com.beowulfe.hap.impl.pairing; +import com.beowulfe.hap.impl.responses.OkResponse; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import com.beowulfe.hap.impl.responses.OkResponse; - class PairingResponse extends OkResponse { - - private static final Map headers = Collections.unmodifiableMap( - new HashMap() { - private static final long serialVersionUID = 1L; - { - put("Content-type", "application/pairing+tlv8"); - } - }); - - public PairingResponse(byte[] body) { - super(body); - } - - @Override - public Map getHeaders() { - return headers; - } + private static final Map headers = + Collections.unmodifiableMap( + new HashMap() { + private static final long serialVersionUID = 1L; + + { + put("Content-type", "application/pairing+tlv8"); + } + }); + + public PairingResponse(byte[] body) { + super(body); + } + @Override + public Map getHeaders() { + return headers; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/pairing/PairingUpdateController.java b/src/main/java/com/beowulfe/hap/impl/pairing/PairingUpdateController.java index 22e9dfcf2..43287fffb 100644 --- a/src/main/java/com/beowulfe/hap/impl/pairing/PairingUpdateController.java +++ b/src/main/java/com/beowulfe/hap/impl/pairing/PairingUpdateController.java @@ -1,42 +1,40 @@ package com.beowulfe.hap.impl.pairing; -import java.io.IOException; -import java.nio.charset.StandardCharsets; - import com.beowulfe.hap.HomekitAuthInfo; import com.beowulfe.hap.impl.http.HttpRequest; import com.beowulfe.hap.impl.http.HttpResponse; import com.beowulfe.hap.impl.jmdns.JmdnsHomekitAdvertiser; import com.beowulfe.hap.impl.pairing.TypeLengthValueUtils.DecodeResult; +import java.io.IOException; +import java.nio.charset.StandardCharsets; public class PairingUpdateController { - private final HomekitAuthInfo authInfo; - private final JmdnsHomekitAdvertiser advertiser; - - public PairingUpdateController(HomekitAuthInfo authInfo, JmdnsHomekitAdvertiser advertiser) { - this.authInfo = authInfo; - this.advertiser = advertiser; - } + private final HomekitAuthInfo authInfo; + private final JmdnsHomekitAdvertiser advertiser; + + public PairingUpdateController(HomekitAuthInfo authInfo, JmdnsHomekitAdvertiser advertiser) { + this.authInfo = authInfo; + this.advertiser = advertiser; + } - public HttpResponse handle(HttpRequest request) throws IOException { - DecodeResult d = TypeLengthValueUtils.decode(request.getBody()); - - int method = d.getByte(MessageType.METHOD); - if (method == 3) { //Add pairing - byte[] username = d.getBytes(MessageType.USERNAME); - byte[] ltpk = d.getBytes(MessageType.PUBLIC_KEY); - authInfo.createUser(authInfo.getMac()+new String(username, StandardCharsets.UTF_8), ltpk); - } else if (method == 4) { //Remove pairing - byte[] username = d.getBytes(MessageType.USERNAME); - authInfo.removeUser(authInfo.getMac()+new String(username, StandardCharsets.UTF_8)); - if (!authInfo.hasUser()) { - advertiser.setDiscoverable(true); - } - } else { - throw new RuntimeException("Unrecognized method: "+method); - } - return new PairingResponse(new byte[] {0x06, 0x01, 0x02}); - } + public HttpResponse handle(HttpRequest request) throws IOException { + DecodeResult d = TypeLengthValueUtils.decode(request.getBody()); + int method = d.getByte(MessageType.METHOD); + if (method == 3) { // Add pairing + byte[] username = d.getBytes(MessageType.USERNAME); + byte[] ltpk = d.getBytes(MessageType.PUBLIC_KEY); + authInfo.createUser(authInfo.getMac() + new String(username, StandardCharsets.UTF_8), ltpk); + } else if (method == 4) { // Remove pairing + byte[] username = d.getBytes(MessageType.USERNAME); + authInfo.removeUser(authInfo.getMac() + new String(username, StandardCharsets.UTF_8)); + if (!authInfo.hasUser()) { + advertiser.setDiscoverable(true); + } + } else { + throw new RuntimeException("Unrecognized method: " + method); + } + return new PairingResponse(new byte[] {0x06, 0x01, 0x02}); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/pairing/ServerEvidenceRoutineImpl.java b/src/main/java/com/beowulfe/hap/impl/pairing/ServerEvidenceRoutineImpl.java index 1140671f4..3eb1290cf 100644 --- a/src/main/java/com/beowulfe/hap/impl/pairing/ServerEvidenceRoutineImpl.java +++ b/src/main/java/com/beowulfe/hap/impl/pairing/ServerEvidenceRoutineImpl.java @@ -1,32 +1,31 @@ package com.beowulfe.hap.impl.pairing; -import java.math.BigInteger; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - import com.nimbusds.srp6.SRP6CryptoParams; import com.nimbusds.srp6.SRP6ServerEvidenceContext; import com.nimbusds.srp6.ServerEvidenceRoutine; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; class ServerEvidenceRoutineImpl implements ServerEvidenceRoutine { - @Override - public BigInteger computeServerEvidence(SRP6CryptoParams cryptoParams, - SRP6ServerEvidenceContext ctx) { - - MessageDigest digest; - try { - digest = MessageDigest.getInstance(cryptoParams.H); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("Could not locate requested algorithm", e); - } - - byte[] hS = digest.digest(SrpHandler.bigIntegerToUnsignedByteArray(ctx.S)); - - digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.A)); - digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.M1)); - digest.update(hS); - - return new BigInteger(1, digest.digest()); - } + @Override + public BigInteger computeServerEvidence( + SRP6CryptoParams cryptoParams, SRP6ServerEvidenceContext ctx) { + + MessageDigest digest; + try { + digest = MessageDigest.getInstance(cryptoParams.H); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Could not locate requested algorithm", e); + } + + byte[] hS = digest.digest(SrpHandler.bigIntegerToUnsignedByteArray(ctx.S)); + + digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.A)); + digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.M1)); + digest.update(hS); + + return new BigInteger(1, digest.digest()); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/pairing/SrpHandler.java b/src/main/java/com/beowulfe/hap/impl/pairing/SrpHandler.java index 5274ff784..d95cab112 100644 --- a/src/main/java/com/beowulfe/hap/impl/pairing/SrpHandler.java +++ b/src/main/java/com/beowulfe/hap/impl/pairing/SrpHandler.java @@ -1,12 +1,5 @@ package com.beowulfe.hap.impl.pairing; -import java.math.BigInteger; -import java.security.MessageDigest; -import java.util.Arrays; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.beowulfe.hap.impl.http.HttpResponse; import com.beowulfe.hap.impl.pairing.HomekitSRP6ServerSession.State; import com.beowulfe.hap.impl.pairing.PairSetupRequest.Stage2Request; @@ -14,85 +7,91 @@ import com.beowulfe.hap.impl.responses.ConflictResponse; import com.beowulfe.hap.impl.responses.NotFoundResponse; import com.nimbusds.srp6.*; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.util.Arrays; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; class SrpHandler { - // Precomputed safe 3072 bit prime 'N'. Origin RFC 5054, appendix A. - private final static BigInteger N_3072 = new BigInteger("5809605995369958062791915965639201402176612226902900533702900882779736177890990861472094774477339581147373410185646378328043729800750470098210924487866935059164371588168047540943981644516632755067501626434556398193186628990071248660819361205119793693985433297036118232914410171876807536457391277857011849897410207519105333355801121109356897459426271845471397952675959440793493071628394122780510124618488232602464649876850458861245784240929258426287699705312584509625419513463605155428017165714465363094021609290561084025893662561222573202082865797821865270991145082200656978177192827024538990239969175546190770645685893438011714430426409338676314743571154537142031573004276428701433036381801705308659830751190352946025482059931306571004727362479688415574702596946457770284148435989129632853918392117997472632693078113129886487399347796982772784615865232621289656944284216824611318709764535152507354116344703769998514148343807"); - private final static BigInteger G = BigInteger.valueOf(5); - private final static String IDENTIFIER = "Pair-Setup"; - - private final static Logger logger = LoggerFactory.getLogger(SrpHandler.class); - - private final BigInteger salt; - private final HomekitSRP6ServerSession session; - private final SRP6CryptoParams config; - private final String pin; - - public SrpHandler(String pin, BigInteger salt) { - config = new SRP6CryptoParams(N_3072, G, "SHA-512"); - session = new HomekitSRP6ServerSession(config); - session.setClientEvidenceRoutine(new ClientEvidenceRoutineImpl()); - session.setServerEvidenceRoutine(new ServerEvidenceRoutineImpl()); - this.pin = pin; - this.salt = salt; - } - - public HttpResponse handle(PairSetupRequest request) throws Exception { - switch(request.getStage()) { - case ONE: - return step1(); - - case TWO: - return step2((Stage2Request) request); - - default: - return new NotFoundResponse(); - } - } - - private HttpResponse step1() throws Exception { - if (session.getState() != State.INIT) { - logger.error("Session is not in state INIT when receiving step1"); - return new ConflictResponse(); - } - - SRP6VerifierGenerator verifierGenerator = new SRP6VerifierGenerator(config); - verifierGenerator.setXRoutine(new XRoutineWithUserIdentity()); - BigInteger verifier = verifierGenerator.generateVerifier(salt, IDENTIFIER, pin); - - Encoder encoder = TypeLengthValueUtils.getEncoder(); - encoder.add(MessageType.STATE, (short) 0x02); - encoder.add(MessageType.SALT, salt); - encoder.add(MessageType.PUBLIC_KEY, session.step1(IDENTIFIER, salt, verifier)); - return new PairingResponse(encoder.toByteArray()); - } - - private HttpResponse step2(Stage2Request request) throws Exception { - if (session.getState() != State.STEP_1) { - logger.error("Session is not in state Stage 1 when receiving step2"); - return new ConflictResponse(); - } - BigInteger m2 = session.step2(request.getA(), request.getM1()); - Encoder encoder = TypeLengthValueUtils.getEncoder(); - encoder.add(MessageType.STATE, (short) 0x04); - encoder.add(MessageType.PROOF, m2); - return new PairingResponse(encoder.toByteArray()); - } + // Precomputed safe 3072 bit prime 'N'. Origin RFC 5054, appendix A. + private static final BigInteger N_3072 = + new BigInteger( + "5809605995369958062791915965639201402176612226902900533702900882779736177890990861472094774477339581147373410185646378328043729800750470098210924487866935059164371588168047540943981644516632755067501626434556398193186628990071248660819361205119793693985433297036118232914410171876807536457391277857011849897410207519105333355801121109356897459426271845471397952675959440793493071628394122780510124618488232602464649876850458861245784240929258426287699705312584509625419513463605155428017165714465363094021609290561084025893662561222573202082865797821865270991145082200656978177192827024538990239969175546190770645685893438011714430426409338676314743571154537142031573004276428701433036381801705308659830751190352946025482059931306571004727362479688415574702596946457770284148435989129632853918392117997472632693078113129886487399347796982772784615865232621289656944284216824611318709764535152507354116344703769998514148343807"); + private static final BigInteger G = BigInteger.valueOf(5); + private static final String IDENTIFIER = "Pair-Setup"; + + private static final Logger logger = LoggerFactory.getLogger(SrpHandler.class); + + private final BigInteger salt; + private final HomekitSRP6ServerSession session; + private final SRP6CryptoParams config; + private final String pin; + + public SrpHandler(String pin, BigInteger salt) { + config = new SRP6CryptoParams(N_3072, G, "SHA-512"); + session = new HomekitSRP6ServerSession(config); + session.setClientEvidenceRoutine(new ClientEvidenceRoutineImpl()); + session.setServerEvidenceRoutine(new ServerEvidenceRoutineImpl()); + this.pin = pin; + this.salt = salt; + } + + public HttpResponse handle(PairSetupRequest request) throws Exception { + switch (request.getStage()) { + case ONE: + return step1(); + + case TWO: + return step2((Stage2Request) request); + + default: + return new NotFoundResponse(); + } + } + + private HttpResponse step1() throws Exception { + if (session.getState() != State.INIT) { + logger.error("Session is not in state INIT when receiving step1"); + return new ConflictResponse(); + } + + SRP6VerifierGenerator verifierGenerator = new SRP6VerifierGenerator(config); + verifierGenerator.setXRoutine(new XRoutineWithUserIdentity()); + BigInteger verifier = verifierGenerator.generateVerifier(salt, IDENTIFIER, pin); + + Encoder encoder = TypeLengthValueUtils.getEncoder(); + encoder.add(MessageType.STATE, (short) 0x02); + encoder.add(MessageType.SALT, salt); + encoder.add(MessageType.PUBLIC_KEY, session.step1(IDENTIFIER, salt, verifier)); + return new PairingResponse(encoder.toByteArray()); + } + + private HttpResponse step2(Stage2Request request) throws Exception { + if (session.getState() != State.STEP_1) { + logger.error("Session is not in state Stage 1 when receiving step2"); + return new ConflictResponse(); + } + BigInteger m2 = session.step2(request.getA(), request.getM1()); + Encoder encoder = TypeLengthValueUtils.getEncoder(); + encoder.add(MessageType.STATE, (short) 0x04); + encoder.add(MessageType.PROOF, m2); + return new PairingResponse(encoder.toByteArray()); + } - public byte[] getK() { - MessageDigest digest = session.getCryptoParams().getMessageDigestInstance(); - BigInteger S = session.getSessionKey(false); - byte[] sBytes = bigIntegerToUnsignedByteArray(S); - return digest.digest(sBytes); - } + public byte[] getK() { + MessageDigest digest = session.getCryptoParams().getMessageDigestInstance(); + BigInteger S = session.getSessionKey(false); + byte[] sBytes = bigIntegerToUnsignedByteArray(S); + return digest.digest(sBytes); + } - public static byte[] bigIntegerToUnsignedByteArray(BigInteger i) { - byte[] array = i.toByteArray(); - if (array[0] == 0) { - array = Arrays.copyOfRange(array, 1, array.length); - } - return array; - } - + public static byte[] bigIntegerToUnsignedByteArray(BigInteger i) { + byte[] array = i.toByteArray(); + if (array[0] == 0) { + array = Arrays.copyOfRange(array, 1, array.length); + } + return array; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/pairing/Stage.java b/src/main/java/com/beowulfe/hap/impl/pairing/Stage.java index 34000e319..68fe3362d 100644 --- a/src/main/java/com/beowulfe/hap/impl/pairing/Stage.java +++ b/src/main/java/com/beowulfe/hap/impl/pairing/Stage.java @@ -1,7 +1,7 @@ package com.beowulfe.hap.impl.pairing; public enum Stage { - ONE, - TWO, - THREE -} \ No newline at end of file + ONE, + TWO, + THREE +} diff --git a/src/main/java/com/beowulfe/hap/impl/pairing/TypeLengthValueUtils.java b/src/main/java/com/beowulfe/hap/impl/pairing/TypeLengthValueUtils.java index 2c5f38206..9e57efa17 100644 --- a/src/main/java/com/beowulfe/hap/impl/pairing/TypeLengthValueUtils.java +++ b/src/main/java/com/beowulfe/hap/impl/pairing/TypeLengthValueUtils.java @@ -9,92 +9,88 @@ import java.util.Map; public class TypeLengthValueUtils { - - private TypeLengthValueUtils() { } - - public static DecodeResult decode(byte[] content) throws IOException { - DecodeResult ret = new DecodeResult(); - ByteArrayInputStream bais = new ByteArrayInputStream(content); - while(bais.available() > 0) { - byte type = (byte) (bais.read() & 0xFF); - int length = bais.read(); - byte[] part = new byte[length]; - bais.read(part); - ret.add(type, part); - } - return ret; - } - - public static Encoder getEncoder() { - return new Encoder(); - } - - public static final class Encoder { - - private final ByteArrayOutputStream baos; - - private Encoder() { - baos = new ByteArrayOutputStream(); - } - - public void add(MessageType type, BigInteger i) throws IOException { - add(type, ByteUtils.toByteArray(i)); - } - - public void add(MessageType type, short b) { - baos.write(type.getKey()); - baos.write(1); - baos.write(b); - } - - public void add(MessageType type, byte[] bytes) throws IOException { - InputStream bais = new ByteArrayInputStream(bytes); - while(bais.available() > 0) { - int toWrite = bais.available(); - toWrite = toWrite > 255 ? 255 : toWrite; - baos.write(type.getKey()); - baos.write(toWrite); - ByteUtils.copyStream(bais, baos, toWrite); - } - } - - public byte[] toByteArray() { - return baos.toByteArray(); - } - - } - - public static final class DecodeResult { - private final Map result = new HashMap<>(); - - private DecodeResult() { - } - - public byte getByte(MessageType type) { - return result.get(type.getKey())[0]; - } - - public BigInteger getBigInt(MessageType type) { - return new BigInteger(1, result.get(type.getKey())); - } - - public byte[] getBytes(MessageType type) { - return result.get(type.getKey()); - } - - public void getBytes(MessageType type, byte[] dest, int srcOffset) { - byte[] b = result.get(type.getKey()); - System.arraycopy(b, srcOffset, dest, 0, Math.min(dest.length, b.length)); - } - - public int getLength(MessageType type) { - return result.get(type.getKey()).length; - } - - private void add(short type, byte[] bytes) { - result.merge(type, bytes, ByteUtils::joinBytes); - } - - } - + + private TypeLengthValueUtils() {} + + public static DecodeResult decode(byte[] content) throws IOException { + DecodeResult ret = new DecodeResult(); + ByteArrayInputStream bais = new ByteArrayInputStream(content); + while (bais.available() > 0) { + byte type = (byte) (bais.read() & 0xFF); + int length = bais.read(); + byte[] part = new byte[length]; + bais.read(part); + ret.add(type, part); + } + return ret; + } + + public static Encoder getEncoder() { + return new Encoder(); + } + + public static final class Encoder { + + private final ByteArrayOutputStream baos; + + private Encoder() { + baos = new ByteArrayOutputStream(); + } + + public void add(MessageType type, BigInteger i) throws IOException { + add(type, ByteUtils.toByteArray(i)); + } + + public void add(MessageType type, short b) { + baos.write(type.getKey()); + baos.write(1); + baos.write(b); + } + + public void add(MessageType type, byte[] bytes) throws IOException { + InputStream bais = new ByteArrayInputStream(bytes); + while (bais.available() > 0) { + int toWrite = bais.available(); + toWrite = toWrite > 255 ? 255 : toWrite; + baos.write(type.getKey()); + baos.write(toWrite); + ByteUtils.copyStream(bais, baos, toWrite); + } + } + + public byte[] toByteArray() { + return baos.toByteArray(); + } + } + + public static final class DecodeResult { + private final Map result = new HashMap<>(); + + private DecodeResult() {} + + public byte getByte(MessageType type) { + return result.get(type.getKey())[0]; + } + + public BigInteger getBigInt(MessageType type) { + return new BigInteger(1, result.get(type.getKey())); + } + + public byte[] getBytes(MessageType type) { + return result.get(type.getKey()); + } + + public void getBytes(MessageType type, byte[] dest, int srcOffset) { + byte[] b = result.get(type.getKey()); + System.arraycopy(b, srcOffset, dest, 0, Math.min(dest.length, b.length)); + } + + public int getLength(MessageType type) { + return result.get(type.getKey()).length; + } + + private void add(short type, byte[] bytes) { + result.merge(type, bytes, ByteUtils::joinBytes); + } + } } diff --git a/src/main/java/com/beowulfe/hap/impl/pairing/UpgradeResponse.java b/src/main/java/com/beowulfe/hap/impl/pairing/UpgradeResponse.java index e16cdc147..81bf833ad 100644 --- a/src/main/java/com/beowulfe/hap/impl/pairing/UpgradeResponse.java +++ b/src/main/java/com/beowulfe/hap/impl/pairing/UpgradeResponse.java @@ -4,26 +4,25 @@ public class UpgradeResponse extends PairingResponse { - private final byte[] readKey; - private final byte[] writeKey; - - UpgradeResponse(byte[] body, byte[] readKey, byte[] writeKey) { - super(body); - this.readKey = readKey; - this.writeKey = writeKey; - } + private final byte[] readKey; + private final byte[] writeKey; - @Override - public boolean doUpgrade() { - return true; - } - - public ByteBuffer getReadKey() { - return ByteBuffer.wrap(readKey); - } - - public ByteBuffer getWriteKey() { - return ByteBuffer.wrap(writeKey); - } + UpgradeResponse(byte[] body, byte[] readKey, byte[] writeKey) { + super(body); + this.readKey = readKey; + this.writeKey = writeKey; + } + @Override + public boolean doUpgrade() { + return true; + } + + public ByteBuffer getReadKey() { + return ByteBuffer.wrap(readKey); + } + + public ByteBuffer getWriteKey() { + return ByteBuffer.wrap(writeKey); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/responses/ConflictResponse.java b/src/main/java/com/beowulfe/hap/impl/responses/ConflictResponse.java index 873669e6a..4b3dbcfcf 100644 --- a/src/main/java/com/beowulfe/hap/impl/responses/ConflictResponse.java +++ b/src/main/java/com/beowulfe/hap/impl/responses/ConflictResponse.java @@ -4,9 +4,8 @@ public class ConflictResponse implements HttpResponse { - @Override - public int getStatusCode() { - return 409; - } - + @Override + public int getStatusCode() { + return 409; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/responses/InternalServerErrorResponse.java b/src/main/java/com/beowulfe/hap/impl/responses/InternalServerErrorResponse.java index f2a978e53..ed98c3400 100644 --- a/src/main/java/com/beowulfe/hap/impl/responses/InternalServerErrorResponse.java +++ b/src/main/java/com/beowulfe/hap/impl/responses/InternalServerErrorResponse.java @@ -1,30 +1,28 @@ package com.beowulfe.hap.impl.responses; +import com.beowulfe.hap.impl.http.HttpResponse; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import com.beowulfe.hap.impl.http.HttpResponse; - public class InternalServerErrorResponse implements HttpResponse { - private final Exception e; - - public InternalServerErrorResponse(Exception e) { - this.e = e; - } - - @Override - public int getStatusCode() { - return 500; - } - - @Override - public ByteBuffer getBody() { - return ByteBuffer.wrap(e.getClass().getName().getBytes(StandardCharsets.UTF_8)); - } - - public Exception getException() { - return e; - } + private final Exception e; + + public InternalServerErrorResponse(Exception e) { + this.e = e; + } + + @Override + public int getStatusCode() { + return 500; + } + + @Override + public ByteBuffer getBody() { + return ByteBuffer.wrap(e.getClass().getName().getBytes(StandardCharsets.UTF_8)); + } + public Exception getException() { + return e; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/responses/NotFoundResponse.java b/src/main/java/com/beowulfe/hap/impl/responses/NotFoundResponse.java index 4c806a077..acf6256c8 100644 --- a/src/main/java/com/beowulfe/hap/impl/responses/NotFoundResponse.java +++ b/src/main/java/com/beowulfe/hap/impl/responses/NotFoundResponse.java @@ -4,9 +4,8 @@ public class NotFoundResponse implements HttpResponse { - @Override - public int getStatusCode() { - return 404; - } - + @Override + public int getStatusCode() { + return 404; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/responses/OkResponse.java b/src/main/java/com/beowulfe/hap/impl/responses/OkResponse.java index 34e7d61e6..2baa9a4db 100644 --- a/src/main/java/com/beowulfe/hap/impl/responses/OkResponse.java +++ b/src/main/java/com/beowulfe/hap/impl/responses/OkResponse.java @@ -1,25 +1,23 @@ package com.beowulfe.hap.impl.responses; -import java.nio.ByteBuffer; - import com.beowulfe.hap.impl.http.HttpResponse; +import java.nio.ByteBuffer; public class OkResponse implements HttpResponse { - private final ByteBuffer body; - - public OkResponse(byte[] body) { - this.body = ByteBuffer.wrap(body); - } - - @Override - public ByteBuffer getBody() { - return body; - } - - @Override - public int getStatusCode() { - return 200; - } + private final ByteBuffer body; + + public OkResponse(byte[] body) { + this.body = ByteBuffer.wrap(body); + } + + @Override + public ByteBuffer getBody() { + return body; + } + @Override + public int getStatusCode() { + return 200; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/responses/UnauthorizedResponse.java b/src/main/java/com/beowulfe/hap/impl/responses/UnauthorizedResponse.java index 4d481f366..12fe26d0f 100644 --- a/src/main/java/com/beowulfe/hap/impl/responses/UnauthorizedResponse.java +++ b/src/main/java/com/beowulfe/hap/impl/responses/UnauthorizedResponse.java @@ -4,9 +4,8 @@ public class UnauthorizedResponse implements HttpResponse { - @Override - public int getStatusCode() { - return 401; - } - + @Override + public int getStatusCode() { + return 401; + } } diff --git a/src/main/java/com/beowulfe/hap/impl/services/AbstractServiceImpl.java b/src/main/java/com/beowulfe/hap/impl/services/AbstractServiceImpl.java index 844b0ecb6..83d917cba 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/AbstractServiceImpl.java +++ b/src/main/java/com/beowulfe/hap/impl/services/AbstractServiceImpl.java @@ -1,12 +1,5 @@ package com.beowulfe.hap.impl.services; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.beowulfe.hap.HomekitAccessory; import com.beowulfe.hap.Service; import com.beowulfe.hap.accessories.BatteryAccessory; @@ -15,79 +8,82 @@ import com.beowulfe.hap.impl.characteristics.common.BatteryLevelCharacteristic; import com.beowulfe.hap.impl.characteristics.common.LowBatteryStatusCharacteristic; import com.beowulfe.hap.impl.characteristics.common.Name; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; abstract class AbstractServiceImpl implements Service { - private final Logger logger = LoggerFactory.getLogger(this.getClass()); - private final String type; - private final List characteristics = new LinkedList<>(); - - /** - * This constructor has been deprecated and replaced with - * {@link #AbstractServiceImpl(String, HomekitAccessory, String)}. Usages of - * this constructor will need to manually configure {@link Name} characteristic - * and {@link BatteryLevelCharacteristic} if needed. - * - * @param type unique UUID of the service. This information can be obtained from HomeKit Accessory Simulator. - */ - @Deprecated - public AbstractServiceImpl(String type) { - this(type, null, null); - } + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + private final String type; + private final List characteristics = new LinkedList<>(); - /** - *

- * Creates a new instance of this class with the specified UUID and {@link HomekitAccessory}. - * Download and install HomeKit Accessory Simulator to discover the corresponding UUID for - * the specific service. - *

- * - *

- * The new service will automatically add {@link Name} characteristic. If the accessory - * is battery operated then it must implement {@link BatteryAccessory} and {@link BatteryLevelCharacteristic} - * will be added too. - *

- * - * @param type unique UUID of the service. This information can be obtained from HomeKit Accessory Simulator. - * @param accessory HomeKit accessory exposed as a service. - * @param serviceName name of the service. This information is usually the name of the accessory. - */ - public AbstractServiceImpl(String type, HomekitAccessory accessory, String serviceName) { - this.type = type; + /** + * This constructor has been deprecated and replaced with {@link #AbstractServiceImpl(String, + * HomekitAccessory, String)}. Usages of this constructor will need to manually configure {@link + * Name} characteristic and {@link BatteryLevelCharacteristic} if needed. + * + * @param type unique UUID of the service. This information can be obtained from HomeKit Accessory + * Simulator. + */ + @Deprecated + public AbstractServiceImpl(String type) { + this(type, null, null); + } - if (accessory != null) { - // Add name characteristic - addCharacteristic(new Name(serviceName)); + /** + * Creates a new instance of this class with the specified UUID and {@link HomekitAccessory}. + * Download and install HomeKit Accessory Simulator to discover the corresponding UUID for + * the specific service. + * + *

The new service will automatically add {@link Name} characteristic. If the accessory is + * battery operated then it must implement {@link BatteryAccessory} and {@link + * BatteryLevelCharacteristic} will be added too. + * + * @param type unique UUID of the service. This information can be obtained from HomeKit Accessory + * Simulator. + * @param accessory HomeKit accessory exposed as a service. + * @param serviceName name of the service. This information is usually the name of the accessory. + */ + public AbstractServiceImpl(String type, HomekitAccessory accessory, String serviceName) { + this.type = type; - // If battery operated accessory then add BatteryLevelCharacteristic - if (accessory instanceof BatteryAccessory) { - logger.warn( - "Accessory {} implements BatteryAccessory, which was incorrectly used to advertise battery state and is not recognized by HomeKit. " - + "Battery-powered devices should report their battery status using LowBatteryStatusAccessory", - accessory.getClass()); - } + if (accessory != null) { + // Add name characteristic + addCharacteristic(new Name(serviceName)); - // If battery operated accessory then add LowBatteryStatusAccessory - if (accessory instanceof BatteryStatusAccessory) { - BatteryStatusAccessory batteryStatusAccessory = (BatteryStatusAccessory) accessory; - addCharacteristic(new LowBatteryStatusCharacteristic(batteryStatusAccessory::getLowBatteryState, - batteryStatusAccessory::subscribeLowBatteryState, - batteryStatusAccessory::unsubscribeLowBatteryState)); + // If battery operated accessory then add BatteryLevelCharacteristic + if (accessory instanceof BatteryAccessory) { + logger.warn( + "Accessory {} implements BatteryAccessory, which was incorrectly used to advertise battery state and is not recognized by HomeKit. " + + "Battery-powered devices should report their battery status using LowBatteryStatusAccessory", + accessory.getClass()); + } - } - } + // If battery operated accessory then add LowBatteryStatusAccessory + if (accessory instanceof BatteryStatusAccessory) { + BatteryStatusAccessory batteryStatusAccessory = (BatteryStatusAccessory) accessory; + addCharacteristic( + new LowBatteryStatusCharacteristic( + batteryStatusAccessory::getLowBatteryState, + batteryStatusAccessory::subscribeLowBatteryState, + batteryStatusAccessory::unsubscribeLowBatteryState)); + } } + } - @Override - public List getCharacteristics() { - return Collections.unmodifiableList(characteristics); - } + @Override + public List getCharacteristics() { + return Collections.unmodifiableList(characteristics); + } - @Override - public String getType() { - return type; - } + @Override + public String getType() { + return type; + } - protected void addCharacteristic(Characteristic characteristic) { - this.characteristics.add(characteristic); - } + protected void addCharacteristic(Characteristic characteristic) { + this.characteristics.add(characteristic); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/services/AccessoryInformationService.java b/src/main/java/com/beowulfe/hap/impl/services/AccessoryInformationService.java index b821f769d..7b22d130b 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/AccessoryInformationService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/AccessoryInformationService.java @@ -8,16 +8,16 @@ public class AccessoryInformationService extends AbstractServiceImpl { - public AccessoryInformationService(HomekitAccessory accessory) throws Exception { - this(accessory, accessory.getLabel()); - } - - public AccessoryInformationService(HomekitAccessory accessory, String serviceName) throws Exception { - super("0000003E-0000-1000-8000-0026BB765291", accessory, serviceName); - addCharacteristic(new Manufacturer(accessory)); - addCharacteristic(new Model(accessory)); - addCharacteristic(new SerialNumber(accessory)); - addCharacteristic(new Identify(accessory)); - } + public AccessoryInformationService(HomekitAccessory accessory) throws Exception { + this(accessory, accessory.getLabel()); + } + public AccessoryInformationService(HomekitAccessory accessory, String serviceName) + throws Exception { + super("0000003E-0000-1000-8000-0026BB765291", accessory, serviceName); + addCharacteristic(new Manufacturer(accessory)); + addCharacteristic(new Model(accessory)); + addCharacteristic(new SerialNumber(accessory)); + addCharacteristic(new Identify(accessory)); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/services/CarbonMonoxideSensorService.java b/src/main/java/com/beowulfe/hap/impl/services/CarbonMonoxideSensorService.java index c84633af9..8f331e57a 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/CarbonMonoxideSensorService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/CarbonMonoxideSensorService.java @@ -5,12 +5,13 @@ public class CarbonMonoxideSensorService extends AbstractServiceImpl { - public CarbonMonoxideSensorService(CarbonMonoxideSensor carbonMonoxideSensor) { - this(carbonMonoxideSensor, carbonMonoxideSensor.getLabel()); - } + public CarbonMonoxideSensorService(CarbonMonoxideSensor carbonMonoxideSensor) { + this(carbonMonoxideSensor, carbonMonoxideSensor.getLabel()); + } - public CarbonMonoxideSensorService(CarbonMonoxideSensor carbonMonoxideSensor, String serviceName) { - super("0000007F-0000-1000-8000-0026BB765291", carbonMonoxideSensor, serviceName); - addCharacteristic(new CarbonMonoxideDetectedCharacteristic(carbonMonoxideSensor)); - } + public CarbonMonoxideSensorService( + CarbonMonoxideSensor carbonMonoxideSensor, String serviceName) { + super("0000007F-0000-1000-8000-0026BB765291", carbonMonoxideSensor, serviceName); + addCharacteristic(new CarbonMonoxideDetectedCharacteristic(carbonMonoxideSensor)); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/services/ContactSensorService.java b/src/main/java/com/beowulfe/hap/impl/services/ContactSensorService.java index 5f7ff17fa..98321fd8c 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/ContactSensorService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/ContactSensorService.java @@ -5,12 +5,12 @@ public class ContactSensorService extends AbstractServiceImpl { - public ContactSensorService(ContactSensor contactSensor) { - this(contactSensor, contactSensor.getLabel()); - } + public ContactSensorService(ContactSensor contactSensor) { + this(contactSensor, contactSensor.getLabel()); + } - public ContactSensorService(ContactSensor contactSensor, String serviceName) { - super("00000080-0000-1000-8000-0026BB765291", contactSensor, serviceName); - addCharacteristic(new ContactSensorStateCharacteristic(contactSensor)); - } + public ContactSensorService(ContactSensor contactSensor, String serviceName) { + super("00000080-0000-1000-8000-0026BB765291", contactSensor, serviceName); + addCharacteristic(new ContactSensorStateCharacteristic(contactSensor)); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/services/FanService.java b/src/main/java/com/beowulfe/hap/impl/services/FanService.java index feb75c1c4..3303118a3 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/FanService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/FanService.java @@ -7,20 +7,19 @@ public class FanService extends AbstractServiceImpl { - public FanService(Fan fan) { - this(fan, fan.getLabel()); - } + public FanService(Fan fan) { + this(fan, fan.getLabel()); + } - public FanService(Fan fan, String serviceName) { - super("00000040-0000-1000-8000-0026BB765291", fan, serviceName); - addCharacteristic(new PowerStateCharacteristic( - () -> fan.getFanPower(), - v -> fan.setFanPower(v), - c -> fan.subscribeFanPower(c), - () -> fan.unsubscribeFanPower() - )); - addCharacteristic(new RotationDirectionCharacteristic(fan)); - addCharacteristic(new RotationSpeedCharacteristic(fan)); - } - -} \ No newline at end of file + public FanService(Fan fan, String serviceName) { + super("00000040-0000-1000-8000-0026BB765291", fan, serviceName); + addCharacteristic( + new PowerStateCharacteristic( + () -> fan.getFanPower(), + v -> fan.setFanPower(v), + c -> fan.subscribeFanPower(c), + () -> fan.unsubscribeFanPower())); + addCharacteristic(new RotationDirectionCharacteristic(fan)); + addCharacteristic(new RotationSpeedCharacteristic(fan)); + } +} diff --git a/src/main/java/com/beowulfe/hap/impl/services/GarageDoorService.java b/src/main/java/com/beowulfe/hap/impl/services/GarageDoorService.java index 6469a2387..8bdfcf85f 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/GarageDoorService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/GarageDoorService.java @@ -7,17 +7,18 @@ public class GarageDoorService extends AbstractServiceImpl { - public GarageDoorService(GarageDoor door) { - this(door, door.getLabel()); - } + public GarageDoorService(GarageDoor door) { + this(door, door.getLabel()); + } - public GarageDoorService(GarageDoor door, String serviceName) { - super("00000041-0000-1000-8000-0026BB765291", door, serviceName); - addCharacteristic(new CurrentDoorStateCharacteristic(door)); - addCharacteristic(new TargetDoorStateCharacteristic(door)); - addCharacteristic(new ObstructionDetectedCharacteristic(() -> door.getObstructionDetected(), - c -> door.subscribeObstructionDetected(c), - () -> door.unsubscribeObstructionDetected())); - } - -} \ No newline at end of file + public GarageDoorService(GarageDoor door, String serviceName) { + super("00000041-0000-1000-8000-0026BB765291", door, serviceName); + addCharacteristic(new CurrentDoorStateCharacteristic(door)); + addCharacteristic(new TargetDoorStateCharacteristic(door)); + addCharacteristic( + new ObstructionDetectedCharacteristic( + () -> door.getObstructionDetected(), + c -> door.subscribeObstructionDetected(c), + () -> door.unsubscribeObstructionDetected())); + } +} diff --git a/src/main/java/com/beowulfe/hap/impl/services/HumiditySensorService.java b/src/main/java/com/beowulfe/hap/impl/services/HumiditySensorService.java index 23b3c354e..4404185f2 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/HumiditySensorService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/HumiditySensorService.java @@ -4,14 +4,13 @@ import com.beowulfe.hap.impl.characteristics.humiditysensor.CurrentRelativeHumidityCharacteristic; public class HumiditySensorService extends AbstractServiceImpl { - - public HumiditySensorService(HumiditySensor sensor) { - this(sensor, sensor.getLabel()); - } - public HumiditySensorService(HumiditySensor sensor, String serviceName) { - super("00000082-0000-1000-8000-0026BB765291", sensor, serviceName); - addCharacteristic(new CurrentRelativeHumidityCharacteristic(sensor)); - } + public HumiditySensorService(HumiditySensor sensor) { + this(sensor, sensor.getLabel()); + } -} \ No newline at end of file + public HumiditySensorService(HumiditySensor sensor, String serviceName) { + super("00000082-0000-1000-8000-0026BB765291", sensor, serviceName); + addCharacteristic(new CurrentRelativeHumidityCharacteristic(sensor)); + } +} diff --git a/src/main/java/com/beowulfe/hap/impl/services/LeakSensorService.java b/src/main/java/com/beowulfe/hap/impl/services/LeakSensorService.java index cf712efa7..e342b0864 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/LeakSensorService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/LeakSensorService.java @@ -5,12 +5,12 @@ public class LeakSensorService extends AbstractServiceImpl { - public LeakSensorService(LeakSensor leakSensor) { - this(leakSensor, leakSensor.getLabel()); - } + public LeakSensorService(LeakSensor leakSensor) { + this(leakSensor, leakSensor.getLabel()); + } - public LeakSensorService(LeakSensor leakSensor, String serviceName) { - super("00000083-0000-1000-8000-0026BB765291", leakSensor, serviceName); - addCharacteristic(new LeakDetectedStateCharacteristic(leakSensor)); - } + public LeakSensorService(LeakSensor leakSensor, String serviceName) { + super("00000083-0000-1000-8000-0026BB765291", leakSensor, serviceName); + addCharacteristic(new LeakDetectedStateCharacteristic(leakSensor)); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/services/LightSensorService.java b/src/main/java/com/beowulfe/hap/impl/services/LightSensorService.java index b5c1610a3..3c1810371 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/LightSensorService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/LightSensorService.java @@ -5,12 +5,12 @@ public class LightSensorService extends AbstractServiceImpl { - public LightSensorService(LightSensor lightSensor) { - this(lightSensor, lightSensor.getLabel()); - } + public LightSensorService(LightSensor lightSensor) { + this(lightSensor, lightSensor.getLabel()); + } - public LightSensorService(LightSensor lightSensor, String serviceName) { - super("00000084-0000-1000-8000-0026BB765291", lightSensor, serviceName); - addCharacteristic(new AmbientLightLevelCharacteristic(lightSensor)); - } + public LightSensorService(LightSensor lightSensor, String serviceName) { + super("00000084-0000-1000-8000-0026BB765291", lightSensor, serviceName); + addCharacteristic(new AmbientLightLevelCharacteristic(lightSensor)); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/services/LightbulbService.java b/src/main/java/com/beowulfe/hap/impl/services/LightbulbService.java index 604d7176d..b8173d9d9 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/LightbulbService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/LightbulbService.java @@ -10,27 +10,26 @@ public class LightbulbService extends AbstractServiceImpl { - public LightbulbService(Lightbulb lightbulb) { - this(lightbulb, lightbulb.getLabel()); - } + public LightbulbService(Lightbulb lightbulb) { + this(lightbulb, lightbulb.getLabel()); + } - public LightbulbService(Lightbulb lightbulb, String serviceName) { - super("00000043-0000-1000-8000-0026BB765291", lightbulb, serviceName); - addCharacteristic(new PowerStateCharacteristic( - () -> lightbulb.getLightbulbPowerState(), - v -> lightbulb.setLightbulbPowerState(v), - c -> lightbulb.subscribeLightbulbPowerState(c), - () -> lightbulb.unsubscribeLightbulbPowerState() - )); - - if (lightbulb instanceof DimmableLightbulb) { - addCharacteristic(new BrightnessCharacteristic((DimmableLightbulb) lightbulb)); - } - - if (lightbulb instanceof ColorfulLightbulb) { - addCharacteristic(new HueCharacteristic((ColorfulLightbulb) lightbulb)); - addCharacteristic(new SaturationCharacteristic((ColorfulLightbulb) lightbulb)); - } - } + public LightbulbService(Lightbulb lightbulb, String serviceName) { + super("00000043-0000-1000-8000-0026BB765291", lightbulb, serviceName); + addCharacteristic( + new PowerStateCharacteristic( + () -> lightbulb.getLightbulbPowerState(), + v -> lightbulb.setLightbulbPowerState(v), + c -> lightbulb.subscribeLightbulbPowerState(c), + () -> lightbulb.unsubscribeLightbulbPowerState())); + if (lightbulb instanceof DimmableLightbulb) { + addCharacteristic(new BrightnessCharacteristic((DimmableLightbulb) lightbulb)); + } + + if (lightbulb instanceof ColorfulLightbulb) { + addCharacteristic(new HueCharacteristic((ColorfulLightbulb) lightbulb)); + addCharacteristic(new SaturationCharacteristic((ColorfulLightbulb) lightbulb)); + } + } } diff --git a/src/main/java/com/beowulfe/hap/impl/services/LockMechanismService.java b/src/main/java/com/beowulfe/hap/impl/services/LockMechanismService.java index e2817c1dc..5dc1f868d 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/LockMechanismService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/LockMechanismService.java @@ -7,16 +7,16 @@ public class LockMechanismService extends AbstractServiceImpl { - public LockMechanismService(LockMechanism lock) { - this(lock, lock.getLabel()); - } + public LockMechanismService(LockMechanism lock) { + this(lock, lock.getLabel()); + } - public LockMechanismService(LockMechanism lock, String serviceName) { - super("00000045-0000-1000-8000-0026BB765291", lock, serviceName); - addCharacteristic(new CurrentLockMechanismStateCharacteristic(lock)); - - if (lock instanceof LockableLockMechanism) { - addCharacteristic(new TargetLockMechanismStateCharacteristic((LockableLockMechanism) lock)); - } - } + public LockMechanismService(LockMechanism lock, String serviceName) { + super("00000045-0000-1000-8000-0026BB765291", lock, serviceName); + addCharacteristic(new CurrentLockMechanismStateCharacteristic(lock)); + + if (lock instanceof LockableLockMechanism) { + addCharacteristic(new TargetLockMechanismStateCharacteristic((LockableLockMechanism) lock)); + } + } } diff --git a/src/main/java/com/beowulfe/hap/impl/services/MotionSensorService.java b/src/main/java/com/beowulfe/hap/impl/services/MotionSensorService.java index ed2064f4d..d05a2db1f 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/MotionSensorService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/MotionSensorService.java @@ -5,12 +5,12 @@ public class MotionSensorService extends AbstractServiceImpl { - public MotionSensorService(MotionSensor motionSensor) { - this(motionSensor, motionSensor.getLabel()); - } + public MotionSensorService(MotionSensor motionSensor) { + this(motionSensor, motionSensor.getLabel()); + } - public MotionSensorService(MotionSensor motionSensor, String serviceName) { - super("00000085-0000-1000-8000-0026BB765291", motionSensor, serviceName); - addCharacteristic(new MotionDetectedStateCharacteristic(motionSensor)); - } + public MotionSensorService(MotionSensor motionSensor, String serviceName) { + super("00000085-0000-1000-8000-0026BB765291", motionSensor, serviceName); + addCharacteristic(new MotionDetectedStateCharacteristic(motionSensor)); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/services/OutletService.java b/src/main/java/com/beowulfe/hap/impl/services/OutletService.java index d7d5a68de..f4d14c384 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/OutletService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/OutletService.java @@ -6,19 +6,18 @@ public class OutletService extends AbstractServiceImpl { - public OutletService(Outlet outlet) { - this(outlet, outlet.getLabel()); - } - - public OutletService(Outlet outlet, String serviceName) { - super("00000047-0000-1000-8000-0026BB765291", outlet, serviceName); - addCharacteristic(new PowerStateCharacteristic( - () -> outlet.getPowerState(), - v -> outlet.setPowerState(v), - c -> outlet.subscribePowerState(c), - () -> outlet.unsubscribePowerState() - )); - addCharacteristic(new OutletInUseCharacteristic(outlet)); - } + public OutletService(Outlet outlet) { + this(outlet, outlet.getLabel()); + } + public OutletService(Outlet outlet, String serviceName) { + super("00000047-0000-1000-8000-0026BB765291", outlet, serviceName); + addCharacteristic( + new PowerStateCharacteristic( + () -> outlet.getPowerState(), + v -> outlet.setPowerState(v), + c -> outlet.subscribePowerState(c), + () -> outlet.unsubscribePowerState())); + addCharacteristic(new OutletInUseCharacteristic(outlet)); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/services/SecuritySystemService.java b/src/main/java/com/beowulfe/hap/impl/services/SecuritySystemService.java index 6d6f9a6bf..31226a550 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/SecuritySystemService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/SecuritySystemService.java @@ -7,14 +7,14 @@ public class SecuritySystemService extends AbstractServiceImpl { - public SecuritySystemService(SecuritySystem securitySystem) { - this(securitySystem, securitySystem.getLabel()); - } + public SecuritySystemService(SecuritySystem securitySystem) { + this(securitySystem, securitySystem.getLabel()); + } - public SecuritySystemService(SecuritySystem securitySystem, String serviceName) { - super("0000007E-0000-1000-8000-0026BB765291", securitySystem, serviceName); - addCharacteristic(new CurrentSecuritySystemStateCharacteristic(securitySystem)); - addCharacteristic(new TargetSecuritySystemStateCharacteristic(securitySystem)); - addCharacteristic(new SecuritySystemAlarmTypeCharacteristic(securitySystem)); - } + public SecuritySystemService(SecuritySystem securitySystem, String serviceName) { + super("0000007E-0000-1000-8000-0026BB765291", securitySystem, serviceName); + addCharacteristic(new CurrentSecuritySystemStateCharacteristic(securitySystem)); + addCharacteristic(new TargetSecuritySystemStateCharacteristic(securitySystem)); + addCharacteristic(new SecuritySystemAlarmTypeCharacteristic(securitySystem)); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/services/SmokeSensorService.java b/src/main/java/com/beowulfe/hap/impl/services/SmokeSensorService.java index 2f5c4e993..8e69ba427 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/SmokeSensorService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/SmokeSensorService.java @@ -5,12 +5,12 @@ public class SmokeSensorService extends AbstractServiceImpl { - public SmokeSensorService(SmokeSensor smokeSensor) { - this(smokeSensor, smokeSensor.getLabel()); - } + public SmokeSensorService(SmokeSensor smokeSensor) { + this(smokeSensor, smokeSensor.getLabel()); + } - public SmokeSensorService(SmokeSensor smokeSensor, String serviceName) { - super("00000087-0000-1000-8000-0026BB765291", smokeSensor, serviceName); - addCharacteristic(new SmokeDetectedCharacteristic(smokeSensor)); - } + public SmokeSensorService(SmokeSensor smokeSensor, String serviceName) { + super("00000087-0000-1000-8000-0026BB765291", smokeSensor, serviceName); + addCharacteristic(new SmokeDetectedCharacteristic(smokeSensor)); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/services/SwitchService.java b/src/main/java/com/beowulfe/hap/impl/services/SwitchService.java index 8bdda8989..01abf1f0d 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/SwitchService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/SwitchService.java @@ -5,18 +5,17 @@ public class SwitchService extends AbstractServiceImpl { - public SwitchService(Switch switchAccessory) { - this(switchAccessory, switchAccessory.getLabel()); - } + public SwitchService(Switch switchAccessory) { + this(switchAccessory, switchAccessory.getLabel()); + } - public SwitchService(Switch switchAccessory, String serviceName) { - super("00000049-0000-1000-8000-0026BB765291", switchAccessory, serviceName); - addCharacteristic(new PowerStateCharacteristic( - () -> switchAccessory.getSwitchState(), - v -> switchAccessory.setSwitchState(v), - c -> switchAccessory.subscribeSwitchState(c), - () -> switchAccessory.unsubscribeSwitchState() - )); - } - -} \ No newline at end of file + public SwitchService(Switch switchAccessory, String serviceName) { + super("00000049-0000-1000-8000-0026BB765291", switchAccessory, serviceName); + addCharacteristic( + new PowerStateCharacteristic( + () -> switchAccessory.getSwitchState(), + v -> switchAccessory.setSwitchState(v), + c -> switchAccessory.subscribeSwitchState(c), + () -> switchAccessory.unsubscribeSwitchState())); + } +} diff --git a/src/main/java/com/beowulfe/hap/impl/services/TemperatureSensorService.java b/src/main/java/com/beowulfe/hap/impl/services/TemperatureSensorService.java index b29bae501..2571b75f2 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/TemperatureSensorService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/TemperatureSensorService.java @@ -4,14 +4,13 @@ import com.beowulfe.hap.impl.characteristics.thermostat.CurrentTemperatureCharacteristic; public class TemperatureSensorService extends AbstractServiceImpl { - - public TemperatureSensorService(TemperatureSensor sensor) { - this(sensor, sensor.getLabel()); - } - public TemperatureSensorService(TemperatureSensor sensor, String serviceName) { - super("0000008A-0000-1000-8000-0026BB765291", sensor, serviceName); - addCharacteristic(new CurrentTemperatureCharacteristic(sensor)); - } + public TemperatureSensorService(TemperatureSensor sensor) { + this(sensor, sensor.getLabel()); + } + public TemperatureSensorService(TemperatureSensor sensor, String serviceName) { + super("0000008A-0000-1000-8000-0026BB765291", sensor, serviceName); + addCharacteristic(new CurrentTemperatureCharacteristic(sensor)); + } } diff --git a/src/main/java/com/beowulfe/hap/impl/services/ThermostatService.java b/src/main/java/com/beowulfe/hap/impl/services/ThermostatService.java index 564a28aeb..b15178903 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/ThermostatService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/ThermostatService.java @@ -7,23 +7,24 @@ public class ThermostatService extends AbstractServiceImpl { - public ThermostatService(BasicThermostat thermostat) { - this(thermostat, thermostat.getLabel()); - } - - public ThermostatService(BasicThermostat thermostat, String serviceName) { - super("0000004A-0000-1000-8000-0026BB765291", thermostat, serviceName); - addCharacteristic(new CurrentHeatingCoolingModeCharacteristic(thermostat)); - addCharacteristic(new CurrentTemperatureCharacteristic(thermostat)); - addCharacteristic(new TargetHeatingCoolingModeCharacteristic(thermostat)); - addCharacteristic(new TargetTemperatureCharacteristic(thermostat)); - addCharacteristic(new TemperatureUnitsCharacteristic(thermostat)); - if (thermostat instanceof HeatingThermostat) { - addCharacteristic(new HeatingThresholdTemperatureCharacteristic((HeatingThermostat) thermostat)); - } - if (thermostat instanceof CoolingThermostat) { - addCharacteristic(new CoolingThresholdTemperatureCharacteristic((CoolingThermostat) thermostat)); - } - } + public ThermostatService(BasicThermostat thermostat) { + this(thermostat, thermostat.getLabel()); + } + public ThermostatService(BasicThermostat thermostat, String serviceName) { + super("0000004A-0000-1000-8000-0026BB765291", thermostat, serviceName); + addCharacteristic(new CurrentHeatingCoolingModeCharacteristic(thermostat)); + addCharacteristic(new CurrentTemperatureCharacteristic(thermostat)); + addCharacteristic(new TargetHeatingCoolingModeCharacteristic(thermostat)); + addCharacteristic(new TargetTemperatureCharacteristic(thermostat)); + addCharacteristic(new TemperatureUnitsCharacteristic(thermostat)); + if (thermostat instanceof HeatingThermostat) { + addCharacteristic( + new HeatingThresholdTemperatureCharacteristic((HeatingThermostat) thermostat)); + } + if (thermostat instanceof CoolingThermostat) { + addCharacteristic( + new CoolingThresholdTemperatureCharacteristic((CoolingThermostat) thermostat)); + } + } } diff --git a/src/main/java/com/beowulfe/hap/impl/services/ValveService.java b/src/main/java/com/beowulfe/hap/impl/services/ValveService.java index 601d55e69..ed4117765 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/ValveService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/ValveService.java @@ -10,23 +10,31 @@ public class ValveService extends AbstractServiceImpl { - public ValveService(Valve valve) { - this(valve, valve.getLabel()); - } + public ValveService(Valve valve) { + this(valve, valve.getLabel()); + } - public ValveService(Valve valve, String serviceName) { - super("000000D0-0000-1000-8000-0026BB765291", valve, serviceName); - addCharacteristic(new ActiveCharacteristic(valve::getValveActive, a -> valve.setValveActive(a), - valve::subscribeValveActive, valve::unsubscribeValveActive)); - addCharacteristic(new ValveTypeCharacteristic(valve)); - addCharacteristic(new InUseCharacteristic(valve::getValveInUse, valve::subscribeValveInUse, - valve::unsubscribeValveInUse)); + public ValveService(Valve valve, String serviceName) { + super("000000D0-0000-1000-8000-0026BB765291", valve, serviceName); + addCharacteristic( + new ActiveCharacteristic( + valve::getValveActive, + a -> valve.setValveActive(a), + valve::subscribeValveActive, + valve::unsubscribeValveActive)); + addCharacteristic(new ValveTypeCharacteristic(valve)); + addCharacteristic( + new InUseCharacteristic( + valve::getValveInUse, valve::subscribeValveInUse, valve::unsubscribeValveInUse)); - if (valve instanceof ValveWithTimer) { - ValveWithTimer vwt = (ValveWithTimer) valve; - addCharacteristic(new SetDurationCharacteristic(vwt)); - addCharacteristic(new RemainingDurationCharacteristic(vwt::getRemainingDuration, - vwt::subscribeRemainingDuration, vwt::unsubscribeRemainingDuration)); - } + if (valve instanceof ValveWithTimer) { + ValveWithTimer vwt = (ValveWithTimer) valve; + addCharacteristic(new SetDurationCharacteristic(vwt)); + addCharacteristic( + new RemainingDurationCharacteristic( + vwt::getRemainingDuration, + vwt::subscribeRemainingDuration, + vwt::unsubscribeRemainingDuration)); } + } } diff --git a/src/main/java/com/beowulfe/hap/impl/services/WindowCoveringService.java b/src/main/java/com/beowulfe/hap/impl/services/WindowCoveringService.java index 18d6499ba..f26cce03d 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/WindowCoveringService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/WindowCoveringService.java @@ -8,31 +8,37 @@ public class WindowCoveringService extends AbstractServiceImpl { - public WindowCoveringService(WindowCovering windowCovering) { - this(windowCovering, windowCovering.getLabel()); - } + public WindowCoveringService(WindowCovering windowCovering) { + this(windowCovering, windowCovering.getLabel()); + } - public WindowCoveringService(WindowCovering windowCovering, String serviceName) { - super("0000008C-0000-1000-8000-0026BB765291", windowCovering, serviceName); - addCharacteristic(new CurrentPositionCharacteristic(windowCovering)); - addCharacteristic(new HoldPositionCharacteristic(windowCovering)); - addCharacteristic(new PositionStateCharacteristic(windowCovering)); - addCharacteristic(new TargetPositionCharacteristic(windowCovering)); - addCharacteristic(new ObstructionDetectedCharacteristic(() -> windowCovering.getObstructionDetected(), - c -> windowCovering.subscribeObstructionDetected(c), - () -> windowCovering.unsubscribeObstructionDetected())); + public WindowCoveringService(WindowCovering windowCovering, String serviceName) { + super("0000008C-0000-1000-8000-0026BB765291", windowCovering, serviceName); + addCharacteristic(new CurrentPositionCharacteristic(windowCovering)); + addCharacteristic(new HoldPositionCharacteristic(windowCovering)); + addCharacteristic(new PositionStateCharacteristic(windowCovering)); + addCharacteristic(new TargetPositionCharacteristic(windowCovering)); + addCharacteristic( + new ObstructionDetectedCharacteristic( + () -> windowCovering.getObstructionDetected(), + c -> windowCovering.subscribeObstructionDetected(c), + () -> windowCovering.unsubscribeObstructionDetected())); - if (windowCovering instanceof HorizontalTiltingWindowCovering) { - addCharacteristic(new CurrentHorizontalTiltAngleCharacteristic( - (HorizontalTiltingWindowCovering) windowCovering)); - addCharacteristic(new TargetHorizontalTiltAngleCharacteristic( - (HorizontalTiltingWindowCovering) windowCovering)); - } - if (windowCovering instanceof VerticalTiltingWindowCovering) { - addCharacteristic(new CurrentVerticalTiltAngleCharacteristic( - (VerticalTiltingWindowCovering) windowCovering)); - addCharacteristic(new TargetVerticalTiltAngleCharacteristic( - (VerticalTiltingWindowCovering) windowCovering)); - } - } + if (windowCovering instanceof HorizontalTiltingWindowCovering) { + addCharacteristic( + new CurrentHorizontalTiltAngleCharacteristic( + (HorizontalTiltingWindowCovering) windowCovering)); + addCharacteristic( + new TargetHorizontalTiltAngleCharacteristic( + (HorizontalTiltingWindowCovering) windowCovering)); + } + if (windowCovering instanceof VerticalTiltingWindowCovering) { + addCharacteristic( + new CurrentVerticalTiltAngleCharacteristic( + (VerticalTiltingWindowCovering) windowCovering)); + addCharacteristic( + new TargetVerticalTiltAngleCharacteristic( + (VerticalTiltingWindowCovering) windowCovering)); + } + } } diff --git a/src/main/java/com/beowulfe/hap/package-info.java b/src/main/java/com/beowulfe/hap/package-info.java index 2a3f7b220..69e95d9d2 100644 --- a/src/main/java/com/beowulfe/hap/package-info.java +++ b/src/main/java/com/beowulfe/hap/package-info.java @@ -1,4 +1,5 @@ /** - * Base package for implementing the Homekit Accessory Protocol. Start with {@link com.beowulfe.hap.HomekitServer}. + * Base package for implementing the Homekit Accessory Protocol. Start with {@link + * com.beowulfe.hap.HomekitServer}. */ -package com.beowulfe.hap; \ No newline at end of file +package com.beowulfe.hap; diff --git a/src/test/java/com/beowulfe/hap/HomekitRootTest.java b/src/test/java/com/beowulfe/hap/HomekitRootTest.java index f6ce00c3e..869cb0e14 100644 --- a/src/test/java/com/beowulfe/hap/HomekitRootTest.java +++ b/src/test/java/com/beowulfe/hap/HomekitRootTest.java @@ -4,97 +4,99 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; -import java.util.concurrent.CompletableFuture; - -import org.junit.*; - import com.beowulfe.hap.impl.HomekitWebHandler; import com.beowulfe.hap.impl.http.HomekitClientConnectionFactory; import com.beowulfe.hap.impl.jmdns.JmdnsHomekitAdvertiser; +import java.util.concurrent.CompletableFuture; +import org.junit.*; public class HomekitRootTest { - - private HomekitAccessory accessory; - private HomekitRoot root; - private HomekitWebHandler webHandler; - private JmdnsHomekitAdvertiser advertiser; - private HomekitAuthInfo authInfo; - - private final static int PORT = 12345; - private final static String LABEL = "Test Label"; - - @Before - public void setup() throws Exception { - accessory = mock(HomekitAccessory.class); - when(accessory.getId()).thenReturn(2); - webHandler = mock(HomekitWebHandler.class); - when(webHandler.start(any())).thenReturn(CompletableFuture.completedFuture(PORT)); - advertiser = mock(JmdnsHomekitAdvertiser.class); - authInfo = mock(HomekitAuthInfo.class); - root = new HomekitRoot(LABEL, webHandler, authInfo, advertiser); - } - - @Test - public void verifyRegistryAdded() throws Exception { - root.addAccessory(accessory); - Assert.assertTrue("Registry does not contain accessory", root.getRegistry().getAccessories().contains(accessory)); - } - - @Test - public void verifyRegistryRemoved() throws Exception { - root.addAccessory(accessory); - root.removeAccessory(accessory); - Assert.assertFalse("Registry still contains accessory", root.getRegistry().getAccessories().contains(accessory)); - } - - @Test - public void testWebHandlerStarts() throws Exception { - root.start(); - verify(webHandler).start(any(HomekitClientConnectionFactory.class)); - } - - @Test - public void testWebHandlerStops() throws Exception { - root.start(); - root.stop(); - verify(webHandler).stop(); - } - - @Test - public void testAdvertiserStarts() throws Exception { - String mac = "00:00:00:00:00:00"; - when(authInfo.getMac()).thenReturn(mac); - root.start(); - verify(advertiser).advertise(eq(LABEL), eq(mac), eq(PORT), eq(1)); - } - - @Test - public void testAdvertiserStops() throws Exception { - root.start(); - root.stop(); - verify(advertiser).stop(); - } - - @Test - public void testAddAccessoryResetsWeb() { - root.start(); - verify(webHandler, never()).resetConnections(); - root.addAccessory(accessory); - verify(webHandler).resetConnections(); - } - - @Test - public void testRemoveAccessoryResetsWeb() { - root.addAccessory(accessory); - root.start(); - verify(webHandler, never()).resetConnections(); - root.removeAccessory(accessory); - verify(webHandler).resetConnections(); - } - @Test(expected=IndexOutOfBoundsException.class) - public void testAddIndexOneAccessory() throws Exception { - when(accessory.getId()).thenReturn(1); - root.addAccessory(accessory); - } + private HomekitAccessory accessory; + private HomekitRoot root; + private HomekitWebHandler webHandler; + private JmdnsHomekitAdvertiser advertiser; + private HomekitAuthInfo authInfo; + + private static final int PORT = 12345; + private static final String LABEL = "Test Label"; + + @Before + public void setup() throws Exception { + accessory = mock(HomekitAccessory.class); + when(accessory.getId()).thenReturn(2); + webHandler = mock(HomekitWebHandler.class); + when(webHandler.start(any())).thenReturn(CompletableFuture.completedFuture(PORT)); + advertiser = mock(JmdnsHomekitAdvertiser.class); + authInfo = mock(HomekitAuthInfo.class); + root = new HomekitRoot(LABEL, webHandler, authInfo, advertiser); + } + + @Test + public void verifyRegistryAdded() throws Exception { + root.addAccessory(accessory); + Assert.assertTrue( + "Registry does not contain accessory", + root.getRegistry().getAccessories().contains(accessory)); + } + + @Test + public void verifyRegistryRemoved() throws Exception { + root.addAccessory(accessory); + root.removeAccessory(accessory); + Assert.assertFalse( + "Registry still contains accessory", + root.getRegistry().getAccessories().contains(accessory)); + } + + @Test + public void testWebHandlerStarts() throws Exception { + root.start(); + verify(webHandler).start(any(HomekitClientConnectionFactory.class)); + } + + @Test + public void testWebHandlerStops() throws Exception { + root.start(); + root.stop(); + verify(webHandler).stop(); + } + + @Test + public void testAdvertiserStarts() throws Exception { + String mac = "00:00:00:00:00:00"; + when(authInfo.getMac()).thenReturn(mac); + root.start(); + verify(advertiser).advertise(eq(LABEL), eq(mac), eq(PORT), eq(1)); + } + + @Test + public void testAdvertiserStops() throws Exception { + root.start(); + root.stop(); + verify(advertiser).stop(); + } + + @Test + public void testAddAccessoryResetsWeb() { + root.start(); + verify(webHandler, never()).resetConnections(); + root.addAccessory(accessory); + verify(webHandler).resetConnections(); + } + + @Test + public void testRemoveAccessoryResetsWeb() { + root.addAccessory(accessory); + root.start(); + verify(webHandler, never()).resetConnections(); + root.removeAccessory(accessory); + verify(webHandler).resetConnections(); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testAddIndexOneAccessory() throws Exception { + when(accessory.getId()).thenReturn(1); + root.addAccessory(accessory); + } }