├── .gitignore ├── .travis.yml ├── .travis ├── codesigning.asc.enc ├── deploy └── prepare ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── pom.xml ├── settings.xml └── src ├── main └── java │ └── com │ └── github │ └── mob41 │ └── blapi │ ├── A1Device.java │ ├── BLDevice.java │ ├── EnvData.java │ ├── FloureonDevice.java │ ├── HysenDevice.java │ ├── MP1Device.java │ ├── RM2Device.java │ ├── RMDevice.java │ ├── SP1Device.java │ ├── SP2Device.java │ ├── dev │ └── hysen │ │ ├── AdvancedStatusInfo.java │ │ ├── AntiFreezing.java │ │ ├── BaseHysenDevice.java │ │ ├── BaseStatusInfo.java │ │ ├── LoopMode.java │ │ ├── Period.java │ │ ├── PowerOnMemory.java │ │ └── SensorControl.java │ ├── ex │ ├── BLApiException.java │ ├── BLApiRuntimeException.java │ └── package-info.java │ ├── mac │ ├── Mac.java │ ├── MacFormatException.java │ └── package-info.java │ ├── package-info.java │ └── pkt │ ├── BytePayload.java │ ├── CmdPacket.java │ ├── CmdPayload.java │ ├── Crc16.java │ ├── Packet.java │ ├── Payload.java │ ├── auth │ ├── AES.java │ ├── AuthCmdPayload.java │ ├── AuthPayload.java │ └── package-info.java │ ├── cmd │ ├── hysen │ │ ├── BaseHysenCommand.java │ │ ├── GenericByteCommand.java │ │ ├── GetBasicInfoCommand.java │ │ ├── GetStatusCommand.java │ │ ├── SetModeCommand.java │ │ ├── SetPeriodsCommand.java │ │ ├── SetPoweCommand.java │ │ ├── SetTempCommand.java │ │ └── SetTimeCommand.java │ ├── package-info.java │ └── rm2 │ │ ├── CheckDataCmdPayload.java │ │ ├── EnterLearnCmdPayload.java │ │ ├── RMTempCmdPayload.java │ │ ├── SendDataCmdPayload.java │ │ └── package-info.java │ ├── dis │ ├── DiscoveryPacket.java │ └── package-info.java │ └── package-info.java └── test ├── java └── com │ └── github │ └── mob41 │ └── blapi │ └── DevicesTest.java └── resources └── simplelogger.properties /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .classpath 3 | .project 4 | .settings/ 5 | /bin/ 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - openjdk8 5 | 6 | install: true 7 | 8 | before_install: 9 | - sudo apt-get update 10 | - sudo apt-get install gnupg gnupg2 11 | - chmod +x .travis/prepare 12 | - .travis/prepare 13 | 14 | script: 15 | - mvn cobertura:cobertura 16 | 17 | after_success: 18 | - bash <(curl -s https://codecov.io/bash) 19 | 20 | before_deploy: 21 | - ls 22 | - chmod +x .travis/deploy 23 | 24 | deploy: 25 | provider: script 26 | script: bash .travis/deploy 27 | on: 28 | branch: master 29 | 30 | after_deploy: 31 | # TODO: Auto JavaDoc Gen 32 | #- git config --global user.email "travis@travis-ci.org" 33 | #- git config --global user.name "travis-ci" 34 | 35 | branches: 36 | only: 37 | - master 38 | 39 | env: 40 | global: 41 | - secure: "Y7H9VySOaGASsbGhr55/HDhAt7EJvMslHuTYub8It/rWaG8f2DGJyfekLovB8Am/ZrW2V4BNOBzL5HaEqiAjdtTKKq9Xd6ZhqF54ZGkd6J64kMIUI+pcKwBgWLlzD8UhKrZiG3ubrfoYVFa08Sb+BhCXfPS2+SFdECb2NH47dFkZR+uqJKN18s7yQzrqvb0jabpFZgCR2dPMJfKoHMCErScV/oT875QEArHFCD1wwK7tQWTvtqWhG9BDZOMdClX4BP8TQx+nN9SNkUvyiG998/Jeu+Hm0lslTBo+bm/jMbPIu4d4YZvbDDXltcn4kuQLtfX5FcdVI9aAK0B7Ycc5t4Ci19zhGLIVzFBiQ0ER3igoSQM/F8EwsvkP1Pl3Nl7sFIhagNgQK2DLeEj7NXe56IUjY7HDl7Bo/ffq31GSjs9XqXVC3EEghilexN8LMbsjuhfGWCHlqb3/a4zw1rr8ekv2iTXBglUTCLghxqkDVFYg+7QdS77fyi+OTo9NZEm46pV5QhabD02JxeFef55uuDwuospzYXzE6KZkIxeZq1bgOtZ9PNi7bjUd4uribOE+dlO8k4+MOiWyEGNoDZhd16Q+C+j1B+Z0SuGgZbFi1F6vvAmnBkyF3jUDxiUaRJ5RVh+eX03f8R5tIhb5hqzDsTB1wXM0oWUHsgXb93VVygI=" 42 | - secure: "fn+6muORgvM7G39J4bIP588OPuYijfnkcEUAQE/xPcPmJ2Q3In+XmDiuyh8lbRHZ88fU/5zGV7Zpvafjgo0dW4dxAtjZYuXArvY5cy5LSSmiElCKvptP09WLRqBRQ/DGS7NEBlTD6TDipdFFVEJOtOz2vg71eKadlUA1HCnhvGv7pOCZeWxzC4oohuPDdMoegQFBIrjHU4i7q0auIHU5hrT2UPppZnqfDMmIDMTU/hkfKxnoeW6DroBCHjCJtwEW+aKb837KgCmDiKsAhGJ/B+PJP/aqNH4ZxIPWPsmgvMIslnW9Csa8k+D95EXxUIyawyfz9AHjg/E85yRRYqhm2PCQxlKfNuTOsBd8ynBZEOr3GQB3+VzCmSTZSRKjAf2Q5aqFi6KFhs/4hg054GryO+qbSL5efO2mkTRnNFzEfOeEGR4ADC2yTVJabu824FVgk3Dcuwp7ll8U/LdUZwYsGt4nS8RW5izOlRnonFWiRDA1JpQNb5bjJVtu8ZjR0BrZpdqa9gWawzSUrxESbK6Zlyuu+4wS5VypGVhbhsAtVsSH+ZT45pDDT5EeK8Fe02f9z4TJmH4K+K6Rf7S6TXQtLXQ5r5X0Xn6OQtx5axJ5UzDbfRUs48MDMsRu3eecD5ROos/hEQOy7Mb39dUbo0grm9pC7q9STgKSilVUhiFxkiQ=" 43 | - secure: "eFDjBk2mLHkMyf1nMqOSWCj/jzUAqpwrnhSovUNF/kCVuyTF0hLqtENyruj9NlbB3q8Lbtd05OhTCnJhL3Fibr/1DCgQwdLEBp0ROXE1tlIZj1r8BMY1CEVIp6ubiEW9YxIKajV+b3sLr+kQtTZ9rL7EECwRuBDhpLYGDjR+KET3608tuUdEuLrSBSsqmdIbBB2EZMHP485WnOxL1AULgOA6xWOzkkOoiNZqaIeDAEmSBo/vCB3T+KN/jAipSq9QSog76fGvNtUAki+tG7LPl/gspGyLqMRrhjFimz7Z1qJYjKxopeuodqcNuERKZZIV9LvGUsHFVaEEaswnEtl0FEsuPZRGMWe9rhZqTtBHqSgwUcnOh6Lful0wVj/3ZEZrSoX4sRRgQOb6AJ5la6MX5pZyowjr1kdhnslSW4YHG9JD28M+CysCkHqmANmjt/9Uf774hXth4cKH5itHiVkE5GQFrj3reAYGs/usGJ14RI3fzuhh7fR/zWSEknjxfVFZDpVsXpaKKnbaeDO+r4ly8odH/Y3JtjWzYPSUbedgw/wagJpO3VUVbb5VAGFqm4U19ekNMOJWeT3bXiUvH0wJsRTBOWwY5aUQK8tF4xajyqa3RiUPsDzDeaM0lXH62a0umVdCMEj1w4rf801GDV2zjWW8BVH7YGFlvzcpc5jFCOE=" 44 | - secure: "s15e/Zme0CMGXMGrOV4frcUkBlBq9A8Qt0x1IE8Tl1DJvwTe5bLN4vY4raobP0vfEFjgKx5hrWJqWLBosj4b3fe6ea8v0/kAoFw+OjGBtHsWEA8qhf1K8c4/dHSc56IRpgRvF7xNAXtjp3rCWyleiUGJvDFfY/oEiUSzrdxDOgtV8uQnahtwrfTniXDsZq2GChm7/AUwTqGwROkGa0YtA9ogqgCv7Ckk3Gac1tmiuTZUGnDgqmiCIqdG2CIu8SznTCu0FkRHqO2vfAh+wyEt3ScanSCZeM983yaHv7tDLfRgh1b1VHCUxMYEhzICn32zZTGcvgmBWe2MOQWg4anUoP71pVIT7vx0VveMuatQ8/jeviLjXaRRMVz4DjLBI+E+RN77dn84XjIBgOsE0ZsRPktxIgECxM08BT/fFCLxw/1fMyNFt44P46FEC8Ly3NGxfg/74RIou24uAog52rhs93nchqQj4XCyz3MikJ3kdJCFCj/mPDLmd7Xr8urbbf7CIwNQmUoilPBeyd1eFxaD/ICC6zcxDSM7EhufetSLh0NXIHM/qvFntl81P5LjgIbYNMIleiRuzsDMIam93ppqRmxC3YMUhEYzERm09t2ra9b7kNyUJqw4JnpUjpImPIQzo8R/V17BmihI/T+/1PDCkhBwfg0qTILORsA0/4si5s0=" 45 | -------------------------------------------------------------------------------- /.travis/codesigning.asc.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a1aw/broadlink-java-api/c6fc5a367fe655d776cb39559db441e6bb668db7/.travis/codesigning.asc.enc -------------------------------------------------------------------------------- /.travis/deploy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ "$TRAVIS_PULL_REQUEST" == 'false' ]; then 3 | if [ "$TRAVIS_BRANCH" = 'master' ]; then 4 | mvn deploy --settings settings.xml 5 | fi 6 | fi 7 | 8 | -------------------------------------------------------------------------------- /.travis/prepare: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ "$TRAVIS_PULL_REQUEST" == 'false' ]; then 3 | if [ "$TRAVIS_BRANCH" = 'master' ]; then 4 | openssl aes-256-cbc -K $encrypted_f5e90db9d6e7_key -iv $encrypted_f5e90db9d6e7_iv -in .travis/codesigning.asc.enc -out .travis/codesigning.asc -d 5 | gpg --fast-import .travis/codesigning.asc 6 | fi 7 | fi 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | Multiple important things to know before contributing to the repository. 3 | 4 | ## Reporting Issues 5 | 6 | As the library is still in heavy development stage, most things are not supposed to be stable. We are welcome to receive problems and bugs reports from users. 7 | 8 | To report a bug, a proper log recording files are required. Please kindly create a file called ```log4j.properties``` next to your application/development environment: 9 | 10 | ```java 11 | log4j.rootLogger=DEBUG, STDOUT 12 | log4j.logger.deng=DEBUG 13 | log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender 14 | log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout 15 | log4j.appender.STDOUT.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n 16 | ``` 17 | 18 | This file will allow the library to print debug logs to console. 19 | 20 | And try to reproduce the problem again. After that, copy the logs to a text file and attach/upload to the issue page. 21 | 22 | ## Code contributing 23 | 24 | Code contributing to the Java Broadlink library is appreciated. However, please follow the following coding style, so as to improve code readability. 25 | 26 | ### We use [K&R (1TBS (OTBS))](https://en.wikipedia.org/wiki/Indent_style#K.26R) coding style. 27 | 28 | ```java 29 | if (condition){ 30 | //do stuff 31 | } else { 32 | //do else stuff 33 | } 34 | 35 | for (int i = 0; i < 10; i++){ 36 | //do stuff 37 | } 38 | 39 | while(true){ 40 | //do loop stuff 41 | } 42 | ``` 43 | 44 | ### Use spaces instead of tabs for indentation. (1 tab for 4 spaces) [[Eclipse IDE]](https://stackoverflow.com/questions/407929/how-do-i-change-eclipse-to-use-spaces-instead-of-tabs) 45 | 46 | ### No EOF allowed in the last line of the source code. Please add a new line (```\r\n```) instead. 47 | 48 | ### Comment and describe usage on every bitwise operation 49 | 50 | ```java 51 | int test = 15; //0000 1111 52 | 53 | test |= 15 << 4; //shift 15 (00001111) left 4 position and OR "test" 54 | ``` 55 | 56 | ### JavaDoc/comment for every method, including constructor, for better JavaDoc API documentation generating 57 | 58 | JavaDoc comment starts with ```/**```, appends ```*``` down on each single line and ends with ```*/```. For example: 59 | 60 | ```java 61 | /** 62 | * This is the base class of all Broadlink devices (e.g. SP1, RMPro) 63 | * 64 | * @author Anthony 65 | * 66 | */ 67 | public BLDevice(){ 68 | //do stuff 69 | } 70 | ``` 71 | 72 | ### Comment style 73 | 74 | Don't be lazy :smile: Eclipse IDE loves automatically helps you to open a comment box like this: 75 | 76 | ```java 77 | /* 78 | * This is a dirty comment 79 | * 80 | */ 81 | ``` 82 | 83 | We don't allow to use this kind of commenting. It is only allowed to use to comment outside of any method to keep clean. Instead, you could use this in methods: 84 | 85 | ```java 86 | /* 87 | System.out.println("I just wanted to comment this code snippet."); 88 | System.out.println("It is much cleaner, right?"); 89 | */ 90 | ``` 91 | 92 | Or even using the simple ```//```: 93 | 94 | ```java 95 | //System.exit(0); 96 | ``` 97 | 98 | 99 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Anthony Law 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # broadlink-java-api [![Build Status](https://travis-ci.org/mob41/broadlink-java-api.svg?branch=master)](https://travis-ci.org/mob41/broadlink-java-api) [![CodeFactor](https://www.codefactor.io/repository/github/mob41/broadlink-java-api/badge)](https://www.codefactor.io/repository/github/mob41/broadlink-java-api) [![codecov](https://codecov.io/gh/mob41/broadlink-java-api/branch/master/graph/badge.svg)](https://codecov.io/gh/mob41/broadlink-java-api) [![Maven Central](https://img.shields.io/maven-central/v/com.github.mob41.blapi/broadlink-java-api.svg)](http://central.maven.org/maven2/com/github/mob41/blapi/broadlink-java-api) 2 | A clean Java API to Broadlink devices! 3 | 4 | This is a Java version of [mjg59](https://github.com/mjg59)'s [python-broadlink](https://github.com/mjg59/python-broadlink) library. 5 | 6 | ## Adding this API 7 | 8 | This API is distributed via **Maven Central**. You can import via adding this as dependency in Maven ```pom.xml``` or clone/download this as ZIP and import in IDE. 9 | 10 | >[![Maven Central](https://img.shields.io/maven-central/v/com.github.mob41.blapi/broadlink-java-api.svg)](http://central.maven.org/maven2/com/github/mob41/blapi/broadlink-java-api) [![Maven Snapshot](https://img.shields.io/maven-metadata/v/http/oss.sonatype.org/content/repositories/snapshots/com/github/mob41/blapi/broadlink-java-api/maven-metadata.xml.svg?maxAge=2592000&label=maven%20snapshot)](https://oss.sonatype.org/content/repositories/snapshots/com/github/mob41/blapi/broadlink-java-api/) [![Maven Latest](https://img.shields.io/maven-metadata/v/http/oss.sonatype.org/content/groups/public/com/github/mob41/blapi/broadlink-java-api/maven-metadata.xml.svg?maxAge=2592000&label=maven%20latest)](https://oss.sonatype.org/content/groups/public/com/github/mob41/blapi/broadlink-java-api/) 11 | 12 | 1. **Via Maven Central**: (Release Builds) Add the following to your ```pom.xml``` under `````` 13 | 14 | ```xml 15 | 16 | com.github.mob41.blapi 17 | broadlink-java-api 18 | 1.0.1 19 | 20 | ``` 21 | 22 | 2. **Via OSSRH Snapshots**: (Development Builds) To access snapshots/development builds e.g. ```1.0.1-SNAPSHOT```, you have to add the OSSRH snapshot repository 23 | 24 | ```xml 25 | 26 | 27 | allow-snapshots 28 | true 29 | 30 | 31 | snapshots-repo 32 | https://oss.sonatype.org/content/repositories/snapshots 33 | false 34 | true 35 | 36 | 37 | 38 | 39 | ``` 40 | 41 | 3. **Via Cloning**: (Eclipse) Clone the project via ```git clone https://github.com/mob41/broadlink-java-api.git``` or via the [download ZIP](https://github.com/mob41/broadlink-java-api/archive/master.zip) and extract the ZIP to a folder. 42 | 43 | And add the project into your Eclipse IDE by right clicking the ```Package Explorer```, and, 44 | ```Import...``` -> ```Maven``` -> ```Existing Maven Projects``` 45 | 46 | Select the folder you cloned/downloaded broadlink-java-api to. And select the ```pom.xml``` inside. 47 | 48 | The project should be added. And the following you have to do is add the following dependency to your ```pom.xml```: 49 | 50 | ```xml 51 | 52 | com.github.mob41.blapi 53 | broadlink-java-api 54 | 1.0.1 55 | 56 | ``` 57 | 58 | ## Tutorial 59 | 60 | 1. Import necessary libraries 61 | 62 | ```java 63 | import com.github.mob41.blapi.BLDevice; 64 | import com.github.mob41.blapi.RM2Device; //Necessary if using 2.ii 65 | import com.github.mob41.blapi.mac.Mac; //Necessary if using 2.ii 66 | ``` 67 | 68 | 2. Creating/Discovering ```BLDevice``` instances by two methods: 69 | 70 | ```java 71 | // 72 | // === Method 1. By Discovering Devices In Local Network === 73 | // 74 | 75 | BLDevice[] devs = BLDevice.discoverDevices(); //Default with 10000 ms (10 sec) timeout, search for multiple devices 76 | 77 | //BLDevice[] devs = BLDevice.discoverDevices(0); //No timeout will block the thread and search for one device only 78 | //BLDevice[] devs = BLDevice.discoverDevices(5000); //With 5000 ms (5 sec) timeout 79 | 80 | //The BLDevice[] array stores the found devices in the local network 81 | 82 | System.out.println("Number of devices: " + devs.length); 83 | 84 | BLDevice blDevice = null; 85 | for (BLDevice dev : devs){ 86 | System.out.println("Type: " + Integer.toHexString(dev.getDeviceType()) + " Host: " + dev.getHost() + " Mac: " + dev.getMac()); 87 | } 88 | 89 | //BLDevice dev = devs[0] 90 | 91 | // 92 | // === Method 2. Create a "RM2Device" or another "BLDevice" child according to your device type === 93 | // 94 | 95 | BLDevice dev = new RM2Device("192.168.1.123", new Mac("01:12:23:34:43:320")); 96 | //~do stuff 97 | //dev.auth(); 98 | ``` 99 | 100 | 3. Before any commands like ```getTemp()``` and ```enterLearning()```, ```BLDevice.auth()``` must be ran to connect and authenticate with the Broadlink device. 101 | 102 | ```java 103 | boolean success = dev.auth(); 104 | System.out.println("Auth status: " + (success ? "Success!" : "Failed!")); 105 | ``` 106 | 107 | 3. Every BLDevice has its very own methods. Please refer to their own source code in the repository (as the main documentation still not completed...). Here's an example: 108 | 109 | ```java 110 | if (dev instanceof RM2Device){ 111 | RM2Device rm2 = (RM2Device) dev; 112 | 113 | boolean success = rm2.enterLearning(); 114 | System.out.println("Enter Learning status: " + (success ? "Success!" : "Failed!")); 115 | 116 | float temp = rm2.getTemp(); 117 | System.out.println("Current temperature reported from RM2: " + temp + " degrees"); 118 | } else { 119 | System.out.println("The \"dev\" is not a RM2Device instance."); 120 | } 121 | ``` 122 | 123 | ## License 124 | 125 | [tl;dr](https://tldrlegal.com/license/mit-license) This project is licensed under the MIT License. -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 29 | 31 | 4.0.0 32 | 33 | com.github.mob41.blapi 34 | broadlink-java-api 35 | jar 36 | 37 | broadlink-java-api 38 | A clean, hacked Broadlink API for Java 39 | http://github.com/mob41/broadlink-java-api 40 | 41 | 42 | 43 | MIT License 44 | http://www.opensource.org/licenses/mit-license.php 45 | 46 | 47 | 48 | 49 | 50 | Anthony Law 51 | anthonylaw0401@gmail.com 52 | Anthony Law 53 | http://github.com/mob41 54 | 55 | 56 | 57 | 58 | scm:git:git://github.com/mob41/broadlink-java-api.git 59 | scm:git:ssh://github.com:mob41/broadlink-java-api.git 60 | http://github.com/mob41/broadlink-java-api/tree/master 61 | 62 | 63 | 64 | 65 | ossrh 66 | https://oss.sonatype.org/content/repositories/snapshots 67 | 68 | 69 | ossrh 70 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 71 | 72 | 73 | 74 | 75 | UTF-8 76 | 1.8 77 | 1.8 78 | 79 | 80 | 81 | 82 | junit 83 | junit 84 | 4.12 85 | test 86 | 87 | 88 | org.slf4j 89 | slf4j-api 90 | 1.7.22 91 | 92 | 93 | org.slf4j 94 | slf4j-simple 95 | 1.7.22 96 | test 97 | 98 | 99 | 100 | 101 | 102 | 103 | org.apache.maven.plugins 104 | maven-source-plugin 105 | 2.2.1 106 | 107 | 108 | attach-sources 109 | 110 | jar-no-fork 111 | 112 | 113 | 114 | 115 | 116 | org.apache.maven.plugins 117 | maven-javadoc-plugin 118 | 2.9.1 119 | 120 | 121 | attach-javadocs 122 | 123 | jar 124 | 125 | 126 | 127 | 128 | 129 | org.apache.maven.plugins 130 | maven-gpg-plugin 131 | 1.5 132 | 133 | 134 | sign-artifacts 135 | verify 136 | 137 | sign 138 | 139 | 140 | 141 | 142 | 143 | org.codehaus.mojo 144 | cobertura-maven-plugin 145 | 2.7 146 | 147 | 148 | html 149 | xml 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 1.0.1 158 | 159 | -------------------------------------------------------------------------------- /settings.xml: -------------------------------------------------------------------------------- 1 | 29 | 30 | 31 | 32 | ossrh 33 | ${env.CI_DEPLOY_USERNAME} 34 | ${env.CI_DEPLOY_PASSWORD} 35 | 36 | 37 | 38 | 39 | ossrh 40 | 41 | true 42 | 43 | 44 | gpg2 45 | ${env.CI_KEY_NAME} 46 | ${env.CI_KEY_PASSPHRASE} 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/A1Device.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi; 30 | 31 | import java.io.IOException; 32 | import java.net.DatagramPacket; 33 | 34 | import javax.xml.bind.DatatypeConverter; 35 | 36 | import com.github.mob41.blapi.mac.Mac; 37 | import com.github.mob41.blapi.pkt.CmdPayload; 38 | import com.github.mob41.blapi.pkt.Payload; 39 | 40 | public class A1Device extends BLDevice { 41 | 42 | public A1Device(String host, Mac mac) throws IOException { 43 | super(BLDevice.DEV_A1, BLDevice.DESC_A1, host, mac); 44 | } 45 | 46 | public EnvData getSensorsData() throws Exception { 47 | DatagramPacket packet = sendCmdPkt(new CmdPayload() { 48 | 49 | @Override 50 | public byte getCommand() { 51 | return 0x6a; 52 | } 53 | 54 | @Override 55 | public Payload getPayload() { 56 | return new Payload() { 57 | 58 | @Override 59 | public byte[] getData() { 60 | byte[] b = new byte[16]; 61 | b[0] = 1; 62 | return b; 63 | } 64 | 65 | }; 66 | } 67 | 68 | }); 69 | byte[] data = packet.getData(); 70 | 71 | log.debug("A1 check sensors received encrypted bytes: " + DatatypeConverter.printHexBinary(data)); 72 | 73 | int err = data[0x22] | (data[0x23] << 8); 74 | 75 | if (err == 0) { 76 | byte[] pl = decryptFromDeviceMessage(data); 77 | log.debug("A1 check sensors received bytes (decrypted):" + DatatypeConverter.printHexBinary(pl)); 78 | 79 | float temp = (float) ((pl[0x4] * 10 + pl[0x5]) / 10.0); 80 | float hum = (float) ((pl[0x6] * 10 + pl[0x7]) / 10.0); 81 | byte light = pl[0x8]; 82 | byte airQuality = pl[0x0a]; 83 | byte noise = pl[0xc]; 84 | 85 | return new EnvData(temp, hum, light, airQuality, noise); 86 | } else { 87 | log.warn("A1 check sensors received an error: " + Integer.toHexString(err) + " / " + err); 88 | return null; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/BLDevice.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi; 30 | 31 | import java.io.Closeable; 32 | import java.io.IOException; 33 | import java.net.DatagramPacket; 34 | import java.net.DatagramSocket; 35 | import java.net.InetAddress; 36 | import java.net.SocketTimeoutException; 37 | import java.util.ArrayList; 38 | import java.util.List; 39 | import java.util.Random; 40 | 41 | import javax.xml.bind.DatatypeConverter; 42 | 43 | import org.slf4j.Logger; 44 | import org.slf4j.LoggerFactory; 45 | 46 | import com.github.mob41.blapi.mac.Mac; 47 | import com.github.mob41.blapi.pkt.CmdPacket; 48 | import com.github.mob41.blapi.pkt.CmdPayload; 49 | import com.github.mob41.blapi.pkt.Packet; 50 | import com.github.mob41.blapi.pkt.auth.AES; 51 | import com.github.mob41.blapi.pkt.auth.AuthCmdPayload; 52 | import com.github.mob41.blapi.pkt.dis.DiscoveryPacket; 53 | 54 | /** 55 | * This is the base class of all Broadlink devices (e.g. SP1, RMPro) 56 | * 57 | * @author Anthony 58 | * 59 | */ 60 | public abstract class BLDevice implements Closeable { 61 | 62 | /** 63 | * The specific logger for this class 64 | */ 65 | protected static final Logger log = LoggerFactory.getLogger(BLDevice.class); 66 | 67 | /** 68 | * Initial key for encryption 69 | */ 70 | public static final byte[] INITIAL_KEY = { 0x09, 0x76, 0x28, 0x34, 0x3f, (byte) 0xe9, (byte) 0x9e, 0x23, 0x76, 0x5c, 71 | 0x15, 0x13, (byte) 0xac, (byte) 0xcf, (byte) 0x8b, 0x02 }; // 16-byte 72 | 73 | /** 74 | * Initial iv for encryption 75 | */ 76 | public static final byte[] INITIAL_IV = { 0x56, 0x2e, 0x17, (byte) 0x99, 0x6d, 0x09, 0x3d, 0x28, (byte) 0xdd, 77 | (byte) 0xb3, (byte) 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58 }; // 16-short 78 | 79 | public static final int DEFAULT_BYTES_SIZE = 0x38; // 56-bytes 80 | 81 | // Devices type HEX 82 | 83 | public static final short DEV_SP1 = 0x0; 84 | 85 | public static final short DEV_SP2 = 0x2711; 86 | 87 | public static final short DEV_SP2_HONEYWELL_ALT1 = 0x2719; 88 | 89 | public static final short DEV_SP2_HONEYWELL_ALT2 = 0x7919; 90 | 91 | public static final short DEV_SP2_HONEYWELL_ALT3 = 0x271a; 92 | 93 | public static final short DEV_SP2_HONEYWELL_ALT4 = 0x791a; 94 | 95 | public static final short DEV_SPMINI = 0x2720; 96 | 97 | public static final short DEV_SP3 = 0x753e; 98 | 99 | public static final short DEV_SPMINI2 = 0x2728; 100 | 101 | public static final short DEV_SPMINI_OEM_ALT1 = 0x2733; 102 | 103 | public static final short DEV_SPMINI_OEM_ALT2 = 0x273e; 104 | 105 | public static final short DEV_SPMINI_PLUS = 0x2736; 106 | 107 | public static final short DEV_RM_2 = 0x2712; 108 | 109 | public static final short DEV_RM_MINI = 0x2737; 110 | 111 | public static final short DEV_RM_MINI_3 = 0x27c2; 112 | 113 | public static final short DEV_RM_PRO_PHICOMM = 0x273d; 114 | 115 | public static final short DEV_RM_2_HOME_PLUS = 0x2783; 116 | 117 | public static final short DEV_RM_2_2HOME_PLUS_GDT = 0x277c; 118 | 119 | public static final short DEV_RM_2_PRO_PLUS = 0x272a; 120 | 121 | public static final short DEV_RM_2_PRO_PLUS_2 = 0x2787; 122 | 123 | public static final short DEV_RM_2_PRO_PLUS_2_BL = 0x278b; 124 | 125 | public static final short DEV_RM_MINI_SHATE = 0x278f; 126 | 127 | public static final short DEV_A1 = 0x2714; 128 | 129 | public static final short DEV_MP1 = 0x4EB5; 130 | 131 | public static final short DEV_HYSEN = 0x4EAD; 132 | 133 | public static final short DEV_FLOUREON = 0xffffffad; 134 | 135 | // 136 | // Friendly device description 137 | // 138 | // Notice: Developers are not recommended to use device description as device identifiers. 139 | // Instead, developers are advised to use Device Type Hex numbers. 140 | 141 | //Unknown 142 | 143 | public static final String DESC_UNKNOWN = "Unknown Device"; 144 | 145 | //RM Series 146 | 147 | public static final String DESC_RM_2 = "RM 2"; 148 | 149 | public static final String DESC_RM_MINI = "RM Mini"; 150 | 151 | public static final String DESC_RM_MINI_3 = "RM Mini 3"; 152 | 153 | public static final String DESC_RM_PRO_PHICOMM = "RM Pro"; 154 | 155 | public static final String DESC_RM_2_HOME_PLUS = "RM 2 Home Plus"; 156 | 157 | public static final String DESC_RM_2_2HOME_PLUS_GDT = "RM 2 Home Plus GDT"; 158 | 159 | public static final String DESC_RM_2_PRO_PLUS = "RM 2 Pro Plus"; 160 | 161 | public static final String DESC_RM_2_PRO_PLUS_2 = "RM 2 Pro Plus 2"; 162 | 163 | public static final String DESC_RM_2_PRO_PLUS_2_BL = "RM 2 Pro Plus 2 BL"; 164 | 165 | public static final String DESC_RM_MINI_SHATE = "RM Mini SHATE"; 166 | 167 | //A Series 168 | 169 | public static final String DESC_A1 = "Environmental Sensor"; 170 | 171 | //MP Series 172 | 173 | public static final String DESC_MP1 = "Power Strip"; 174 | 175 | //SP Series 176 | 177 | public static final String DESC_SP1 = "Smart Plug V1"; 178 | 179 | public static final String DESC_SP2 = "Smart Plug V2"; 180 | 181 | public static final String DESC_SP2_HONEYWELL_ALT1 = "Smart Plug Honeywell Alt 1"; 182 | 183 | public static final String DESC_SP2_HONEYWELL_ALT2 = "Smart Plug Honeywell Alt 2"; 184 | 185 | public static final String DESC_SP2_HONEYWELL_ALT3 = "Smart Plug Honeywell Alt 3"; 186 | 187 | public static final String DESC_SP2_HONEYWELL_ALT4 = "Smart Plug Honeywell Alt 4"; 188 | 189 | public static final String DESC_SPMINI = "Smart Plug Mini"; 190 | 191 | public static final String DESC_SP3 = "Smart Plug V3"; 192 | 193 | public static final String DESC_SPMINI2 = "Smart Plug Mini V2"; 194 | 195 | public static final String DESC_SPMINI_OEM_ALT1 = "Smart Plug OEM Alt 1"; 196 | 197 | public static final String DESC_SPMINI_OEM_ALT2 = "Smart Plug OEM Alt 2"; 198 | 199 | public static final String DESC_SPMINI_PLUS = "Smart Plug Mini Plus"; 200 | 201 | public static final String DESC_HYSEN = "Hysen Thermostat"; 202 | 203 | public static final String DESC_FLOUREON = "Floureon Thermostat"; 204 | /** 205 | * The destination port for discovery broadcasting (from __init__.py) 206 | */ 207 | public static final int DISCOVERY_DEST_PORT = 80; 208 | 209 | /** 210 | * The discovery receive buffer size (from __init__.py) 211 | */ 212 | public static final int DISCOVERY_RECEIVE_BUFFER_SIZE = 0x40; // 64-bytes 213 | 214 | /** 215 | * Default discovery timeout (10 seconds) 216 | */ 217 | public static final int DEFAULT_TIMEOUT = 10000; // 10 seconds (10000 ms) 218 | 219 | /** 220 | * Packet count that is sent by this instance of BLDevice. This is for 221 | * {@link #sendCmdPkt(CmdPayload) sendCmdPkt} method. 222 | */ 223 | private int pktCount; 224 | 225 | /** 226 | * Encryption key. Initialization value is {@link #INITIAL_KEY INITIAL_KEY}. 227 | * This is for {@link #sendCmdPkt(CmdPayload) sendCmdPkt} method. 228 | */ 229 | private byte[] key; 230 | 231 | /** 232 | * Encryption iv. Initialization value is {@link #INITIAL_IV INITIAL_IV}. 233 | * This is for {@link #sendCmdPkt(CmdPayload) sendCmdPkt} method. 234 | */ 235 | private byte[] iv; 236 | 237 | /** 238 | * Device/Client ID. Initialization value is {0,0,0,0}. And it 239 | * is changed after the {@link #auth() auth} method, that Broadlink devices 240 | * will provide a id for this client/device. This is for 241 | * {@link #sendCmdPkt(CmdPayload) sendCmdPkt} method. 242 | */ 243 | private byte[] id; 244 | 245 | /** 246 | * Device type received from discovering devices, or those 247 | * BLDevice.DEV_* constants 248 | */ 249 | private final short deviceType; 250 | 251 | /** 252 | * A friendly description of this device 253 | */ 254 | private final String deviceDesc; 255 | 256 | /** 257 | * Specific datagram socket for this instance, to reuse address. 258 | */ 259 | private DatagramSocket sock; 260 | 261 | /** 262 | * Target device host 263 | */ 264 | private String host; 265 | 266 | /** 267 | * Target device MAC, using {@link com.github.mob41.blapi.mac.Mac} 268 | * implementation to handle MAC addresses 269 | */ 270 | private Mac mac; 271 | 272 | /** 273 | * AES decryption object 274 | */ 275 | private AES aes = null; 276 | 277 | /** 278 | * flag to denote this object alreay authorized. 279 | */ 280 | private boolean alreadyAuthorized; 281 | 282 | /** 283 | * Constructs a BLDevice, with a device type (constants), 284 | * hostname and MAC address 285 | * 286 | * @param deviceType 287 | * Device type constants (BLDevice.DEV_*) 288 | * @param deviceDesc 289 | * Friendly device description 290 | * @param host 291 | * Hostname of target Broadlink device 292 | * @param mac 293 | * MAC address of target Broadlink device 294 | * @throws IOException 295 | * Problems on constructing a datagram socket 296 | */ 297 | protected BLDevice(short deviceType, String deviceDesc, String host, Mac mac) throws IOException { 298 | key = INITIAL_KEY; 299 | iv = INITIAL_IV; 300 | id = new byte[] { 0, 0, 0, 0 }; 301 | 302 | pktCount = new Random().nextInt(0xffff); 303 | 304 | this.deviceType = deviceType; 305 | this.deviceDesc = deviceDesc; 306 | 307 | this.host = host; 308 | this.mac = mac; 309 | 310 | sock = new DatagramSocket(); 311 | sock.setReuseAddress(true); 312 | sock.setBroadcast(true); 313 | aes = new AES(iv, key); 314 | alreadyAuthorized = false; 315 | } 316 | 317 | /** 318 | * Releases the resources of this BLDevice 319 | */ 320 | @Override 321 | public void close() { 322 | sock.close(); 323 | } 324 | 325 | /** 326 | * Returns the device type of this Broadlink device 327 | * 328 | * @return The device type in short 329 | */ 330 | public short getDeviceType() { 331 | return deviceType; 332 | } 333 | 334 | /** 335 | * Returns this Broadlink device's hostname / IP address 336 | * 337 | * @return The hostname / IP address in String 338 | */ 339 | public String getHost() { 340 | return host; 341 | } 342 | 343 | /** 344 | * Returns this Broadlink device's MAC address 345 | * 346 | * @return The MAC address in BLApi's Mac implementation 347 | */ 348 | public Mac getMac() { 349 | return mac; 350 | } 351 | 352 | public AES getAes() { 353 | return aes; 354 | } 355 | 356 | /** 357 | * Returns a friendly description of this BLDevice 358 | * @return a String 359 | */ 360 | public String getDeviceDescription() { 361 | return deviceDesc; 362 | } 363 | 364 | /** 365 | * Compatibility with previous code 366 | * @return Boolean whether this method is success or not 367 | * @throws IOException If I/O goes wrong 368 | */ 369 | public boolean auth() throws IOException { 370 | return auth(false); 371 | } 372 | 373 | /** 374 | * Authenticates with the broadlink device, before any other control 375 | * commands 376 | * @param reauth Setting this to true forces to perform re-auth with the device. Defaults not to perform re-auth. 377 | * @return Boolean whether this method is success or not 378 | * @throws IOException 379 | * If I/O goes wrong 380 | */ 381 | public boolean auth(boolean reauth) throws IOException { 382 | log.debug("auth Authentication method starts"); 383 | if(alreadyAuthorized && !reauth) { 384 | log.debug("auth Already Authorized."); 385 | return true; 386 | } 387 | 388 | AuthCmdPayload sendPayload = new AuthCmdPayload(); 389 | log.debug("auth Sending CmdPacket with AuthCmdPayload: cmd=" + Integer.toHexString(sendPayload.getCommand()) 390 | + " len=" + sendPayload.getPayload().getData().length); 391 | 392 | log.debug("auth AuthPayload initial bytes to send: {}", DatatypeConverter.printHexBinary(sendPayload.getPayload().getData())); 393 | 394 | DatagramPacket recvPack = sendCmdPkt(10000, 2048, sendPayload); 395 | 396 | byte[] data = recvPack.getData(); 397 | 398 | if(data.length <= 0) { 399 | log.error("auth Received 0 bytes on initial request."); 400 | alreadyAuthorized = false; 401 | return false; 402 | } 403 | 404 | log.debug("auth recv encrypted data bytes (" + data.length +") after initial req: {}", DatatypeConverter.printHexBinary(data)); 405 | 406 | byte[] payload = null; 407 | try { 408 | log.debug("auth Decrypting encrypted data"); 409 | 410 | payload = decryptFromDeviceMessage(data); 411 | 412 | log.debug("auth Decrypted. len=" + payload.length); 413 | 414 | } catch (Exception e) { 415 | log.error("auth Received datagram decryption error. Aborting method", e); 416 | alreadyAuthorized = false; 417 | return false; 418 | } 419 | 420 | log.debug("auth Packet received payload bytes: " + DatatypeConverter.printHexBinary(payload)); 421 | 422 | key = subbytes(payload, 0x04, 0x14); 423 | 424 | log.debug("auth Packet received key bytes: " + DatatypeConverter.printHexBinary(key)); 425 | 426 | if (key.length % 16 != 0) { 427 | log.error("auth Received key len is not a multiple of 16! Aborting"); 428 | alreadyAuthorized = false; 429 | return false; 430 | } 431 | 432 | // recreate AES object with new key 433 | aes = new AES(iv, key); 434 | 435 | id = subbytes(payload, 0x00, 0x04); 436 | 437 | log.debug("auth Packet received id bytes: " + DatatypeConverter.printHexBinary(id) + " with ID len=" + id.length); 438 | 439 | log.debug("auth End of authentication method"); 440 | alreadyAuthorized = true; 441 | 442 | return true; 443 | } 444 | 445 | /** 446 | * Sends a command packet from localhost to Broadlink device, with buffer 447 | * size 1024 bytes, 10 seconds timeout
448 | *
449 | * Before any commands to be sent to the device, {@link #auth() auth} must 450 | * be ran first in order to authenticate with the device and gain a device 451 | * ID, encryption key and IV. 452 | * 453 | * @param cmdPayload 454 | * Command data to be sent 455 | * @return {@link DatagramPacket} containing the byte data and sender host 456 | * information. 457 | * @throws IOException 458 | * Problems when sending the packet 459 | */ 460 | public DatagramPacket sendCmdPkt(CmdPayload cmdPayload) throws IOException { 461 | return sendCmdPkt(10000, cmdPayload); 462 | } 463 | 464 | /** 465 | * Sends a command packet from localhost to Broadlink device, with default 466 | * buffer size 1024 bytes
467 | *
468 | * Before any commands to be sent to the device, {@link #auth() auth} must 469 | * be ran first in order to authenticate with the device and gain a device 470 | * ID, encryption key and IV. 471 | * 472 | * @param timeout 473 | * Socket read timeout 474 | * @param cmdPayload 475 | * Command data to be sent 476 | * @return {@link DatagramPacket} containing the byte data and sender host 477 | * information. 478 | * @throws IOException 479 | * Problems when sending the packet 480 | */ 481 | public DatagramPacket sendCmdPkt(int timeout, CmdPayload cmdPayload) throws IOException { 482 | return sendCmdPkt(InetAddress.getLocalHost(), 0, timeout, 1024, cmdPayload); 483 | } 484 | 485 | /** 486 | * Sends a command packet from localhost to Broadlink device
487 | *
488 | * Before any commands to be sent to the device, {@link #auth() auth} must 489 | * be ran first in order to authenticate with the device and gain a device 490 | * ID, encryption key and IV. 491 | * 492 | * @param timeout 493 | * Socket read timeout 494 | * @param bufSize 495 | * Receive datagram buffer size 496 | * @param cmdPayload 497 | * Command data to be sent 498 | * @return {@link DatagramPacket} containing the byte data and sender host 499 | * information. 500 | * @throws IOException 501 | * Problems when sending the packet 502 | */ 503 | public DatagramPacket sendCmdPkt(int timeout, int bufSize, CmdPayload cmdPayload) throws IOException { 504 | return sendCmdPkt(InetAddress.getLocalHost(), 0, timeout, bufSize, cmdPayload); 505 | } 506 | 507 | /** 508 | * Binds to a specific IP address and sends a command packet to Broadlink 509 | * device
510 | *
511 | * Before any commands to be sent to the device, {@link #auth() auth} must 512 | * be ran first in order to authenticate with the device and gain a device 513 | * ID, encryption key and IV. 514 | * 515 | * @param sourceIpAddr 516 | * Bind the socket to this IP address 517 | * @param sourcePort 518 | * Bind the socket to this port 519 | * @param timeout 520 | * Socket read timeout 521 | * @param bufSize 522 | * Receive datagram buffer size 523 | * @param cmdPayload 524 | * Command data to be sent 525 | * @return {@link DatagramPacket} containing the byte data and sender host 526 | * information. 527 | * @throws IOException 528 | * Problems when sending the packet 529 | */ 530 | public DatagramPacket sendCmdPkt(InetAddress sourceIpAddr, int sourcePort, int timeout, int bufSize, 531 | CmdPayload cmdPayload) throws IOException { 532 | CmdPacket cmdPkt = new CmdPacket(mac, pktCount++, id, aes, cmdPayload); 533 | log.debug("sendCmdPkt - Send Command Packet bytes: {}", DatatypeConverter.printHexBinary(cmdPkt.getData())); 534 | return sendPkt(sock, cmdPkt, InetAddress.getByName(host), 80, timeout, bufSize); 535 | } 536 | 537 | /** 538 | * Creates a Broadlink device client 539 | * 540 | * @param deviceType 541 | * Device type constant (BLDevice.DEV_*) 542 | * @param host 543 | * Target Broadlink device hostname 544 | * @param mac 545 | * Target Broadlink device MAC address 546 | * @return A BLDevice client 547 | * @throws IOException 548 | * Problems when constucting a datagram socket 549 | */ 550 | public static BLDevice createInstance(short deviceType, String host, Mac mac) throws IOException { 551 | String desc = BLDevice.getDescOfType(deviceType); 552 | switch (deviceType) { 553 | case DEV_SP1: 554 | return new SP1Device(host, mac); 555 | case DEV_SP2: 556 | case DEV_SP2_HONEYWELL_ALT1: 557 | case DEV_SP2_HONEYWELL_ALT2: 558 | case DEV_SP2_HONEYWELL_ALT3: 559 | case DEV_SP2_HONEYWELL_ALT4: 560 | case DEV_SPMINI: 561 | case DEV_SP3: 562 | case DEV_SPMINI2: 563 | case DEV_SPMINI_OEM_ALT1: 564 | case DEV_SPMINI_OEM_ALT2: 565 | case DEV_SPMINI_PLUS: 566 | return new SP2Device(deviceType, desc, host, mac); 567 | case DEV_RM_2: 568 | case DEV_RM_MINI: 569 | case DEV_RM_MINI_3: 570 | return new RM2Device(deviceType, desc, host, mac); 571 | case DEV_RM_PRO_PHICOMM: 572 | case DEV_RM_2_HOME_PLUS: 573 | case DEV_RM_2_2HOME_PLUS_GDT: 574 | case DEV_RM_2_PRO_PLUS: 575 | case DEV_RM_2_PRO_PLUS_2: 576 | case DEV_RM_2_PRO_PLUS_2_BL: 577 | case DEV_RM_MINI_SHATE: 578 | return new RM2Device(deviceType, desc, host, mac); 579 | case DEV_A1: 580 | return new A1Device(host, mac); 581 | case DEV_MP1: 582 | return new MP1Device(host, mac); 583 | case DEV_FLOUREON: 584 | return new FloureonDevice(host, mac); 585 | case DEV_HYSEN: 586 | return new HysenDevice(host, mac); 587 | } 588 | return null; 589 | } 590 | 591 | /** 592 | * Discover Broadlink devices in the local network, with 593 | * {@link #DEFAULT_TIMEOUT default timeout} 594 | * 595 | * @return An array of BLDevice in the network 596 | * @throws IOException 597 | * Problems when discovering 598 | */ 599 | public static BLDevice[] discoverDevices() throws IOException { 600 | return discoverDevices(DEFAULT_TIMEOUT); 601 | } 602 | 603 | /** 604 | * Discover Broadlink devices in the local network 605 | * 606 | * @param timeout 607 | * Socket read timeout 608 | * @return An array of BLDevice in the network 609 | * @throws IOException 610 | * Problems when discovering 611 | */ 612 | public static BLDevice[] discoverDevices(int timeout) throws IOException { 613 | return discoverDevices(InetAddress.getLocalHost(), 0, timeout); 614 | } 615 | 616 | /** 617 | * Discover Broadlink devices in the network, binded with a specific IP 618 | * address 619 | * 620 | * @param sourceIpAddr 621 | * The IP address to be binded 622 | * @param sourcePort 623 | * The port to be binded 624 | * @param timeout 625 | * Socket read timeout 626 | * @return An array of BLDevice in the network 627 | * @throws IOException 628 | * Problems when discovering 629 | */ 630 | public static BLDevice[] discoverDevices(InetAddress sourceIpAddr, int sourcePort, int timeout) throws IOException { 631 | boolean debug = log.isDebugEnabled(); 632 | 633 | if (debug) 634 | log.debug("Discovering devices"); 635 | 636 | List devices = new ArrayList(50); 637 | 638 | if (debug) 639 | log.debug("Constructing DiscoveryPacket"); 640 | 641 | DiscoveryPacket dpkt = new DiscoveryPacket(sourceIpAddr, sourcePort); 642 | 643 | DatagramSocket sock = new DatagramSocket(sourcePort, sourceIpAddr); 644 | 645 | sock.setBroadcast(true); 646 | sock.setReuseAddress(true); 647 | 648 | byte[] sendBytes = dpkt.getData(); 649 | DatagramPacket sendpack = new DatagramPacket(sendBytes, sendBytes.length, 650 | InetAddress.getByName("255.255.255.255"), DISCOVERY_DEST_PORT); 651 | 652 | if (debug) 653 | log.debug("Sending broadcast"); 654 | 655 | sock.send(sendpack); 656 | 657 | byte[] receBytes = new byte[DISCOVERY_RECEIVE_BUFFER_SIZE]; 658 | 659 | DatagramPacket recePacket = new DatagramPacket(receBytes, 0, receBytes.length); 660 | if (timeout == 0) { 661 | if (debug) 662 | log.debug("No timeout was set. Blocking thread until received"); 663 | log.debug("Waiting for datagrams"); 664 | 665 | sock.receive(recePacket); 666 | 667 | if (debug) 668 | log.debug("Received. Closing socket"); 669 | 670 | sock.close(); 671 | 672 | String host = recePacket.getAddress().getHostAddress(); 673 | Mac mac = new Mac(subbytes(receBytes, 0x3a, 0x40)); 674 | short deviceType = (short) (receBytes[0x34] | receBytes[0x35] << 8); 675 | 676 | if (debug) 677 | log.debug("Info: host=" + host + " mac=" + mac.getMacString() + " deviceType=0x" 678 | + Integer.toHexString(deviceType)); 679 | log.debug("Creating BLDevice instance"); 680 | 681 | BLDevice inst = createInstance(deviceType, host, mac); 682 | 683 | if (inst != null) { 684 | if (debug) 685 | log.debug("Adding to found devices list"); 686 | 687 | devices.add(inst); 688 | } else if (debug) { 689 | log.debug("Cannot create instance, returned null, not adding to found devices list"); 690 | } 691 | } else { 692 | if (debug) 693 | log.debug("A timeout of " + timeout + " ms was set. Running loop"); 694 | 695 | long startTime = System.currentTimeMillis(); 696 | long elapsed; 697 | while ((elapsed = System.currentTimeMillis() - startTime) < timeout) { 698 | if (debug) 699 | log.debug("Elapsed: " + elapsed + " ms"); 700 | log.debug("Socket timeout: timeout-elapsed=" + (timeout - elapsed)); 701 | 702 | sock.setSoTimeout((int) (timeout - elapsed)); 703 | 704 | try { 705 | if (debug) 706 | log.debug("Waiting for datagrams"); 707 | 708 | sock.receive(recePacket); 709 | } catch (SocketTimeoutException e) { 710 | if (debug) 711 | log.debug("Socket timed out for " + (timeout - elapsed) + " ms", e); 712 | 713 | break; 714 | } 715 | 716 | if (debug) 717 | log.debug("Received datagram"); 718 | 719 | String host = recePacket.getAddress().getHostAddress(); 720 | Mac mac = new Mac(reverseBytes(subbytes(receBytes, 0x3a, 0x40))); 721 | short deviceType = (short) (receBytes[0x34] | receBytes[0x35] << 8); 722 | 723 | if (debug) 724 | log.debug("Info: host=" + host + " mac=" + mac.getMacString() + " deviceType=0x" 725 | + Integer.toHexString(deviceType)); 726 | log.debug("Creating BLDevice instance"); 727 | 728 | BLDevice inst = createInstance(deviceType, host, mac); 729 | 730 | if (inst != null) { 731 | if (debug) 732 | log.debug("Adding to found devices list"); 733 | 734 | devices.add(inst); 735 | } else if (debug) { 736 | log.debug("Cannot create instance, returned null, not adding to found devices list"); 737 | } 738 | } 739 | } 740 | 741 | if (debug) 742 | log.debug("Converting list to array: " + devices.size()); 743 | 744 | BLDevice[] out = new BLDevice[devices.size()]; 745 | 746 | for (int i = 0; i < out.length; i++) { 747 | out[i] = devices.get(i); 748 | } 749 | 750 | if (debug) 751 | log.debug("End of device discovery: " + out.length); 752 | 753 | sock.close(); 754 | 755 | return out; 756 | } 757 | 758 | public static String getDescOfType(short devType){ 759 | switch (devType) { 760 | 761 | // 762 | // RM Series 763 | // 764 | 765 | case BLDevice.DEV_RM_2: 766 | return DESC_RM_2; 767 | case BLDevice.DEV_RM_MINI: 768 | return DESC_RM_MINI; 769 | case BLDevice.DEV_RM_MINI_3: 770 | return DESC_RM_MINI_3; 771 | case BLDevice.DEV_RM_PRO_PHICOMM: 772 | return DESC_RM_PRO_PHICOMM; 773 | case BLDevice.DEV_RM_2_HOME_PLUS: 774 | return DESC_RM_2_HOME_PLUS; 775 | case BLDevice.DEV_RM_2_2HOME_PLUS_GDT: 776 | return DESC_RM_2_2HOME_PLUS_GDT; 777 | case BLDevice.DEV_RM_2_PRO_PLUS: 778 | return DESC_RM_2_PRO_PLUS; 779 | case BLDevice.DEV_RM_2_PRO_PLUS_2: 780 | return DESC_RM_2_PRO_PLUS_2; 781 | case BLDevice.DEV_RM_2_PRO_PLUS_2_BL: 782 | return DESC_RM_2_PRO_PLUS_2_BL; 783 | case BLDevice.DEV_RM_MINI_SHATE: 784 | return DESC_RM_MINI_SHATE; 785 | 786 | // 787 | // SP2 Series 788 | // 789 | 790 | case BLDevice.DEV_SP2: 791 | return DESC_SP2; 792 | case BLDevice.DEV_SP2_HONEYWELL_ALT1: 793 | return DESC_SP2_HONEYWELL_ALT1; 794 | case BLDevice.DEV_SP2_HONEYWELL_ALT2: 795 | return DESC_SP2_HONEYWELL_ALT2; 796 | case BLDevice.DEV_SP2_HONEYWELL_ALT3: 797 | return DESC_SP2_HONEYWELL_ALT3; 798 | case BLDevice.DEV_SP2_HONEYWELL_ALT4: 799 | return DESC_SP2_HONEYWELL_ALT4; 800 | case BLDevice.DEV_SP3: 801 | return DESC_SP3; 802 | case BLDevice.DEV_SPMINI: 803 | return DESC_SPMINI; 804 | case BLDevice.DEV_SPMINI2: 805 | return DESC_SPMINI2; 806 | case BLDevice.DEV_SPMINI_OEM_ALT1: 807 | return DESC_SPMINI_OEM_ALT1; 808 | case BLDevice.DEV_SPMINI_OEM_ALT2: 809 | return DESC_SPMINI_OEM_ALT2; 810 | case BLDevice.DEV_SPMINI_PLUS: 811 | return DESC_SPMINI_PLUS; 812 | 813 | case BLDevice.DEV_SP1: 814 | return BLDevice.DESC_SP1; 815 | case BLDevice.DEV_MP1: 816 | return BLDevice.DESC_MP1; 817 | case BLDevice.DEV_A1: 818 | return BLDevice.DESC_A1; 819 | case BLDevice.DEV_HYSEN: 820 | return BLDevice.DESC_HYSEN; 821 | case BLDevice.DEV_FLOUREON: 822 | return BLDevice.DESC_FLOUREON; 823 | // 824 | // Unregonized 825 | // 826 | default: 827 | return DESC_UNKNOWN; 828 | } 829 | } 830 | 831 | /** 832 | * Misc: Reverse the byte array 833 | * 834 | * @param data 835 | * Original data 836 | * @return Result byte array 837 | */ 838 | public static byte[] reverseBytes(byte[] data) { 839 | byte[] out = new byte[data.length]; 840 | 841 | for (int i = 0; i < out.length; i++) { 842 | out[i] = data[data.length - 1 - i]; 843 | } 844 | 845 | return out; 846 | } 847 | 848 | /** 849 | * Misc: Pull bytes out from end of array until a non null is detected 850 | * 851 | * @param data 852 | * Original data 853 | * @param offset 854 | * Starting offset 855 | * @return Result byte array 856 | */ 857 | public static byte[] removeNullsFromEnd(byte[] data, int offset) { 858 | int new_length = 0; 859 | for (int i = data.length - 1; i >= offset; i--) { 860 | if (data[i] != 0x00) { // null 861 | new_length = i + 1; 862 | break; 863 | } 864 | } 865 | 866 | byte[] out = new byte[new_length]; 867 | 868 | for (int x = offset; x < new_length; x++) { 869 | out[x - offset] = data[x]; 870 | } 871 | 872 | return out; 873 | } 874 | 875 | /** 876 | * Misc: Pull bytes out from an array until a NULL (0) is detected 877 | * 878 | * @param data 879 | * Original data 880 | * @param offset 881 | * Starting offset 882 | * @return Result byte array 883 | */ 884 | public static byte[] subbytesTillNull(byte[] data, int offset) { 885 | int new_length = 0; 886 | for (int i = offset; i < data.length; i++) { 887 | if (data[i] == 0x00) { // null 888 | new_length = i; 889 | break; 890 | } 891 | } 892 | 893 | byte[] out = new byte[new_length]; 894 | 895 | for (int x = offset; x < new_length; x++) { 896 | out[x - offset] = data[x]; 897 | } 898 | 899 | return out; 900 | } 901 | 902 | /** 903 | * Get Payload without header and padded for decryption. 904 | * 905 | * @param data the encrypted data message from the device and includes the header 906 | * @return Payload bytes without the header and padded to modulo 16 907 | */ 908 | public byte[] getRawPayloadBytesPadded(byte[] data) { 909 | byte[] encData = subbytes(data, BLDevice.DEFAULT_BYTES_SIZE, data.length); 910 | byte[] newBytes = null; 911 | if(encData.length > 0) { 912 | int numpad = 16 - (encData.length % 16); 913 | 914 | newBytes = new byte[encData.length+numpad]; 915 | for(int i = 0; i < newBytes.length; i++) { 916 | if(i < encData.length) 917 | newBytes[i] = encData[i]; 918 | else 919 | newBytes[i] = 0x00; 920 | } 921 | } 922 | return newBytes; 923 | } 924 | 925 | protected byte[] decryptFromDeviceMessage(byte[] encData) throws Exception { 926 | byte[] encPL = getRawPayloadBytesPadded(encData); 927 | byte[] pl = aes.decrypt(encPL); 928 | 929 | return pl; 930 | } 931 | /** 932 | * Picks bytes from start-set to the end-set in a bytes array 933 | * 934 | * @param data 935 | * The bytes array to be used 936 | * @param start 937 | * The starting position to be picked 938 | * @param end 939 | * The ending position to be picked 940 | * @return The bytes array picked with length (end - start) 941 | */ 942 | public static byte[] subbytes(byte[] data, int start, int end) { 943 | byte[] out = new byte[end - start]; 944 | 945 | int outi = 0; 946 | for (int i = start; i < end; i++, outi++) { 947 | out[outi] = data[i]; 948 | } 949 | 950 | return out; 951 | } 952 | 953 | /** 954 | * Sends a compiled packet to a destination host and port, and receives a 955 | * datagram from the source port specified. 956 | * 957 | * @param pkt 958 | * The compiled packet to be sent 959 | * @param sourceIpAddr 960 | * Source IP address to be binded for receiving datagrams 961 | * @param sourcePort 962 | * Source Port to be bineded for receiving datagrams 963 | * @param destIpAddr 964 | * Destination IP address 965 | * @param destPort 966 | * Destination Port 967 | * @param timeout 968 | * Socket timeout. 0 will disable the timeout 969 | * @param bufSize 970 | * Receiving datagram's buffer size 971 | * @return The received datagram 972 | * @throws IOException 973 | * Thrown if socket timed out, cannot bind source IP and source 974 | * port, no permission, etc. 975 | */ 976 | public static DatagramPacket sendPkt(Packet pkt, InetAddress sourceIpAddr, int sourcePort, InetAddress destIpAddr, 977 | int destPort, int timeout, int bufSize) throws IOException { 978 | log.debug("sendPkt - call with create socket for: " + sourceIpAddr.getHostAddress() + " and port " + sourcePort); 979 | DatagramSocket sock = new DatagramSocket(sourcePort, sourceIpAddr); 980 | 981 | sock.setBroadcast(true); 982 | sock.setReuseAddress(true); 983 | 984 | DatagramPacket recePkt = sendPkt(sock, pkt, destIpAddr, destPort, timeout, bufSize); 985 | sock.close(); 986 | 987 | return recePkt; 988 | } 989 | 990 | /** 991 | * Sends a compiled packet to a destination host and port, and receives a 992 | * datagram from the source port specified. 993 | * 994 | * @param sock 995 | * Uses an external socket 996 | * @param pkt 997 | * The compiled packet to be sent 998 | * @param destIpAddr 999 | * Destination IP address 1000 | * @param destPort 1001 | * Destination Port 1002 | * @param timeout 1003 | * Socket timeout. 0 will disable the timeout 1004 | * @param bufSize 1005 | * Receiving datagram's buffer size 1006 | * @return The received datagram 1007 | * @throws IOException 1008 | * Thrown if socket timed out, cannot bind source IP and source 1009 | * port, no permission, etc. 1010 | */ 1011 | public static DatagramPacket sendPkt(DatagramSocket sock, Packet pkt, InetAddress destIpAddr, int destPort, int timeout, int bufSize) throws IOException { 1012 | 1013 | String boundHost = null; 1014 | if(sock.getInetAddress() == null) 1015 | boundHost = "0.0.0.0"; 1016 | else 1017 | boundHost = sock.getInetAddress().getHostAddress(); 1018 | log.debug("sendPkt - call with given sock for " + boundHost + " and port " + sock.getPort()); 1019 | 1020 | byte[] data = pkt.getData(); 1021 | DatagramPacket sendpack = new DatagramPacket(data, data.length, destIpAddr, destPort); 1022 | log.debug("snedPkt - data for length: " + data.length + " to: " + sendpack.getAddress().getHostAddress() + " for port: " + sendpack.getPort()); 1023 | 1024 | byte[] rece = new byte[bufSize]; 1025 | DatagramPacket recepack = new DatagramPacket(rece, 0, rece.length); 1026 | 1027 | long startTime = System.currentTimeMillis(); 1028 | long elapsed; 1029 | while ((elapsed = System.currentTimeMillis() - startTime) < timeout) { 1030 | try { 1031 | sock.send(sendpack); 1032 | sock.setSoTimeout(1000); 1033 | sock.receive(recepack); 1034 | break; 1035 | } catch (SocketTimeoutException e) { 1036 | if (elapsed > timeout) { 1037 | break; 1038 | } 1039 | 1040 | continue; 1041 | } 1042 | } 1043 | 1044 | log.debug("sendPkt - recv data bytes (" + recepack.getData().length +") after initial req: {}", DatatypeConverter.printHexBinary(recepack.getData())); 1045 | recepack.setData(removeNullsFromEnd(recepack.getData(), 0)); 1046 | return recepack; 1047 | } 1048 | 1049 | public static byte[] chgLen(byte[] data, int newLen) { 1050 | byte[] newBytes = new byte[newLen]; 1051 | for (int i = 0; i < data.length; i++) { 1052 | newBytes[i] = data[i]; 1053 | } 1054 | return newBytes; 1055 | } 1056 | } 1057 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/EnvData.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi; 30 | 31 | public class EnvData { 32 | 33 | public static final byte LIGHT_DARK = 0x00; 34 | 35 | public static final byte LIGHT_DIM = 0x01; 36 | 37 | public static final byte LIGHT_NORMAL = 0x02; 38 | 39 | public static final byte LIGHT_BRIGHT = 0x03; 40 | 41 | public static final byte AIR_QUALITY_EXCELLENT = 0x00; 42 | 43 | public static final byte AIR_QUALITY_GOOD = 0x01; 44 | 45 | public static final byte AIR_QUALITY_NORMAL = 0x02; 46 | 47 | public static final byte AIR_QUALITY_BAD = 0x03; 48 | 49 | public static final byte NOISE_QUIET = 0x00; 50 | 51 | public static final byte NOISE_NORMAL = 0x01; 52 | 53 | public static final byte NOISE_NOISY = 0x02; 54 | 55 | private final float temp; 56 | 57 | private final float humidity; 58 | 59 | private final byte light; 60 | 61 | private final byte airQuality; 62 | 63 | private final byte noise; 64 | 65 | protected EnvData(float temp, float hum, byte light, byte airQuality, byte noise){ 66 | this.temp = temp; 67 | this.humidity = hum; 68 | this.light = light; 69 | this.airQuality = airQuality; 70 | this.noise = noise; 71 | } 72 | 73 | public float getTemp() { 74 | return temp; 75 | } 76 | 77 | public float getHumidity() { 78 | return humidity; 79 | } 80 | 81 | public byte getLight() { 82 | return light; 83 | } 84 | 85 | public String getLightDescription(){ 86 | switch (light){ 87 | case LIGHT_DARK: 88 | return "dark"; 89 | case LIGHT_DIM: 90 | return "dim"; 91 | case LIGHT_NORMAL: 92 | return "normal"; 93 | case LIGHT_BRIGHT: 94 | return "bright"; 95 | default: 96 | return "unknown"; 97 | } 98 | } 99 | 100 | public byte getAirQuality() { 101 | return airQuality; 102 | } 103 | 104 | public String getAirQualityDescription(){ 105 | switch (light){ 106 | case AIR_QUALITY_EXCELLENT: 107 | return "excellent"; 108 | case AIR_QUALITY_GOOD: 109 | return "good"; 110 | case AIR_QUALITY_NORMAL: 111 | return "normal"; 112 | case AIR_QUALITY_BAD: 113 | return "bad"; 114 | default: 115 | return "unknown"; 116 | } 117 | } 118 | 119 | public byte getNoise() { 120 | return noise; 121 | } 122 | 123 | public String getNoiseDescription(){ 124 | switch (light){ 125 | case NOISE_QUIET: 126 | return "quiet"; 127 | case NOISE_NORMAL: 128 | return "normal"; 129 | case NOISE_NOISY: 130 | return "noisy"; 131 | default: 132 | return "unknown"; 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/FloureonDevice.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | 30 | package com.github.mob41.blapi; 31 | 32 | import java.io.IOException; 33 | 34 | import com.github.mob41.blapi.dev.hysen.BaseHysenDevice; 35 | import com.github.mob41.blapi.mac.Mac; 36 | 37 | public class FloureonDevice extends BaseHysenDevice { 38 | 39 | /** 40 | * Creates a FloureonDevice client instance 41 | * 42 | * @param host The target Broadlink hostname 43 | * @param mac The target Broadlink MAC address 44 | * @throws IOException Problems on constructing socket 45 | */ 46 | public FloureonDevice(String host, Mac mac) throws IOException { 47 | super(BLDevice.DEV_FLOUREON, BLDevice.DESC_FLOUREON, host, mac); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/HysenDevice.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | 30 | package com.github.mob41.blapi; 31 | 32 | import java.io.IOException; 33 | 34 | import com.github.mob41.blapi.dev.hysen.BaseHysenDevice; 35 | import com.github.mob41.blapi.mac.Mac; 36 | 37 | public class HysenDevice extends BaseHysenDevice { 38 | 39 | /** 40 | * Creates a HysenDevice client instance 41 | * 42 | * @param host The target Broadlink hostname 43 | * @param mac The target Broadlink MAC address 44 | * @throws IOException Problems on constructing socket 45 | */ 46 | public HysenDevice(String host, Mac mac) throws IOException { 47 | super(BLDevice.DEV_HYSEN, BLDevice.DESC_HYSEN, host, mac); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/MP1Device.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | 30 | package com.github.mob41.blapi; 31 | 32 | import java.io.IOException; 33 | import java.net.DatagramPacket; 34 | 35 | import javax.xml.bind.DatatypeConverter; 36 | 37 | import com.github.mob41.blapi.mac.Mac; 38 | import com.github.mob41.blapi.pkt.CmdPayload; 39 | import com.github.mob41.blapi.pkt.Payload; 40 | 41 | public class MP1Device extends BLDevice { 42 | /** 43 | * Generic way to create a MP1Device 44 | * @param deviceType Device Type 45 | * @param deviceDesc Friendly device description 46 | * @param host The target Broadlink hostname 47 | * @param mac The target Broadlink MAC address 48 | * @throws IOException Problems on constructing socket 49 | */ 50 | protected MP1Device(short deviceType, String deviceDesc, String host, Mac mac) throws IOException{ 51 | super(deviceType, deviceDesc, host, mac); 52 | } 53 | 54 | /** 55 | * Creates a MP1Device client instance 56 | * 57 | * @param host 58 | * The target Broadlink hostname 59 | * @param mac 60 | * The target Broadlink MAC address 61 | * @throws IOException 62 | * Problems on constructing socket 63 | */ 64 | public MP1Device(String host, Mac mac) throws IOException { 65 | super(BLDevice.DEV_MP1, BLDevice.DESC_MP1, host, mac); 66 | } 67 | 68 | /** 69 | * Set the power state 70 | * @param sid The SID 71 | * @param state Power State 72 | * @throws Exception 73 | */ 74 | public void setState(int sid, boolean state) throws Exception { 75 | int sid_mask = 0x01 << (sid - 1); 76 | setStateMask(sid_mask, state); 77 | } 78 | 79 | private void setStateMask(final int sid_mask, final boolean state) throws Exception { 80 | // """Sets the power state of the smart power strip.""" 81 | DatagramPacket packet = sendCmdPkt(new CmdPayload() { 82 | 83 | @Override 84 | public byte getCommand() { 85 | return 0x6a; 86 | } 87 | 88 | @Override 89 | public Payload getPayload() { 90 | return new Payload() { 91 | 92 | @Override 93 | public byte[] getData() { 94 | byte[] b = new byte[16]; 95 | b[0x00] = (byte) (0x0d); 96 | b[0x02] = (byte) (0xa5); 97 | b[0x03] = (byte) (0xa5); 98 | b[0x04] = (byte) (0x5a); 99 | b[0x05] = (byte) (0x5a); 100 | b[0x06] = (byte) (0xb2 + (state ? (sid_mask << 1) : sid_mask)); 101 | b[0x07] = (byte) (0xc0); 102 | b[0x08] = (byte) (0x02); 103 | b[0x0a] = (byte) (0x03); 104 | b[0x0d] = (byte) (sid_mask); 105 | b[0x0e] = (byte) (state ? sid_mask : 0); 106 | return b; 107 | } 108 | 109 | }; 110 | } 111 | 112 | }); 113 | 114 | byte[] data = packet.getData(); 115 | 116 | int err = data[0x22] | (data[0x23] << 8); 117 | 118 | if (err == 0) { 119 | log.debug("MP1 set state mask received encrypted bytes: " + DatatypeConverter.printHexBinary(data)); 120 | } else { 121 | log.warn("MP1 set state mask received returned err: " + Integer.toHexString(err) + " / " + err); 122 | } 123 | } 124 | 125 | public boolean getStateByIndex(int index) throws Exception{ 126 | return getStates()[index]; 127 | } 128 | 129 | public boolean[] getStates() throws Exception { 130 | // """Returns the power state of the smart power strip.""" 131 | byte state = getStatesRaw(); 132 | boolean[] data = new boolean[4]; 133 | data[0] = ((state & 0x01) != 0) ? true : false; 134 | data[1] = ((state & 0x02) != 0) ? true : false; 135 | data[2] = ((state & 0x04) != 0) ? true : false; 136 | data[3] = ((state & 0x08) != 0) ? true : false; 137 | return data; 138 | } 139 | 140 | private byte getStatesRaw() throws Exception { 141 | // """Returns the power state of the smart power strip in raw format.""" 142 | DatagramPacket packet = sendCmdPkt(new CmdPayload() { 143 | 144 | @Override 145 | public byte getCommand() { 146 | return 0x6a; 147 | } 148 | 149 | @Override 150 | public Payload getPayload() { 151 | return new Payload() { 152 | 153 | @Override 154 | public byte[] getData() { 155 | byte[] b = new byte[16]; 156 | b[0x00] = (byte) (0x0a); 157 | b[0x02] = (byte) (0xa5); 158 | b[0x03] = (byte) (0xa5); 159 | b[0x04] = (byte) (0x5a); 160 | b[0x05] = (byte) (0x5a); 161 | b[0x06] = (byte) (0xae); 162 | b[0x07] = (byte) (0xc0); 163 | b[0x08] = (byte) (0x01); 164 | return b; 165 | } 166 | 167 | }; 168 | } 169 | 170 | }); 171 | 172 | byte[] data = packet.getData(); 173 | 174 | log.debug("MP1 get states raw received encrypted bytes: " + DatatypeConverter.printHexBinary(data)); 175 | 176 | int err = data[0x22] | (data[0x23] << 8); 177 | 178 | if (err == 0) { 179 | byte[] pl = decryptFromDeviceMessage(data); 180 | log.debug("MP1 get states raw received bytes (decrypted): " + DatatypeConverter.printHexBinary(pl)); 181 | byte state = 0; 182 | if (pl[0x3c] >= 48 && pl[0x3c] <= 57) { 183 | String decodeValue1; 184 | decodeValue1 = String.valueOf(pl[0x46]); 185 | state = Short.decode(decodeValue1).byteValue(); 186 | } else { 187 | state = pl[0x46]; 188 | } 189 | return state; 190 | } else { 191 | log.warn("MP1 get states raw received an error: " + Integer.toHexString(err) + " / " + err); 192 | } 193 | return 0; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/RM2Device.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi; 30 | 31 | import java.io.IOException; 32 | import java.net.DatagramPacket; 33 | 34 | import javax.xml.bind.DatatypeConverter; 35 | 36 | import com.github.mob41.blapi.mac.Mac; 37 | import com.github.mob41.blapi.pkt.cmd.rm2.CheckDataCmdPayload; 38 | import com.github.mob41.blapi.pkt.cmd.rm2.EnterLearnCmdPayload; 39 | import com.github.mob41.blapi.pkt.cmd.rm2.RMTempCmdPayload; 40 | 41 | /** 42 | * Broadlink RM2 device client 43 | * 44 | * @author Anthony 45 | * 46 | */ 47 | public class RM2Device extends BLDevice { 48 | 49 | /** 50 | * Generic way to create a RM2Device 51 | * @param deviceType Device Type 52 | * @param deviceDesc Friendly device description 53 | * @param host The target Broadlink hostname 54 | * @param mac The target Broadlink MAC address 55 | * @throws IOException Problems on constructing socket 56 | */ 57 | protected RM2Device(short deviceType, String deviceDesc, String host, Mac mac) throws IOException{ 58 | super(deviceType, deviceDesc, host, mac); 59 | } 60 | 61 | /** 62 | * Creates a RM2Device client instance 63 | * 64 | * @param host 65 | * The target Broadlink hostname 66 | * @param mac 67 | * The target Broadlink MAC address 68 | * @throws IOException 69 | * Problems on constructing socket 70 | */ 71 | public RM2Device(String host, Mac mac) throws IOException { 72 | super(BLDevice.DEV_RM_2, BLDevice.DESC_RM_2, host, mac); 73 | } 74 | 75 | /** 76 | * Requests the RM2 to return the learned data
77 | *
78 | * The {@link #auth() auth()} method must be ran before these commands 79 | * 80 | * @return Result whether the command is successfully sent. 81 | * @throws IOException 82 | * Problems on sending packet 83 | */ 84 | public byte[] checkData() throws Exception { 85 | CheckDataCmdPayload cmdPayload = new CheckDataCmdPayload(); 86 | DatagramPacket packet = sendCmdPkt(10000, cmdPayload); 87 | byte[] data = packet.getData(); 88 | 89 | int err = data[0x22] | (data[0x23] << 8); 90 | 91 | log.debug("RM2 check data received encrypted bytes: " + DatatypeConverter.printHexBinary(data)); 92 | 93 | 94 | if (err == 0) { 95 | byte[] encData = decryptFromDeviceMessage(data); 96 | 97 | return subbytes(encData, 0x04, encData.length); 98 | } 99 | 100 | log.warn("RM2 check data received error: " + Integer.toHexString(err) + " / " + err); 101 | return null; 102 | } 103 | 104 | /** 105 | * Requests the RM2 to enter learning mode.
106 | *
107 | * The {@link #auth() auth()} method must be ran before these commands 108 | * 109 | * @return Result whether the command is successfully sent. 110 | * @throws IOException 111 | * Problems on sending packet 112 | */ 113 | public boolean enterLearning() throws IOException { 114 | EnterLearnCmdPayload cmdPayload = new EnterLearnCmdPayload(); 115 | DatagramPacket packet = sendCmdPkt(10000, cmdPayload); 116 | 117 | byte[] data = packet.getData(); 118 | log.debug("RM2 enter learning received encrypted bytes: " + DatatypeConverter.printHexBinary(data)); 119 | int err = data[0x22] | (data[0x23] << 8); 120 | 121 | if (err == 0) { 122 | return true; 123 | } 124 | 125 | log.warn("RM2 enter learning received error: " + Integer.toHexString(err) + " / " + err); 126 | return false; 127 | } 128 | 129 | /** 130 | * Requests the RM2 to return the room temperature
131 | *
132 | * The {@link #auth() auth()} method must be ran before these commands 133 | * 134 | * @return The room temperature in a floating number 135 | * @throws IOException 136 | * Problems on sending packet 137 | */ 138 | public double getTemp() throws Exception { 139 | DatagramPacket packet = sendCmdPkt(new RMTempCmdPayload()); 140 | byte[] data = packet.getData(); 141 | 142 | log.debug("RM2 get temp received encrypted bytes: " + DatatypeConverter.printHexBinary(data)); 143 | int err = data[0x22] | (data[0x23] << 8); 144 | 145 | if (err == 0) { 146 | byte[] pl = decryptFromDeviceMessage(data); 147 | log.debug("RM2 get temp received bytes (decrypted): " + DatatypeConverter.printHexBinary(pl)); 148 | 149 | return (double) (pl[0x4] * 10 + pl[0x5]) / 10.0; 150 | } else { 151 | log.warn("RM2 get temp received error: " + Integer.toHexString(err) + " / " + err); 152 | } 153 | 154 | return -1; 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/RMDevice.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi; 30 | 31 | import java.io.IOException; 32 | 33 | import com.github.mob41.blapi.mac.Mac; 34 | 35 | /** 36 | * This is a legacy class for backward compatibility with RM2Device. 37 | * 38 | * Please consider using RM2Device instead of RMDevice. 39 | * 40 | * @deprecated 41 | * @author Anthony 42 | * 43 | */ 44 | public class RMDevice extends RM2Device { 45 | 46 | public RMDevice(short deviceType, String deviceDesc, String host, Mac mac) throws IOException { 47 | super(deviceType, deviceDesc, host, mac); 48 | } 49 | 50 | public RMDevice(String host, Mac mac) throws IOException { 51 | super(host, mac); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/SP1Device.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | 30 | package com.github.mob41.blapi; 31 | 32 | import java.io.IOException; 33 | import java.net.DatagramPacket; 34 | 35 | import javax.xml.bind.DatatypeConverter; 36 | 37 | import com.github.mob41.blapi.mac.Mac; 38 | import com.github.mob41.blapi.pkt.CmdPayload; 39 | import com.github.mob41.blapi.pkt.Payload; 40 | 41 | public class SP1Device extends BLDevice { 42 | 43 | public SP1Device(String host, Mac mac) throws IOException { 44 | super(BLDevice.DEV_SP1, BLDevice.DESC_SP1, host, mac); 45 | } 46 | 47 | public void setPower(final boolean state) throws Exception { 48 | DatagramPacket packet = sendCmdPkt(new CmdPayload() { 49 | 50 | @Override 51 | public byte getCommand() { 52 | return 0x66; 53 | } 54 | 55 | @Override 56 | public Payload getPayload() { 57 | return new Payload() { 58 | 59 | @Override 60 | public byte[] getData() { 61 | byte[] b = new byte[4]; 62 | b[0] = (byte) (state ? 1 : 0); 63 | return b; 64 | } 65 | 66 | }; 67 | } 68 | 69 | }); 70 | 71 | byte[] data = packet.getData(); 72 | 73 | log.debug("SP1 set power received encrypted bytes: " + DatatypeConverter.printHexBinary(data)); 74 | 75 | int err = data[0x22] | (data[0x23] << 8); 76 | 77 | if (err != 0) { 78 | log.warn("SP1 set power received returned err: " + Integer.toHexString(err) + " / " + err); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/SP2Device.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | 30 | package com.github.mob41.blapi; 31 | 32 | import java.io.IOException; 33 | import java.net.DatagramPacket; 34 | 35 | import javax.xml.bind.DatatypeConverter; 36 | 37 | import com.github.mob41.blapi.mac.Mac; 38 | import com.github.mob41.blapi.pkt.CmdPayload; 39 | import com.github.mob41.blapi.pkt.Payload; 40 | 41 | public class SP2Device extends BLDevice { 42 | 43 | protected SP2Device(short deviceType, String deviceDesc, String host, Mac mac) throws IOException { 44 | super(deviceType, deviceDesc, host, mac); 45 | } 46 | 47 | public SP2Device(String host, Mac mac) throws IOException{ 48 | super(BLDevice.DEV_SP2, BLDevice.DESC_SP2, host, mac); 49 | } 50 | 51 | public void setState(final boolean state) throws Exception { 52 | DatagramPacket packet = sendCmdPkt(new CmdPayload() { 53 | 54 | @Override 55 | public byte getCommand() { 56 | return 0x6a; 57 | } 58 | 59 | @Override 60 | public Payload getPayload() { 61 | return new Payload() { 62 | 63 | @Override 64 | public byte[] getData() { 65 | byte[] b = new byte[16]; 66 | b[0] = (byte) 2; 67 | b[4] = (byte) (state ? 1 : 0); 68 | return b; 69 | } 70 | 71 | }; 72 | } 73 | 74 | }); 75 | 76 | byte[] data = packet.getData(); 77 | 78 | log.debug("SP2 set state received encrypted bytes: " + DatatypeConverter.printHexBinary(data)); 79 | 80 | int err = data[0x22] | (data[0x23] << 8); 81 | 82 | if (err != 0) { 83 | log.warn("SP2 set state received returned err: " + Integer.toHexString(err) + " / " + err); 84 | } 85 | } 86 | 87 | public boolean getState() throws Exception { 88 | DatagramPacket packet = sendCmdPkt(new CmdPayload() { 89 | 90 | @Override 91 | public byte getCommand() { 92 | return 0x6a; 93 | } 94 | 95 | @Override 96 | public Payload getPayload() { 97 | return new Payload() { 98 | 99 | @Override 100 | public byte[] getData() { 101 | byte[] b = new byte[16]; 102 | b[0] = 1; 103 | return b; 104 | } 105 | 106 | }; 107 | } 108 | 109 | }); 110 | byte[] data = packet.getData(); 111 | 112 | log.debug("SP2 get state received encrypted bytes: " + DatatypeConverter.printHexBinary(data)); 113 | 114 | int err = data[0x22] | (data[0x23] << 8); 115 | 116 | if (err == 0) { 117 | byte[] pl = decryptFromDeviceMessage(data); 118 | log.debug("SP2 get state received bytes (decrypted): " + DatatypeConverter.printHexBinary(pl)); 119 | return pl[0x4] == 1 ? true : false; 120 | } else { 121 | log.warn("SP2 get state received an error: " + Integer.toHexString(err) + " / " + err); 122 | } 123 | 124 | return false; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/dev/hysen/AdvancedStatusInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.mob41.blapi.dev.hysen; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * Advanced status info retrieved from a hysen type thermostat 7 | * 8 | * Adapted from https://github.com/mjg59/python-broadlink 9 | * 10 | * @author alpapad 11 | * 12 | */ 13 | public class AdvancedStatusInfo extends BaseStatusInfo { 14 | 15 | private final short hour; 16 | private final short min; 17 | private final short sec; 18 | private final short dayofweek; 19 | 20 | // there are 8 periods available for configuration: 21 | // 6 for weekdays and 2 for weekends. 22 | // Loop mode controls which periods get to be applied during Sat and Sun. 23 | private final Period[] periods = new Period[8]; 24 | 25 | private final Period[] weekday = new Period[6]; 26 | private final Period[] weekend = new Period[2]; 27 | 28 | protected AdvancedStatusInfo(byte[] payload) { 29 | super(payload); 30 | 31 | this.hour = payload[19]; 32 | this.min = payload[20]; 33 | this.sec = payload[21]; 34 | this.dayofweek = payload[22]; 35 | if (payload.length>=46) { 36 | for (int i = 0; i < 6; i++) { 37 | this.periods[i] = new Period(i, payload); 38 | this.weekday[i] = this.periods[i]; 39 | 40 | } 41 | 42 | for (int i = 6; i <= 7; i++) { 43 | this.periods[i] = new Period(i, payload); 44 | this.weekend[i - 6] = this.periods[i]; 45 | } 46 | } 47 | } 48 | 49 | public short getHour() { 50 | return hour; 51 | } 52 | 53 | public short getMin() { 54 | return min; 55 | } 56 | 57 | public short getSec() { 58 | return sec; 59 | } 60 | 61 | public short getDayofweek() { 62 | return dayofweek; 63 | } 64 | 65 | public Period[] getWeekday() { 66 | return weekday; 67 | } 68 | 69 | public Period[] getWeekend() { 70 | return weekend; 71 | } 72 | 73 | public Period[] getPeriods() { 74 | return periods; 75 | } 76 | 77 | @Override 78 | public String toString() { 79 | return "StatusInfo [remote lock=" + remoteLock + ",\n power=" + power + ",\n active=" + active 80 | + ",\n manual temperature=" + manualTemp + ",\n room temp=" + roomTemp + ",\n thermostat temp=" 81 | + thermostatTemp + ",\n auto_mode=" + autoMode + ",\n loop_mode=" + loopMode + ",\n SensorControl=" 82 | + sensorControl + ",\n osv=" + osv + ",\n dif=" + dif + ",\n svh=" + svh + ",\n svl=" + svl 83 | + ",\n room temp adj=" + roomTempAdjustment + ",\n anti freeze=" + antiFreezing + ",\n powerOnMemory=" 84 | + powerOnMemory + ",\n fac?=" + fac + ",\n external temp=" + externalTemp + ",\n hour=" + hour 85 | + ",\n min=" + min + ",\n sec=" + sec + ",\n dayofweek=" + dayofweek + ",\n\n weekday=" 86 | + Arrays.toString(weekday) + ",\n\n weekend=" + Arrays.toString(weekend) + "]"; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/dev/hysen/AntiFreezing.java: -------------------------------------------------------------------------------- 1 | package com.github.mob41.blapi.dev.hysen; 2 | 3 | /** 4 | * Anti-freezing function 5 | * 6 | * 00:anti-freezing function shut down 7 | * 8 | * 01:anti-freezing function open 9 | * 10 | * 00:anti-freezing function shut down 11 | * 12 | */ 13 | public enum AntiFreezing { 14 | SHUT_DOWN((byte) 0x00), // 15 | OPEN((byte) 0x01); 16 | 17 | private final byte value; 18 | 19 | private AntiFreezing(byte value) { 20 | this.value = value; 21 | } 22 | 23 | public byte getValue() { 24 | return value; 25 | } 26 | 27 | public static AntiFreezing fromValue(byte val) { 28 | switch (val) { 29 | case 0x00: 30 | return SHUT_DOWN; 31 | case 0x01: 32 | return OPEN; 33 | default: 34 | return SHUT_DOWN; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/dev/hysen/BaseHysenDevice.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | 30 | package com.github.mob41.blapi.dev.hysen; 31 | 32 | import java.io.IOException; 33 | 34 | import javax.xml.bind.DatatypeConverter; 35 | 36 | import com.github.mob41.blapi.BLDevice; 37 | import com.github.mob41.blapi.mac.Mac; 38 | import com.github.mob41.blapi.pkt.cmd.hysen.GetBasicInfoCommand; 39 | import com.github.mob41.blapi.pkt.cmd.hysen.GetStatusCommand; 40 | import com.github.mob41.blapi.pkt.cmd.hysen.SetModeCommand; 41 | import com.github.mob41.blapi.pkt.cmd.hysen.SetPeriodsCommand; 42 | import com.github.mob41.blapi.pkt.cmd.hysen.SetPoweCommand; 43 | import com.github.mob41.blapi.pkt.cmd.hysen.SetTempCommand; 44 | 45 | /** 46 | * Base hysen "class" thermostats 47 | * 48 | * Adapted from https://github.com/mjg59/python-broadlink 49 | * 50 | * @author alpapad 51 | * 52 | */ 53 | public class BaseHysenDevice extends BLDevice { 54 | 55 | /** 56 | * Generic way to create a BaseHysenDevice 57 | * 58 | * @param deviceType 59 | * Device Type 60 | * @param deviceDesc 61 | * Friendly device description 62 | * @param host 63 | * The target Broadlink hostname 64 | * @param mac 65 | * The target Broadlink MAC address 66 | * @throws IOException 67 | * Problems on constructing socket 68 | */ 69 | protected BaseHysenDevice(short deviceType, String deviceDesc, String host, Mac mac) throws IOException { 70 | super(deviceType, deviceDesc, host, mac); 71 | } 72 | 73 | public byte[] decryptFromDeviceMessage(byte[] encData) throws Exception { 74 | return super.decryptFromDeviceMessage(encData); 75 | } 76 | 77 | public double getThermostatTemp() throws Exception { 78 | BaseStatusInfo info = getBasicStatus(); 79 | return info.getThermostatTemp(); 80 | } 81 | 82 | public double getExternalTemp() throws Exception { 83 | BaseStatusInfo info = getBasicStatus(); 84 | return info.getExternalTemp(); 85 | } 86 | 87 | public double getRoomTemp() throws Exception { 88 | BaseStatusInfo info = getBasicStatus(); 89 | return info.getRoomTemp(); 90 | } 91 | 92 | public BaseStatusInfo getBasicStatus() throws Exception { 93 | byte[] pl = new GetBasicInfoCommand().execute(this); 94 | if (pl != null) { 95 | log.debug("getBasicStatus - received bytes: {}", DatatypeConverter.printHexBinary(pl)); 96 | return new BaseStatusInfo(pl); 97 | } 98 | return null; 99 | } 100 | 101 | public AdvancedStatusInfo getAdvancedStatus() throws Exception { 102 | byte[] pl = new GetStatusCommand().execute(this); 103 | if (pl != null) { 104 | log.debug("getAdvancedStatus - received bytes: {}", DatatypeConverter.printHexBinary(pl)); 105 | return new AdvancedStatusInfo(pl); 106 | } 107 | return null; 108 | } 109 | 110 | /** 111 | * Change controller mode auto_mode = 1 for auto (scheduled/timed) mode, 0 for 112 | * manual mode. Manual mode will activate last used temperature. In typical 113 | * usage call set_temp to activate manual control and set temp. loop_mode refers 114 | * to index in [ "12345,67", "123456,7", "1234567" ] E.g. loop_mode = 0 115 | * ("12345,67") means Saturday and Sunday follow the "weekend" schedule 116 | * loop_mode = 2 ("1234567") means every day (including Saturday and Sunday) 117 | * follows the "weekday" schedule 118 | * 119 | * @throws Exception If I/O goes wrong 120 | */ 121 | public void setMode(boolean autoMode, LoopMode loopMode, SensorControl sensorControl) throws Exception { 122 | new SetModeCommand(tob(autoMode), loopMode.getValue(), sensorControl.getValue()).execute(this); 123 | } 124 | 125 | /** 126 | * Change controller mode auto_mode = 1 for auto (scheduled/timed) mode, 0 for 127 | * manual mode. Manual mode will activate last used temperature. In typical 128 | * usage call set_temp to activate manual control and set temp. loop_mode refers 129 | * to index in [ "12345,67", "123456,7", "1234567" ] E.g. loop_mode = 0 130 | * ("12345,67") means Saturday and Sunday follow the "weekend" schedule 131 | * loop_mode = 2 ("1234567") means every day (including Saturday and Sunday) 132 | * follows the "weekday" schedule 133 | * 134 | * @throws Exception If I/O goes wrong 135 | */ 136 | public void setMode(boolean autoMode, LoopMode loopMode) throws Exception { 137 | BaseStatusInfo status = this.getBasicStatus(); 138 | new SetModeCommand(tob(autoMode), loopMode.getValue(), status.getSensorControl().getValue()).execute(this); 139 | } 140 | 141 | public void setPower(boolean powerOn, boolean remoteLock) throws Exception { 142 | new SetPoweCommand(tob(powerOn), tob(remoteLock)).execute(this); 143 | } 144 | 145 | public void setPower(boolean powerOn) throws Exception { 146 | BaseStatusInfo status = this.getBasicStatus(); 147 | new SetPoweCommand(tob(powerOn), tob(status.getRemoteLock())).execute(this); 148 | } 149 | 150 | public void setLock(boolean remoteLock) throws Exception { 151 | BaseStatusInfo status = this.getBasicStatus(); 152 | new SetPoweCommand(tob(status.getPower()), tob(remoteLock)).execute(this); 153 | } 154 | 155 | public void setThermostatTemp(double temp) throws Exception { 156 | new SetTempCommand(temp).execute(this); 157 | } 158 | 159 | public void switchToAuto() throws Exception { 160 | BaseStatusInfo status = this.getBasicStatus(); 161 | this.setMode(true, status.getLoopMode(), status.getSensorControl()); 162 | } 163 | 164 | public void switchToManual() throws Exception { 165 | BaseStatusInfo status = this.getBasicStatus(); 166 | this.setMode(false, status.getLoopMode(), status.getSensorControl()); 167 | } 168 | 169 | public void setAdvancedOptions(LoopMode loopMode, SensorControl sensor, short osv, short dif, short svh, short svl, 170 | double adj, AntiFreezing antiFreeze, PowerOnMemory poweron) throws Exception { 171 | new SetModeCommand(loopMode.getValue(), sensor.getValue(), tob(osv), tob(dif), tob(svh), tob(svl), adj, 172 | antiFreeze.getValue(), poweron.getValue()).execute(this); 173 | } 174 | 175 | public void setPeriods(Period[] schedule) throws Exception { 176 | new SetPeriodsCommand(schedule).execute(this); 177 | } 178 | 179 | private static byte tob(boolean v) { 180 | return (byte) (v ? 1 : 0); 181 | } 182 | 183 | private static byte tob(short in) { 184 | return (byte) (in & 0xff); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/dev/hysen/BaseStatusInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.mob41.blapi.dev.hysen; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | /** 7 | * Base set of status codes 8 | * 9 | * Adapted from https://github.com/mjg59/python-broadlink 10 | * 11 | * @author alpapad 12 | */ 13 | public class BaseStatusInfo { 14 | /** 15 | * The specific logger for this class 16 | */ 17 | protected static final Logger log = LoggerFactory.getLogger(BaseStatusInfo.class); 18 | // remote_lock 19 | protected final boolean remoteLock; 20 | protected final boolean power; 21 | protected final boolean active; 22 | // temp_manual 23 | protected final boolean manualTemp; 24 | // room_temp 25 | protected final double roomTemp; 26 | // thermostat_temp 27 | protected final double thermostatTemp; 28 | // auto_mode 29 | protected final boolean autoMode; 30 | // loop_mode 31 | protected final LoopMode loopMode; 32 | // sensor 33 | protected final SensorControl sensorControl; 34 | // osv 35 | protected final short osv; 36 | // dif 37 | protected final short dif; 38 | // svh 39 | protected final short svh; 40 | // svl 41 | protected final short svl; 42 | // room_temp_adj 43 | protected final double roomTempAdjustment; 44 | // fre 45 | protected final AntiFreezing antiFreezing; 46 | // pon 47 | protected final PowerOnMemory powerOnMemory; 48 | 49 | // unsure 50 | protected final short fac; 51 | // external_temp 52 | protected final double externalTemp; 53 | 54 | private static String bytesToString(byte[] hashInBytes) { 55 | 56 | StringBuilder sb = new StringBuilder(); 57 | for (byte b : hashInBytes) { 58 | sb.append(String.format("%04d ", b)); 59 | } 60 | return sb.toString(); 61 | 62 | } 63 | 64 | protected BaseStatusInfo(byte[] payload) { 65 | log.debug("payload: {}",bytesToString(payload)); 66 | this.remoteLock = byteToBool((byte) (payload[3] & 0x1)); 67 | this.power = byteToBool((byte) (payload[4] & 1)); 68 | this.active = byteToBool((byte) ((payload[4] >> 4) & 1)); 69 | this.manualTemp = byteToBool((byte) ((payload[4] >> 6) & 1)); 70 | this.roomTemp = (payload[5] & 0xff) / 2.0; 71 | this.thermostatTemp = (payload[6] & 0xff) / 2.0; 72 | this.autoMode = (byte)(payload[7] & 15) != 0; 73 | this.loopMode = LoopMode.fromValue((byte) (((payload[7] >> 4)) - 1)); 74 | this.sensorControl = SensorControl.fromValue(payload[8]); 75 | this.osv = payload[9]; 76 | this.dif = payload[10]; 77 | this.svh = payload[11]; 78 | this.svl = payload[12]; 79 | double tempAdj = (((payload[13] << 8) + payload[14]) / 2.0); 80 | if (tempAdj > 32767) { 81 | tempAdj = (32767 - tempAdj); 82 | } 83 | this.roomTempAdjustment = tempAdj; 84 | 85 | this.antiFreezing = AntiFreezing.fromValue(payload[15]); 86 | this.powerOnMemory = PowerOnMemory.fromValue(payload[16]); 87 | this.fac = payload[17]; 88 | this.externalTemp = (payload[18] & 255) / 2.0; 89 | } 90 | 91 | public boolean getRemoteLock() { 92 | return remoteLock; 93 | } 94 | 95 | public boolean getPower() { 96 | return power; 97 | } 98 | 99 | public boolean getActive() { 100 | return active; 101 | } 102 | 103 | public boolean getManualTemp() { 104 | return manualTemp; 105 | } 106 | 107 | public double getRoomTemp() { 108 | return roomTemp; 109 | } 110 | 111 | public double getThermostatTemp() { 112 | return thermostatTemp; 113 | } 114 | 115 | public boolean getAutoMode() { 116 | return autoMode; 117 | } 118 | 119 | /** 120 | * loopMode refers to index in [ "12345,67", "123456,7", "1234567" ] E.g. 121 | * loop_mode = 0 ("12345,67") means Saturday and Sunday follow the "weekend" 122 | * schedule loop_mode = 2 ("1234567") means every day (including Saturday and 123 | * Sunday) follows the "weekday" schedule 124 | * 125 | * @return 126 | */ 127 | public LoopMode getLoopMode() { 128 | return loopMode; 129 | } 130 | 131 | /** 132 | * Sensor control option 0:internal sensor 133 | * 134 | * 1:external sensor 135 | * 136 | * 2:internal control temperature,external limit temperature 137 | * 138 | * default: 0:internal sensor 139 | * 140 | * @return 141 | */ 142 | public SensorControl getSensorControl() { 143 | return sensorControl; 144 | } 145 | 146 | /** 147 | * Limit temperature value of external sensor 148 | * 149 | * values: 5-99ºC 150 | * 151 | * default: 42ºC 152 | * 153 | */ 154 | 155 | public short getOsv() { 156 | return osv; 157 | } 158 | 159 | /** 160 | * Return difference of limit temperature value of external sensor 161 | * 162 | * 163 | * values: 1-9ºC 164 | * 165 | * default: 2ºC 166 | * 167 | * @return 168 | */ 169 | public short getDif() { 170 | return dif; 171 | } 172 | 173 | /** 174 | * Set upper limit temperature value 175 | * 176 | * 177 | * values: 5-99ºC 178 | * 179 | * default: 35ºC 180 | * 181 | * @return 182 | */ 183 | public short getSvh() { 184 | return svh; 185 | } 186 | 187 | /** 188 | * Set lower limit temperature value 189 | * 190 | * values: 5-99ºC 191 | * 192 | * default: 5ºC 193 | * 194 | * @return 195 | */ 196 | public short getSvl() { 197 | return svl; 198 | } 199 | 200 | /** 201 | * Measure temperature 202 | * 203 | * Measure temperature,check and calibration 204 | * 205 | * 0.1ºC precision Calibration (actual temperature) 206 | * 207 | * @return 208 | */ 209 | public double getRoomTempAdjustment() { 210 | return roomTempAdjustment; 211 | } 212 | 213 | /** 214 | * Anti-freezing function 215 | * 216 | * 00:anti-freezing function shut down 217 | * 218 | * 01:anti-freezing function open 219 | * 220 | * 00:anti-freezing function shut down 221 | * 222 | * @return 223 | */ 224 | public AntiFreezing getAntiFreezing() { 225 | return antiFreezing; 226 | } 227 | 228 | /** 229 | * Power on memory 230 | * 231 | * 00:Power on no need memory 232 | * 233 | * 01:Power on need memory 234 | * 235 | * default: 00:Power on no need memory 236 | * 237 | * @return 238 | */ 239 | public PowerOnMemory getPowerOnMemory() { 240 | return powerOnMemory; 241 | } 242 | 243 | /** 244 | * NOT SURE Factory default 245 | * 246 | * 08:just display,no other meaning 247 | * 248 | * 00:Restore factory default 249 | * 250 | * default: 08 251 | * 252 | * @return 253 | */ 254 | public short getFac() { 255 | return fac; 256 | } 257 | 258 | public double getExternalTemp() { 259 | return externalTemp; 260 | } 261 | 262 | @Override 263 | public String toString() { 264 | return "BaseStatusInfo [\nremote lock=" + remoteLock + ",\n power=" + power + ",\n active=" + active 265 | + ",\n manual temp=" + manualTemp + ",\n room temp=" + roomTemp + ",\n thermostat temp=" 266 | + thermostatTemp + ",\n auto_mode=" + autoMode + ",\n loop_mode=" + loopMode + ",\n sensor=" 267 | + sensorControl + ",\n osv=" + osv + ",\n dif=" + dif + ",\n svh=" + svh + ",\n svl=" + svl 268 | + ",\n room_temp_adj=" + roomTempAdjustment + ",\n anti freeze=" + antiFreezing + ",\n powerOnMemory=" 269 | + powerOnMemory + ",\n fac?=" + fac + ",\n external temp=" + externalTemp + "]"; 270 | } 271 | 272 | protected static byte boolToByte(boolean v) { 273 | return (byte) (v ? 1 : 0); 274 | } 275 | 276 | protected static boolean byteToBool(byte v) { 277 | return v == (byte) 1; 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/dev/hysen/LoopMode.java: -------------------------------------------------------------------------------- 1 | package com.github.mob41.blapi.dev.hysen; 2 | 3 | /** 4 | * loop_mode refers to index in [ "12345,67", "123456,7", "1234567" ] E.g. 5 | * loop_mode = 0 ("12345,67") means Saturday and Sunday follow the "weekend" 6 | * schedule loop_mode = 2 ("1234567") means every day (including Saturday and 7 | * Sunday) follows the "weekday" schedule 8 | * 9 | * @author alpapad 10 | * 11 | */ 12 | public enum LoopMode { 13 | D12345_67(0x00), // 14 | D123456_7(0x01), // 15 | D1234567(0x02); 16 | 17 | private final byte value; 18 | 19 | private LoopMode(int value) { 20 | this.value = (byte) (value & 0xFF); 21 | } 22 | 23 | public byte getValue() { 24 | return value; 25 | } 26 | 27 | public static LoopMode fromValue(byte val) { 28 | switch (val) { 29 | case 0x00: 30 | return D12345_67; 31 | case 0x01: 32 | return D123456_7; 33 | case 0x02: 34 | return D1234567; 35 | default: 36 | return D12345_67; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/dev/hysen/Period.java: -------------------------------------------------------------------------------- 1 | package com.github.mob41.blapi.dev.hysen; 2 | 3 | /** 4 | * Represents a "Period" in what hysen calls "Time and interval programming" 5 | * 6 | * @author alpapad 7 | */ 8 | public class Period { 9 | // start_hour 10 | private final short startHour; 11 | // start_minute 12 | private final short startMinute; 13 | private final double temp; 14 | 15 | protected Period(int offset, byte[] payload) { 16 | this.startHour = payload[2 * offset + 23]; 17 | this.startMinute = payload[2 * offset + 24]; 18 | this.temp = payload[offset + 39] / 2.0; 19 | } 20 | 21 | public short getStartHour() { 22 | return startHour; 23 | } 24 | 25 | public short getStartMinute() { 26 | return startMinute; 27 | } 28 | 29 | public double getTemp() { 30 | return temp; 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return "\n\tSchedule [startHour=" + startHour + ", startMinute=" + startMinute + ", temp=" + temp + "]"; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/dev/hysen/PowerOnMemory.java: -------------------------------------------------------------------------------- 1 | package com.github.mob41.blapi.dev.hysen; 2 | 3 | /** 4 | * Power on memory 5 | * 6 | * 00:Power on no need memory 7 | * 8 | * 01:Power on need memory 9 | * 10 | * default: 00:Power on no need memory 11 | * 12 | */ 13 | public enum PowerOnMemory { 14 | ON_NO_NEED_MEM((byte) 0), // 15 | ON_NEED_MEM((byte) 1); 16 | 17 | private final byte value; 18 | 19 | private PowerOnMemory(byte value) { 20 | this.value = value; 21 | } 22 | 23 | public byte getValue() { 24 | return value; 25 | } 26 | 27 | public static PowerOnMemory fromValue(byte val) { 28 | switch (val) { 29 | case 0: 30 | return ON_NO_NEED_MEM; 31 | case 1: 32 | return ON_NEED_MEM; 33 | default: 34 | return ON_NO_NEED_MEM; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/dev/hysen/SensorControl.java: -------------------------------------------------------------------------------- 1 | package com.github.mob41.blapi.dev.hysen; 2 | 3 | /** 4 | * Sensor control option 5 | * 6 | * 0:internal sensor 7 | * 8 | * 1:external sensor 9 | * 10 | * 2:internal control temperature,external limit temperature 11 | * 12 | * default: 0:internal sensor 13 | * 14 | */ 15 | public enum SensorControl { 16 | 17 | INTERNAL((byte) 0x00), // 18 | EXTERNAL((byte) 0x01), // 19 | INTERNAL_TEMP_EXTERNAL_LIMIT((byte) 0x02); 20 | 21 | private final byte value; 22 | 23 | private SensorControl(byte value) { 24 | this.value = value; 25 | } 26 | 27 | public byte getValue() { 28 | return value; 29 | } 30 | 31 | public static SensorControl fromValue(byte val) { 32 | switch (val) { 33 | case 0x00: 34 | return INTERNAL; 35 | case 0x01: 36 | return EXTERNAL; 37 | case 0x02: 38 | return INTERNAL_TEMP_EXTERNAL_LIMIT; 39 | default: 40 | return INTERNAL; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/ex/BLApiException.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi.ex; 30 | 31 | /** 32 | * BLApiException is the super-class of multiple exceptions thrown 33 | * in the library that requires applications to catch. 34 | * 35 | * @author Anthony 36 | * 37 | */ 38 | public class BLApiException extends Exception { 39 | 40 | /** 41 | * 42 | */ 43 | private static final long serialVersionUID = 6978231778391256347L; 44 | 45 | public BLApiException(String arg0) { 46 | super(arg0); 47 | } 48 | 49 | public BLApiException(Throwable arg0) { 50 | super(arg0); 51 | } 52 | 53 | public BLApiException(String arg0, Throwable arg1) { 54 | super(arg0, arg1); 55 | } 56 | 57 | public BLApiException(String arg0, Throwable arg1, boolean arg2, boolean arg3) { 58 | super(arg0, arg1, arg2, arg3); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/ex/BLApiRuntimeException.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi.ex; 30 | 31 | /** 32 | * BLApiRuntimeException is the super-class of multiple exceptions 33 | * thrown in the runtime of the library. 34 | * 35 | * @author Anthony 36 | * 37 | */ 38 | public class BLApiRuntimeException extends RuntimeException { 39 | 40 | /** 41 | * 42 | */ 43 | private static final long serialVersionUID = 6978231778391256347L; 44 | 45 | public BLApiRuntimeException(String arg0) { 46 | super(arg0); 47 | } 48 | 49 | public BLApiRuntimeException(Throwable arg0) { 50 | super(arg0); 51 | } 52 | 53 | public BLApiRuntimeException(String arg0, Throwable arg1) { 54 | super(arg0, arg1); 55 | } 56 | 57 | public BLApiRuntimeException(String arg0, Throwable arg1, boolean arg2, boolean arg3) { 58 | super(arg0, arg1, arg2, arg3); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/ex/package-info.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | /** 30 | * Library base exceptions 31 | * 32 | * @author Anthony 33 | * 34 | */ 35 | package com.github.mob41.blapi.ex; -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/mac/Mac.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi.mac; 30 | 31 | 32 | /** 33 | * A class that handles a MAC address in String and bytes array format 34 | * 35 | * @author Anthony 36 | */ 37 | public class Mac { 38 | 39 | private final byte[] mac; 40 | 41 | /** 42 | * Creates an instance representing the MAC address 43 | * 44 | * @param macBytes The 6-byte MAC address in byte array 45 | * @throws MacFormatException If the MAC address bytes array specified is not with length 6 46 | * or null 47 | */ 48 | public Mac(byte[] macBytes) throws MacFormatException { 49 | if (!isMACValid(macBytes)) { 50 | throw new MacFormatException(macBytes); 51 | } 52 | mac = macBytes; 53 | } 54 | 55 | /** 56 | * Creates an instance representing the MAC address 57 | * 58 | * @param macStr MAC address represented in String seperated by cottons ( 59 | * :) (e.g. 00:00:00:00:00:00) 60 | * @throws MacFormatException If the MAC address does not have a valid format: 61 | *

62 | * does not have a hex (e.g. 0f) in the macStr, or, 63 | * does not have 6 hex separated by cottons (:), or, a 64 | * null is specified 65 | */ 66 | public Mac(String macStr) throws MacFormatException { 67 | mac = macStrToBytes(macStr); 68 | } 69 | 70 | /** 71 | * Returns the MAC address in bytes array 72 | * 73 | * @return MAC address in bytes array 74 | */ 75 | public byte[] getMac() { 76 | return mac; 77 | } 78 | 79 | /** 80 | * Returns the MAC address represented in String 81 | * 82 | * @return MAC address in String 83 | */ 84 | public String getMacString() { 85 | return bytesToMacStr(mac); 86 | } 87 | 88 | /** 89 | * Converts MAC address String into bytes 90 | * 91 | * @param macStr The 6-byte MAC Address (00:00:00:00:00:00) in String separated 92 | * by cottons (:) 93 | * @return Converted MAC Address in bytes 94 | * @throws MacFormatException If the MAC address does not have a valid format: 95 | *

96 | * does not have a hex (e.g. 0f) in the macStr, or, 97 | * does not have 6 hex separated by cottons (:), or, a 98 | * null is specified 99 | */ 100 | public static byte[] macStrToBytes(String macStr) throws MacFormatException { 101 | if (macStr == null) { 102 | throw new MacFormatException(macStr); 103 | } 104 | 105 | String[] macs = macStr.split(":"); 106 | 107 | if (macs.length != 6) { 108 | throw new MacFormatException(macStr); 109 | } 110 | 111 | byte[] bout = new byte[6]; 112 | for (int i = 0; i < macs.length; i++) { 113 | try { 114 | Integer hex = Integer.parseInt(macs[i], 16); 115 | bout[i] = hex.byteValue(); 116 | } catch (NumberFormatException e) { 117 | throw new MacFormatException(macStr, e); 118 | } 119 | } 120 | 121 | return bout; 122 | } 123 | 124 | /** 125 | * Returns whether the specified MAC bytes array is valid with the following 126 | * conditions:
127 | *
128 | * 1. macBytes not null
129 | * 2. macBytes's length is equal to 6 130 | * 131 | * @param macBytes The byte array to be validated 132 | * @return The validation result 133 | */ 134 | public static boolean isMACValid(byte[] macBytes) { 135 | return macBytes != null && macBytes.length == 6; 136 | } 137 | 138 | /** 139 | * Converts MAC address bytes into String 140 | * 141 | * @param macBytes The 6-byte MAC Address in byte array 142 | * @return A MAC address String converted from the byte array 143 | * @throws MacFormatException If the MAC address bytes array specified is not with length 6 144 | * or null 145 | */ 146 | public static String bytesToMacStr(byte[] macBytes) throws MacFormatException { 147 | if (!isMACValid(macBytes)) { 148 | throw new MacFormatException(macBytes); 149 | } 150 | 151 | String str = ""; 152 | 153 | for (int i = 0; i < macBytes.length; i++) { 154 | String hexStr = String.format("%02x", macBytes[i]); 155 | str += hexStr; 156 | 157 | if (i != macBytes.length - 1) { 158 | str += ':'; 159 | } 160 | } 161 | return str; 162 | } 163 | 164 | @Override 165 | public String toString() { 166 | return getMacString(); 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/mac/MacFormatException.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi.mac; 30 | 31 | import com.github.mob41.blapi.ex.BLApiRuntimeException; 32 | 33 | /** 34 | * MacFormatException is thrown where the MAC address String / 35 | * bytes array specified does not have a valid format. (Total 48 bit / 6 bytes 36 | * 00:00:00:00:00:00) 37 | * 38 | * @author Anthony 39 | * 40 | */ 41 | public class MacFormatException extends BLApiRuntimeException { 42 | 43 | /** 44 | * 45 | */ 46 | private static final long serialVersionUID = -4391144827334864769L; 47 | 48 | private static final String INVALID_MAC_FORMAT = "Invalid MAC address String format"; 49 | 50 | private static final String INCORRECT_MAC_ADDR_BYTES = "Incorrect MAC address bytes"; 51 | 52 | /** 53 | * Creates the exception with a "Invalid MAC address String format" message. 54 | * 55 | * @param arg0 56 | * the invalid MAC address String 57 | */ 58 | public MacFormatException(String arg0) { 59 | super(INVALID_MAC_FORMAT + ": " + arg0); 60 | } 61 | 62 | /** 63 | * Creates the exception with a "Invalid MAC address String format" message, 64 | * and a Throwable 65 | * 66 | * @param arg0 67 | * the invalid MAC address String 68 | * @param arg1 69 | * the Throwable 70 | */ 71 | public MacFormatException(String arg0, Throwable arg1) { 72 | super(INVALID_MAC_FORMAT + ": " + arg0, arg1); 73 | } 74 | 75 | /** 76 | * Creates the exception with a "Incorrect MAC address bytes" 77 | * 78 | * @param bytes 79 | * the bytes array with incorrect length (not equal to 6) 80 | */ 81 | public MacFormatException(byte[] bytes) { 82 | super(INCORRECT_MAC_ADDR_BYTES + ": " + (bytes != null ? bytes.length : "null") + "/6: " 83 | + bytesArrToHexStrArr(bytes)); 84 | } 85 | 86 | private static String[] bytesArrToHexStrArr(byte[] ba) { 87 | if (ba == null) { 88 | return null; 89 | } 90 | 91 | String[] outa = new String[ba.length]; 92 | for (int i = 0; i < outa.length; i++) { 93 | outa[i] = Integer.toHexString(ba[i]); 94 | } 95 | 96 | return outa; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/mac/package-info.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | /** 30 | * MAC Address handling 31 | * 32 | * @author Anthony 33 | * 34 | */ 35 | package com.github.mob41.blapi.mac; -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/package-info.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | /** 30 | * Base library implementations 31 | * 32 | * @author Anthony 33 | * 34 | */ 35 | package com.github.mob41.blapi; -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/BytePayload.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi.pkt; 30 | 31 | public class BytePayload implements Payload { 32 | 33 | private final byte[] data; 34 | 35 | public BytePayload(byte[] data) { 36 | this.data = data; 37 | } 38 | 39 | @Override 40 | public byte[] getData() { 41 | return data; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/CmdPacket.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi.pkt; 30 | 31 | import javax.xml.bind.DatatypeConverter; 32 | 33 | import org.slf4j.Logger; 34 | import org.slf4j.LoggerFactory; 35 | 36 | import com.github.mob41.blapi.BLDevice; 37 | import com.github.mob41.blapi.ex.BLApiRuntimeException; 38 | import com.github.mob41.blapi.mac.Mac; 39 | import com.github.mob41.blapi.pkt.auth.AES; 40 | 41 | /** 42 | * This constructs a byte array with the format of a command to the Broadlink 43 | * device 44 | * 45 | * @author Anthony 46 | * 47 | */ 48 | public class CmdPacket implements Packet { 49 | 50 | private static final Logger log = LoggerFactory.getLogger(CmdPacket.class); 51 | 52 | private final byte[] data; 53 | 54 | /** 55 | * Constructs a command packet 56 | * 57 | * @param targetMac 58 | * Target Broadlink device MAC address 59 | * @param count 60 | * Count of packets sent (provided by BLDevice sendPkt()) 61 | * @param id 62 | * This BLDevice ID provided by the Broadlink device. It is 63 | * {0,0,0,0} if auth() not ran 64 | * @param aesInstance 65 | * The AES encrypt/decrypt instance 66 | * @param cmdPayload 67 | * The data to be sent 68 | */ 69 | public CmdPacket(Mac targetMac, int count, byte[] id, AES aesInstance, CmdPayload cmdPayload) { 70 | 71 | byte cmd = cmdPayload.getCommand(); 72 | byte[] payload = cmdPayload.getPayload().getData(); 73 | byte[] headerdata; 74 | 75 | log.debug("Constructor CmdPacket starts"); 76 | log.debug("count=" + count + " cmdPayload.cmd=" + Integer.toHexString(cmd) + " payload.len=" + payload.length); 77 | 78 | count = (count + 1) & 0xffff; // increased by the sendPkt() 79 | 80 | log.debug("New count: " + count + " (added by 1)"); 81 | log.debug("Creating byte array with data"); 82 | 83 | headerdata = new byte[BLDevice.DEFAULT_BYTES_SIZE]; 84 | for (int i = 0; i < headerdata.length; i++) { 85 | headerdata[i] = 0x00; 86 | } 87 | 88 | headerdata[0x00] = 0x5a; 89 | headerdata[0x01] = (byte) 0xa5; 90 | headerdata[0x02] = (byte) 0xaa; 91 | headerdata[0x03] = 0x55; 92 | headerdata[0x04] = 0x5a; 93 | headerdata[0x05] = (byte) 0xa5; 94 | headerdata[0x06] = (byte) 0xaa; 95 | headerdata[0x07] = 0x55; 96 | 97 | headerdata[0x24] = 0x2a; 98 | headerdata[0x25] = 0x27; 99 | headerdata[0x26] = cmd; 100 | 101 | headerdata[0x28] = (byte) (count & 0xff); 102 | headerdata[0x29] = (byte) (count >> 8); 103 | 104 | byte[] mac = targetMac.getMac(); 105 | 106 | headerdata[0x2a] = mac[0]; 107 | headerdata[0x2b] = mac[1]; 108 | headerdata[0x2c] = mac[2]; 109 | headerdata[0x2d] = mac[3]; 110 | headerdata[0x2e] = mac[4]; 111 | headerdata[0x2f] = mac[5]; 112 | 113 | headerdata[0x30] = id[0]; 114 | headerdata[0x31] = id[1]; 115 | headerdata[0x32] = id[2]; 116 | headerdata[0x33] = id[3]; 117 | 118 | // pad the payload for AES encryption 119 | byte[] payloadPad = null; 120 | if(payload.length > 0) { 121 | int numpad = 16 - (payload.length % 16); 122 | 123 | payloadPad = new byte[payload.length+numpad]; 124 | for(int i = 0; i < payloadPad.length; i++) { 125 | if(i < payload.length) 126 | payloadPad[i] = payload[i]; 127 | else 128 | payloadPad[i] = 0x00; 129 | } 130 | } 131 | 132 | log.debug("Running checksum for un-encrypted payload"); 133 | 134 | int checksumpayload = 0xbeaf; 135 | for (int i = 0; i < payloadPad.length; i++) { 136 | checksumpayload = checksumpayload + Byte.toUnsignedInt(payloadPad[i]); 137 | checksumpayload = checksumpayload & 0xffff; 138 | } 139 | 140 | headerdata[0x34] = (byte) (checksumpayload & 0xff); 141 | headerdata[0x35] = (byte) (checksumpayload >> 8); 142 | 143 | log.debug("Un-encrypted payload checksum: " + Integer.toHexString(checksumpayload)); 144 | 145 | try { 146 | log.debug("Encrypting payload"); 147 | 148 | payload = aesInstance.encrypt(payloadPad); 149 | log.debug("Encrypted payload bytes: {}", DatatypeConverter.printHexBinary(payload)); 150 | 151 | log.debug("Encrypted. len=" + payload.length); 152 | } catch (Exception e) { 153 | log.error("Cannot encrypt payload! Aborting", e); 154 | throw new BLApiRuntimeException("Cannot encrypt payload", e); 155 | } 156 | 157 | data = new byte[BLDevice.DEFAULT_BYTES_SIZE + payload.length]; 158 | 159 | for (int i = 0; i < headerdata.length; i++) { 160 | data[i] = headerdata[i]; 161 | } 162 | 163 | for (int i = 0; i < payload.length; i++) { 164 | data[i + BLDevice.DEFAULT_BYTES_SIZE] = payload[i]; 165 | } 166 | 167 | log.debug("Running whole packet checksum"); 168 | 169 | int checksumpkt = 0xbeaf; 170 | for (int i = 0; i < data.length; i++) { 171 | checksumpkt = checksumpkt + Byte.toUnsignedInt(data[i]); 172 | checksumpkt = checksumpkt & 0xffff; 173 | // log.debug("index: " + i + ", data byte: " + Byte.toUnsignedInt(data[i]) + ", checksum: " + checksumpkt); 174 | } 175 | 176 | log.debug("Whole packet checksum: " + Integer.toHexString(checksumpkt)); 177 | 178 | data[0x20] = (byte) (checksumpkt & 0xff); 179 | data[0x21] = (byte) (checksumpkt >> 8); 180 | 181 | log.debug("End of CmdPacket constructor"); 182 | } 183 | 184 | @Override 185 | public byte[] getData() { 186 | return data; 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/CmdPayload.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi.pkt; 30 | 31 | public interface CmdPayload { 32 | 33 | public byte getCommand(); 34 | 35 | public Payload getPayload(); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/Crc16.java: -------------------------------------------------------------------------------- 1 | package com.github.mob41.blapi.pkt; 2 | 3 | 4 | /** 5 | * Stolen from org.eclipse.kura.protocol.modbus.Crc16 (Eclipse kura project) 6 | * 7 | * Used to calculate the CRC-16 (cyclical redundancy check) for an array of bytes. 8 | */ 9 | public class Crc16 { 10 | 11 | private Crc16() { 12 | }; 13 | 14 | /** 15 | * calculate the crc for the passed buffer 16 | * 17 | * @param buff 18 | * byte array to calculate CRC of 19 | * @param buffLen 20 | * number of bytes in array to calculate against 21 | * @param crcSeed 22 | * starting seed for CRC calculation 23 | * @return CRC16 as calculated for buff 24 | */ 25 | public static int getCrc16(byte[] buff, int buffLen, int crcSeed) { 26 | int hi, lo, tmp; 27 | 28 | lo = crcSeed & 0x0ff; 29 | hi = crcSeed >> 8 & 0x0ff; 30 | 31 | for (int i = 0; i < buffLen; i++) { 32 | tmp = (lo ^ buff[i]) & 0x0ff; 33 | lo = hi ^ abCrcTbl2[tmp]; 34 | hi = abCrcTbl1[tmp]; 35 | } 36 | return lo + (hi << 8); 37 | } 38 | 39 | private final static int[] abCrcTbl1 = { 0x000, 0x0C0, 0x0C1, 0x001, 0x0C3, 0x003, 0x002, 0x0C2, 0x0C6, 0x006, 40 | 0x007, 0x0C7, 0x005, 0x0C5, 0x0C4, 0x004, 0x0CC, 0x00C, 0x00D, 0x0CD, 0x00F, 0x0CF, 0x0CE, 0x00E, 0x00A, 41 | 0x0CA, 0x0CB, 0x00B, 0x0C9, 0x009, 0x008, 0x0C8, 0x0D8, 0x018, 0x019, 0x0D9, 0x01B, 0x0DB, 0x0DA, 0x01A, 42 | 0x01E, 0x0DE, 0x0DF, 0x01F, 0x0DD, 0x01D, 0x01C, 0x0DC, 0x014, 0x0D4, 0x0D5, 0x015, 0x0D7, 0x017, 0x016, 43 | 0x0D6, 0x0D2, 0x012, 0x013, 0x0D3, 0x011, 0x0D1, 0x0D0, 0x010, 0x0F0, 0x030, 0x031, 0x0F1, 0x033, 0x0F3, 44 | 0x0F2, 0x032, 0x036, 0x0F6, 0x0F7, 0x037, 0x0F5, 0x035, 0x034, 0x0F4, 0x03C, 0x0FC, 0x0FD, 0x03D, 0x0FF, 45 | 0x03F, 0x03E, 0x0FE, 0x0FA, 0x03A, 0x03B, 0x0FB, 0x039, 0x0F9, 0x0F8, 0x038, 0x028, 0x0E8, 0x0E9, 0x029, 46 | 0x0EB, 0x02B, 0x02A, 0x0EA, 0x0EE, 0x02E, 0x02F, 0x0EF, 0x02D, 0x0ED, 0x0EC, 0x02C, 0x0E4, 0x024, 0x025, 47 | 0x0E5, 0x027, 0x0E7, 0x0E6, 0x026, 0x022, 0x0E2, 0x0E3, 0x023, 0x0E1, 0x021, 0x020, 0x0E0, 0x0A0, 0x060, 48 | 0x061, 0x0A1, 0x063, 0x0A3, 0x0A2, 0x062, 0x066, 0x0A6, 0x0A7, 0x067, 0x0A5, 0x065, 0x064, 0x0A4, 0x06C, 49 | 0x0AC, 0x0AD, 0x06D, 0x0AF, 0x06F, 0x06E, 0x0AE, 0x0AA, 0x06A, 0x06B, 0x0AB, 0x069, 0x0A9, 0x0A8, 0x068, 50 | 0x078, 0x0B8, 0x0B9, 0x079, 0x0BB, 0x07B, 0x07A, 0x0BA, 0x0BE, 0x07E, 0x07F, 0x0BF, 0x07D, 0x0BD, 0x0BC, 51 | 0x07C, 0x0B4, 0x074, 0x075, 0x0B5, 0x077, 0x0B7, 0x0B6, 0x076, 0x072, 0x0B2, 0x0B3, 0x073, 0x0B1, 0x071, 52 | 0x070, 0x0B0, 0x050, 0x090, 0x091, 0x051, 0x093, 0x053, 0x052, 0x092, 0x096, 0x056, 0x057, 0x097, 0x055, 53 | 0x095, 0x094, 0x054, 0x09C, 0x05C, 0x05D, 0x09D, 0x05F, 0x09F, 0x09E, 0x05E, 0x05A, 0x09A, 0x09B, 0x05B, 54 | 0x099, 0x059, 0x058, 0x098, 0x088, 0x048, 0x049, 0x089, 0x04B, 0x08B, 0x08A, 0x04A, 0x04E, 0x08E, 0x08F, 55 | 0x04F, 0x08D, 0x04D, 0x04C, 0x08C, 0x044, 0x084, 0x085, 0x045, 0x087, 0x047, 0x046, 0x086, 0x082, 0x042, 56 | 0x043, 0x083, 0x041, 0x081, 0x080, 0x040 }; 57 | 58 | private final static int[] abCrcTbl2 = { 0x00, 0x0C1, 0x081, 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x01, 0x0C0, 0x080, 59 | 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x00, 0x0C1, 0x081, 60 | 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x01, 0x0C0, 0x080, 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x00, 0x0C1, 0x081, 61 | 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x01, 0x0C0, 0x080, 62 | 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x00, 0x0C1, 0x081, 63 | 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x01, 0x0C0, 0x080, 64 | 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x00, 0x0C1, 0x081, 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x01, 0x0C0, 0x080, 65 | 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x00, 0x0C1, 0x081, 66 | 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x01, 0x0C0, 0x080, 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x00, 0x0C1, 0x081, 67 | 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x01, 0x0C0, 0x080, 68 | 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x00, 0x0C1, 0x081, 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x01, 0x0C0, 0x080, 69 | 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x00, 0x0C1, 0x081, 70 | 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x01, 0x0C0, 0x080, 71 | 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x00, 0x0C1, 0x081, 72 | 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x01, 0x0C0, 0x080, 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x00, 0x0C1, 0x081, 73 | 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x00, 0x0C1, 0x081, 0x40, 0x01, 0x0C0, 0x080, 0x41, 0x01, 0x0C0, 0x080, 74 | 0x41, 0x00, 0x0C1, 0x081, 0x40 }; 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/Packet.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi.pkt; 30 | 31 | /** 32 | * Packet interface
33 | *
34 | * This is probably useless... 35 | * 36 | * @author Anthony 37 | * 38 | */ 39 | public interface Packet { 40 | 41 | /** 42 | * Returns this packet's final compiled data 43 | * 44 | * @return Final compiled data 45 | */ 46 | public byte[] getData(); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/Payload.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi.pkt; 30 | 31 | public interface Payload { 32 | 33 | public byte[] getData(); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/auth/AES.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi.pkt.auth; 30 | 31 | import javax.crypto.Cipher; 32 | import javax.crypto.spec.IvParameterSpec; 33 | import javax.crypto.spec.SecretKeySpec; 34 | 35 | public class AES { 36 | 37 | private static final String CIPHER_ALGO = "AES/CBC/NoPadding"; 38 | 39 | private static final String KEY_ALGO = "AES"; 40 | 41 | private final byte[] key; 42 | 43 | private final byte[] iv; 44 | 45 | public AES(byte[] iv, byte[] key) { 46 | this.key = key; 47 | this.iv = iv; 48 | } 49 | 50 | public byte[] encrypt(byte[] data) throws Exception { 51 | Cipher c = Cipher.getInstance(CIPHER_ALGO); 52 | c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, KEY_ALGO), new IvParameterSpec(iv)); 53 | return c.doFinal(data); 54 | } 55 | 56 | public byte[] decrypt(byte[] data) throws Exception { 57 | Cipher c = Cipher.getInstance(CIPHER_ALGO); 58 | c.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, KEY_ALGO), new IvParameterSpec(iv)); 59 | return c.doFinal(data); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/auth/AuthCmdPayload.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi.pkt.auth; 30 | 31 | import com.github.mob41.blapi.pkt.CmdPayload; 32 | import com.github.mob41.blapi.pkt.Payload; 33 | 34 | public class AuthCmdPayload implements CmdPayload { 35 | 36 | private final AuthPayload authPayload; 37 | 38 | public AuthCmdPayload() { 39 | authPayload = new AuthPayload(); 40 | } 41 | 42 | @Override 43 | public byte getCommand() { 44 | return 0x65; 45 | } 46 | 47 | @Override 48 | public Payload getPayload() { 49 | return authPayload; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/auth/AuthPayload.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi.pkt.auth; 30 | 31 | import com.github.mob41.blapi.pkt.Payload; 32 | 33 | public class AuthPayload implements Payload { 34 | 35 | private final byte[] data; 36 | 37 | public AuthPayload() { 38 | data = new byte[0x50]; 39 | 40 | data[0x04] = 0x31; 41 | data[0x05] = 0x31; 42 | data[0x06] = 0x31; 43 | data[0x07] = 0x31; 44 | data[0x08] = 0x31; 45 | data[0x09] = 0x31; 46 | data[0x0a] = 0x31; 47 | data[0x0b] = 0x31; 48 | data[0x0c] = 0x31; 49 | data[0x0d] = 0x31; 50 | data[0x0e] = 0x31; 51 | data[0x0f] = 0x31; 52 | data[0x10] = 0x31; 53 | data[0x11] = 0x31; 54 | data[0x12] = 0x31; 55 | data[0x1e] = 0x01; 56 | data[0x2d] = 0x01; 57 | data[0x30] = (byte) 'T'; 58 | data[0x31] = (byte) 'e'; 59 | data[0x32] = (byte) 's'; 60 | data[0x33] = (byte) 't'; 61 | data[0x34] = (byte) ' '; 62 | data[0x35] = (byte) ' '; 63 | data[0x36] = (byte) '1'; 64 | } 65 | 66 | @Override 67 | public byte[] getData() { 68 | return data; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/auth/package-info.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | /** 30 | * Authentication packets implementations 31 | * 32 | * @author Anthony 33 | * 34 | */ 35 | package com.github.mob41.blapi.pkt.auth; -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/cmd/hysen/BaseHysenCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.mob41.blapi.pkt.cmd.hysen; 2 | 3 | import java.net.DatagramPacket; 4 | import java.util.Arrays; 5 | 6 | import javax.xml.bind.DatatypeConverter; 7 | 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import com.github.mob41.blapi.dev.hysen.BaseHysenDevice; 12 | import com.github.mob41.blapi.pkt.CmdPayload; 13 | import com.github.mob41.blapi.pkt.Crc16; 14 | import com.github.mob41.blapi.pkt.Payload; 15 | 16 | /** 17 | * Base hysen command Payload. Handles crc calculation 18 | * 19 | * 20 | * Adapted from https://github.com/mjg59/python-broadlink 21 | * 22 | * @author alpapad 23 | * 24 | */ 25 | public abstract class BaseHysenCommand implements CmdPayload { 26 | 27 | protected static final Logger log = LoggerFactory.getLogger(BaseHysenCommand.class); 28 | 29 | public byte[] execute(BaseHysenDevice device) throws Exception { 30 | 31 | DatagramPacket packet = device.sendCmdPkt(this); 32 | 33 | byte[] data = packet.getData(); 34 | 35 | log.debug(this.getClass().getSimpleName() + " received encrypted bytes: " 36 | + DatatypeConverter.printHexBinary(data)); 37 | 38 | int err = data[0x22] | (data[0x23] << 8); 39 | 40 | if (err == 0) { 41 | byte[] pl = device.decryptFromDeviceMessage(data); 42 | log.debug(this.getClass().getSimpleName() + " received bytes (decrypted): " 43 | + DatatypeConverter.printHexBinary(pl)); 44 | return Arrays.copyOfRange(pl, 2, pl.length); 45 | } else { 46 | log.warn(this.getClass().getSimpleName() + " received an error: " + Integer.toHexString(err) + " / " + err); 47 | } 48 | return null; 49 | } 50 | 51 | @Override 52 | public byte getCommand() { 53 | return 0x6a; 54 | } 55 | 56 | @Override 57 | public Payload getPayload() { 58 | return new Payload() { 59 | /** 60 | * hysen thermostats require a crc16 calculated on the payload before it can be 61 | * send and a length field. 62 | * 63 | * Payload format: 2 bytes len: first byte is len, second is 0. len includes 64 | * also CRC (2 bytes_ X bytes payload 2 bytes CRC16 in ModBus format 65 | */ 66 | @Override 67 | public byte[] getData() { 68 | byte[] p = getCmdBytes(); 69 | byte[] cmd = new byte[p.length + 4]; 70 | cmd[0] = (byte) (p.length + 2); 71 | cmd[1] = 0x00; 72 | 73 | System.arraycopy(p, 0, cmd, 2, p.length); 74 | 75 | int crc = Crc16.getCrc16(p, p.length, 0xffff); 76 | cmd[cmd.length - 2] = (byte) (crc & 0xff); 77 | cmd[cmd.length - 1] = (byte) ((crc >> 8) & 0xFF); 78 | return cmd; 79 | } 80 | }; 81 | } 82 | 83 | protected abstract byte[] getCmdBytes(); 84 | 85 | protected static byte getTempByte(double temp) { 86 | return (byte) ((int) (temp * 2) & 0xff); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/cmd/hysen/GenericByteCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.mob41.blapi.pkt.cmd.hysen; 2 | 3 | public class GenericByteCommand extends BaseHysenCommand { 4 | 5 | private final byte[] cmd; 6 | 7 | public GenericByteCommand(byte[] cmd) { 8 | this.cmd = cmd; 9 | } 10 | 11 | @Override 12 | protected byte[] getCmdBytes() { 13 | return cmd; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/cmd/hysen/GetBasicInfoCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.mob41.blapi.pkt.cmd.hysen; 2 | 3 | public class GetBasicInfoCommand extends GenericByteCommand { 4 | private static byte[] cmd = new byte[] { 0x01, 0x03, 0x00, 0x00, 0x00, 0x08 }; 5 | 6 | public GetBasicInfoCommand() { 7 | super(cmd); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/cmd/hysen/GetStatusCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.mob41.blapi.pkt.cmd.hysen; 2 | 3 | public class GetStatusCommand extends GenericByteCommand { 4 | private static byte[] cmd = new byte[] { 0x01, 0x03, 0x00, 0x00, 0x00, 0x16 }; 5 | 6 | public GetStatusCommand() { 7 | super(cmd); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/cmd/hysen/SetModeCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.mob41.blapi.pkt.cmd.hysen; 2 | 3 | public class SetModeCommand extends GenericByteCommand { 4 | 5 | public SetModeCommand(byte loop_mode, byte sensor, byte osv, byte dif, byte svh, byte svl, double adj, byte fre, 6 | byte poweron) { 7 | super(new byte[] { 0x01, 0x10, 0x00, 0x02, 0x00, 0x05, 0x0a, loop_mode, sensor, osv, dif, svh, svl, 8 | (byte) ((int) (adj * 2) >> 8 & 0xff), (byte) ((int) (adj * 2) & 0xff), fre, poweron }); 9 | } 10 | 11 | public SetModeCommand(byte auto_mode, byte loop_mode, byte sensor) { 12 | super(new byte[] { 0x01, 0x06, 0x00, 0x02, ((byte) (((loop_mode + 1) << 4) + auto_mode)), sensor }); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/cmd/hysen/SetPeriodsCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.mob41.blapi.pkt.cmd.hysen; 2 | 3 | import com.github.mob41.blapi.dev.hysen.Period; 4 | 5 | public class SetPeriodsCommand extends BaseHysenCommand { 6 | 7 | private final byte[] cmd = new byte[] { 0x01, 0x10, 0x00, 0x0a, 0x00, 0x0c, 0x18, // 8 | 0x0, 0x0, 0x0, // wd p1 9 | 0x0, 0x0, 0x0, // wd p2 10 | 0x0, 0x0, 0x0, // wd p3 11 | 0x0, 0x0, 0x0, // wd p4 12 | 0x0, 0x0, 0x0, // wd p5 13 | 0x0, 0x0, 0x0, // wd p6 14 | 15 | 0x0, 0x0, 0x0, // we p1 16 | 0x0, 0x0, 0x0, // we p1 17 | 0x0, 0x0, 0x0, // we p1 18 | 0x0, 0x0, 0x0, // we p1 19 | }; 20 | 21 | public SetPeriodsCommand(Period[] schedule) { 22 | super(); 23 | assert schedule != null; 24 | assert schedule.length == 8; 25 | 26 | int offset = 7; 27 | for (Period s : schedule) { 28 | cmd[offset++] = (byte) (s.getStartHour() & 0xFF); 29 | cmd[offset++] = (byte) (s.getStartMinute() & 0xFF); 30 | } 31 | 32 | for (Period s : schedule) { 33 | cmd[offset++] = (byte) ((int) (s.getTemp() * 2) & 0xFF); 34 | } 35 | } 36 | 37 | @Override 38 | protected byte[] getCmdBytes() { 39 | return cmd; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/cmd/hysen/SetPoweCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.mob41.blapi.pkt.cmd.hysen; 2 | 3 | public class SetPoweCommand extends GenericByteCommand { 4 | 5 | public SetPoweCommand(byte power, byte remote_lock) { 6 | super(new byte[] { 0x01, 0x06, 0x00, 0x00, remote_lock, power }); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/cmd/hysen/SetTempCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.mob41.blapi.pkt.cmd.hysen; 2 | 3 | public class SetTempCommand extends GenericByteCommand { 4 | 5 | public SetTempCommand(double temp) { 6 | super(new byte[] { 0x01, 0x06, 0x00, 0x01, 0x00, getTempByte(temp) }); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/cmd/hysen/SetTimeCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.mob41.blapi.pkt.cmd.hysen; 2 | 3 | public class SetTimeCommand extends GenericByteCommand { 4 | 5 | public SetTimeCommand(byte hour, byte minute, byte second, byte day) { 6 | super(new byte[] { 0x01, 0x10, 0x00, 0x08, 0x00, 0x02, 0x04, hour, minute, second, day }); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/cmd/package-info.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | /** 30 | * Command packets implementations 31 | * 32 | * @author Anthony 33 | * 34 | */ 35 | package com.github.mob41.blapi.pkt.cmd; -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/cmd/rm2/CheckDataCmdPayload.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi.pkt.cmd.rm2; 30 | 31 | import com.github.mob41.blapi.pkt.CmdPayload; 32 | import com.github.mob41.blapi.pkt.Payload; 33 | 34 | public final class CheckDataCmdPayload implements CmdPayload { 35 | 36 | private final Payload payload; 37 | 38 | private final byte[] payloadBytes; 39 | 40 | public CheckDataCmdPayload() { 41 | payloadBytes = new byte[16]; 42 | payloadBytes[0] = 0x04; 43 | 44 | payload = new Payload() { 45 | 46 | @Override 47 | public byte[] getData() { 48 | return payloadBytes; 49 | } 50 | 51 | }; 52 | } 53 | 54 | @Override 55 | public byte getCommand() { 56 | return 0x6a; 57 | } 58 | 59 | @Override 60 | public Payload getPayload() { 61 | return payload; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/cmd/rm2/EnterLearnCmdPayload.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi.pkt.cmd.rm2; 30 | 31 | import com.github.mob41.blapi.pkt.CmdPayload; 32 | import com.github.mob41.blapi.pkt.Payload; 33 | 34 | public final class EnterLearnCmdPayload implements CmdPayload { 35 | 36 | private final Payload payload; 37 | 38 | private final byte[] payloadBytes; 39 | 40 | public EnterLearnCmdPayload() { 41 | payloadBytes = new byte[16]; 42 | payloadBytes[0] = 0x03; 43 | 44 | payload = new Payload() { 45 | 46 | @Override 47 | public byte[] getData() { 48 | return payloadBytes; 49 | } 50 | 51 | }; 52 | } 53 | 54 | @Override 55 | public byte getCommand() { 56 | return 0x6a; 57 | } 58 | 59 | @Override 60 | public Payload getPayload() { 61 | return payload; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/cmd/rm2/RMTempCmdPayload.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi.pkt.cmd.rm2; 30 | 31 | import com.github.mob41.blapi.pkt.CmdPayload; 32 | import com.github.mob41.blapi.pkt.Payload; 33 | 34 | public final class RMTempCmdPayload implements CmdPayload { 35 | 36 | @Override 37 | public byte getCommand() { 38 | return 0x6a; 39 | } 40 | 41 | @Override 42 | public Payload getPayload() { 43 | return new Payload() { 44 | 45 | @Override 46 | public byte[] getData() { 47 | byte[] b = new byte[16]; 48 | b[0] = 0x01; 49 | return b; 50 | } 51 | 52 | }; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/cmd/rm2/SendDataCmdPayload.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi.pkt.cmd.rm2; 30 | 31 | import com.github.mob41.blapi.pkt.CmdPayload; 32 | import com.github.mob41.blapi.pkt.Payload; 33 | 34 | public final class SendDataCmdPayload implements CmdPayload { 35 | 36 | private final Payload payload; 37 | 38 | private final byte[] payloadBytes; 39 | 40 | private final byte[] dataBytes; 41 | 42 | public SendDataCmdPayload(byte[] irRfCodeData) { 43 | this.dataBytes = irRfCodeData; 44 | 45 | payloadBytes = new byte[4 + dataBytes.length]; 46 | payloadBytes[0] = 0x02; 47 | 48 | for (int i = 4; i < dataBytes.length; i++) { 49 | payloadBytes[i] = dataBytes[i - 4]; 50 | } 51 | 52 | payload = new Payload() { 53 | 54 | @Override 55 | public byte[] getData() { 56 | return payloadBytes; 57 | } 58 | 59 | }; 60 | } 61 | 62 | /** 63 | * Returns the IR/RF code data to be sent 64 | * 65 | * @return IR/RF code data 66 | */ 67 | public byte[] getData() { 68 | return dataBytes; 69 | } 70 | 71 | @Override 72 | public byte getCommand() { 73 | return 0x6a; 74 | } 75 | 76 | @Override 77 | public Payload getPayload() { 78 | return payload; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/cmd/rm2/package-info.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | /** 30 | * Broadlink RM2 command packets implementations 31 | * 32 | * @author Anthony 33 | * 34 | */ 35 | package com.github.mob41.blapi.pkt.cmd.rm2; -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/dis/DiscoveryPacket.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi.pkt.dis; 30 | 31 | import java.net.InetAddress; 32 | import java.net.UnknownHostException; 33 | import java.util.Calendar; 34 | import java.util.TimeZone; 35 | 36 | import org.slf4j.Logger; 37 | import org.slf4j.LoggerFactory; 38 | 39 | import com.github.mob41.blapi.ex.BLApiRuntimeException; 40 | import com.github.mob41.blapi.pkt.Packet; 41 | 42 | /** 43 | * This class packs a packet to discover Broadlink devices 44 | * 45 | * @author Anthony 46 | * 47 | */ 48 | public class DiscoveryPacket implements Packet { 49 | 50 | public static final int DEFAULT_SOURCE_PORT = 0; // This source port is from 51 | // the python-broadlink 52 | // source code 53 | 54 | private static final Logger log = LoggerFactory.getLogger(DiscoveryPacket.class); 55 | 56 | private final byte[] data; 57 | 58 | public DiscoveryPacket() { 59 | this(null); 60 | } 61 | 62 | public DiscoveryPacket(InetAddress localIpAddr) { 63 | this(localIpAddr, DEFAULT_SOURCE_PORT, Calendar.getInstance(), TimeZone.getDefault()); 64 | } 65 | 66 | public DiscoveryPacket(InetAddress localIpAddr, int sourcePort) { 67 | this(localIpAddr, sourcePort, Calendar.getInstance(), TimeZone.getDefault()); 68 | } 69 | 70 | public DiscoveryPacket(InetAddress localIpAddr, int sourcePort, Calendar cal, TimeZone tz) { 71 | log.debug("DiscoveryPacket constructor start"); 72 | log.debug("cal=" + cal.getTimeInMillis() + " tz=" + tz.getID()); 73 | if (localIpAddr == null) { 74 | log.debug("localIpAddr is null. Calling InetAddress.getLocalHost"); 75 | try { 76 | localIpAddr = InetAddress.getLocalHost(); 77 | } catch (UnknownHostException e) { 78 | log.error("Could not relieve local IP address", e); 79 | throw new BLApiRuntimeException("Could not relieve local IP address", e); 80 | } 81 | } 82 | log.debug("localIpAddr= " + localIpAddr.getHostName() + "/" + localIpAddr.getHostAddress()); 83 | 84 | int rawOffset = tz.getRawOffset(); 85 | int tzOffset = rawOffset / 3600; 86 | 87 | log.debug("Raw offset: " + rawOffset); 88 | log.debug("Calculated offset: getRawOffset/1000/-3600=" + tzOffset); 89 | 90 | int min = cal.get(Calendar.MINUTE); 91 | int hr = cal.get(Calendar.HOUR); 92 | 93 | int year = cal.get(Calendar.YEAR); 94 | int dayOfWk = dayOfWeekConv(cal.get(Calendar.DAY_OF_WEEK)); // Day of 95 | // week (May 96 | // return -1 97 | // if 98 | // Calendar 99 | // return a 100 | // wrong 101 | // field 102 | // value) 103 | int dayOfMn = cal.get(Calendar.DAY_OF_MONTH); // Day of month 104 | int month = cal.get(Calendar.MONTH) + 1; // Month 105 | 106 | log.debug("min=" + min + " hr=" + hr); 107 | log.debug("year=" + year + " dayOfWk=" + dayOfWk); 108 | log.debug("dayOfMn=" + dayOfMn + " month=" + month); 109 | 110 | byte[] ipAddrBytes = localIpAddr.getAddress(); 111 | 112 | data = new byte[0x30]; // 48-byte 113 | 114 | // data[0x00-0x07] = 0x00; 115 | 116 | // This is directly "copied" from the python-broadlink source code 117 | if (tzOffset < 0) { 118 | data[0x08] = (byte) (0xff + tzOffset - 1); 119 | data[0x09] = (byte) 0xff; 120 | data[0x0a] = (byte) 0xff; 121 | data[0x0b] = (byte) 0xff; 122 | log.debug("tzOffset<0: 0x08=" + Integer.toHexString(0xff + tzOffset - 1) + " 0x09-0x0b=0xff"); 123 | } else { 124 | data[0x08] = (byte) tzOffset; 125 | data[0x09] = (byte) 0x00; 126 | data[0x0a] = (byte) 0x00; 127 | data[0x0b] = (byte) 0x00; 128 | log.debug("tzOffset>0: 0x08=" + Integer.toHexString(tzOffset) + " 0x09-0x0b=0x00"); 129 | } 130 | 131 | data[0x0c] = (byte) (year & 0xff); 132 | data[0x0d] = (byte) (year >> 8); // Shift 8 bits 133 | 134 | data[0x0e] = (byte) min; 135 | data[0x0f] = (byte) hr; 136 | 137 | // subyear = str(year)[2:] //Somehow this code is dirty to do the same 138 | // as python code 139 | data[0x10] = (byte) Integer.parseInt(Integer.toString(year).substring(2, 4)); // Year 140 | // without 141 | // century 142 | 143 | data[0x11] = (byte) dayOfWk; 144 | data[0x12] = (byte) dayOfMn; 145 | data[0x13] = (byte) month; 146 | 147 | // IP address 148 | data[0x18] = ipAddrBytes[0]; 149 | data[0x19] = ipAddrBytes[1]; 150 | data[0x1a] = ipAddrBytes[2]; 151 | data[0x1b] = ipAddrBytes[3]; 152 | 153 | data[0x1c] = (byte) (sourcePort & 0xff); 154 | data[0x1d] = (byte) (sourcePort >> 8); 155 | 156 | data[0x26] = 6; 157 | 158 | // Checksum 159 | short checksum = (short) 0xbeaf; 160 | 161 | for (int i = 0; i < data.length; i++) { 162 | checksum += (int) (data[i] & 0xff); 163 | } 164 | 165 | log.debug("checksum=" + Integer.toHexString(checksum)); 166 | 167 | data[0x20] = (byte) (checksum & 0xff); 168 | data[0x21] = (byte) (checksum >> 8); 169 | 170 | log.debug("DiscoveryPacket constructor end"); 171 | } 172 | 173 | @Override 174 | public byte[] getData() { 175 | return data; 176 | } 177 | 178 | private static int dayOfWeekConv(int fieldVal) { 179 | switch (fieldVal) { 180 | case Calendar.SUNDAY: 181 | return 6; 182 | case Calendar.MONDAY: 183 | return 0; 184 | case Calendar.TUESDAY: 185 | return 1; 186 | case Calendar.WEDNESDAY: 187 | return 2; 188 | case Calendar.THURSDAY: 189 | return 3; 190 | case Calendar.FRIDAY: 191 | return 4; 192 | case Calendar.SATURDAY: 193 | return 5; 194 | } 195 | return -1; 196 | } 197 | 198 | } 199 | -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/dis/package-info.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | /** 30 | * Discovery packet implementations 31 | * 32 | * @author Anthony 33 | * 34 | */ 35 | package com.github.mob41.blapi.pkt.dis; -------------------------------------------------------------------------------- /src/main/java/com/github/mob41/blapi/pkt/package-info.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | /** 30 | * Packet implementations to communicate with the Broadlink devices 31 | * 32 | * @author Anthony 33 | * 34 | */ 35 | package com.github.mob41.blapi.pkt; -------------------------------------------------------------------------------- /src/test/java/com/github/mob41/blapi/DevicesTest.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016, 2017 Anthony Law 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * Contributors: 25 | * - Anthony Law (mob41) - Initial API Implementation 26 | * - bwssytems 27 | * - Christian Fischer (computerlyrik) 28 | *******************************************************************************/ 29 | package com.github.mob41.blapi; 30 | 31 | import java.net.InetAddress; 32 | import java.util.Arrays; 33 | 34 | import org.junit.Ignore; 35 | import org.junit.Test; 36 | import org.slf4j.Logger; 37 | import org.slf4j.LoggerFactory; 38 | 39 | import com.github.mob41.blapi.dev.hysen.AdvancedStatusInfo; 40 | import com.github.mob41.blapi.dev.hysen.BaseHysenDevice; 41 | import com.github.mob41.blapi.dev.hysen.LoopMode; 42 | 43 | public class DevicesTest { 44 | 45 | private static final Logger log = LoggerFactory.getLogger(DevicesTest.class); 46 | 47 | @Test 48 | @Ignore 49 | public void testDevices() throws Exception { 50 | BLDevice[] devs = BLDevice.discoverDevices(InetAddress.getByName("192.168.1.7"), 0, 0); 51 | log.info("BLDevice returned " + devs.length + " number of devices."); 52 | for (int i = 0; i < devs.length; i++) { 53 | BLDevice dev = devs[i]; 54 | 55 | log.info("BLDevice mac is " + dev.getMac().getMacString()); 56 | 57 | dev.auth(); 58 | switch (dev.getDeviceType()) { 59 | case BLDevice.DEV_A1: 60 | EnvData theData = ((A1Device) dev).getSensorsData(); 61 | log.info("A1Device Temp: " + theData.getTemp() + ", Humidity: " + theData.getHumidity() + ", light: " 62 | + theData.getLight() + ", air_quality: " + theData.getAirQualityDescription() + ", noise: " 63 | + theData.getNoise()); 64 | break; 65 | case BLDevice.DEV_MP1: 66 | boolean[] states = ((MP1Device) dev).getStates(); 67 | log.info("MP1 Device all status: " + Arrays.toString(states)); 68 | break; 69 | case BLDevice.DEV_SP2: 70 | case BLDevice.DEV_SP2_HONEYWELL_ALT1: 71 | case BLDevice.DEV_SP2_HONEYWELL_ALT2: 72 | case BLDevice.DEV_SP2_HONEYWELL_ALT3: 73 | case BLDevice.DEV_SP2_HONEYWELL_ALT4: 74 | case BLDevice.DEV_SPMINI: 75 | case BLDevice.DEV_SP3: 76 | case BLDevice.DEV_SPMINI2: 77 | case BLDevice.DEV_SPMINI_OEM_ALT1: 78 | case BLDevice.DEV_SPMINI_OEM_ALT2: 79 | case BLDevice.DEV_SPMINI_PLUS: 80 | boolean state = ((SP2Device) dev).getState(); 81 | log.info("SP2Device returned state: " + state); 82 | break; 83 | case BLDevice.DEV_SP1: 84 | ((SP1Device) dev).setPower(true); 85 | log.info("SP1Device set power to true"); 86 | break; 87 | case BLDevice.DEV_RM_2: 88 | case BLDevice.DEV_RM_MINI: 89 | case BLDevice.DEV_RM_MINI_3: 90 | case BLDevice.DEV_RM_PRO_PHICOMM: 91 | case BLDevice.DEV_RM_2_HOME_PLUS: 92 | case BLDevice.DEV_RM_2_2HOME_PLUS_GDT: 93 | case BLDevice.DEV_RM_2_PRO_PLUS: 94 | case BLDevice.DEV_RM_2_PRO_PLUS_2: 95 | case BLDevice.DEV_RM_2_PRO_PLUS_2_BL: 96 | case BLDevice.DEV_RM_MINI_SHATE: 97 | double theTemp = ((RM2Device) dev).getTemp(); 98 | log.info("RMDevice get temperature: " + theTemp); 99 | break; 100 | 101 | case BLDevice.DEV_HYSEN: 102 | case BLDevice.DEV_FLOUREON: { 103 | BaseHysenDevice fld = BaseHysenDevice.class.cast(dev); 104 | double theTemp2 = fld.getRoomTemp(); 105 | log.info("BaseHysenDevice get room temperature: " + theTemp2); 106 | theTemp2 = fld.getExternalTemp(); 107 | log.info("BaseHysenDevice get getExternalTemp: " + theTemp2); 108 | theTemp2 = fld.getThermostatTemp(); 109 | log.info("BaseHysenDevice get getThermostatTemp: " + theTemp2); 110 | AdvancedStatusInfo info = fld.getAdvancedStatus(); 111 | 112 | log.info("BaseHysenDevice get status: " + info); 113 | 114 | fld.setPower(true, false); 115 | Thread.sleep(1000); 116 | fld.setPower(false, true); 117 | Thread.sleep(1000); 118 | fld.setPower(true, false); 119 | Thread.sleep(1000); 120 | fld.setPower(true, true); 121 | Thread.sleep(1000); 122 | fld.setThermostatTemp(24.5d); 123 | Thread.sleep(1000); 124 | fld.setThermostatTemp(theTemp2); 125 | 126 | info = fld.getAdvancedStatus(); 127 | fld.setPeriods(info.getPeriods()); 128 | fld.setPeriods(info.getPeriods()); 129 | 130 | Thread.sleep(300); 131 | fld.setMode(true, LoopMode.D123456_7); 132 | Thread.sleep(100); 133 | info = fld.getAdvancedStatus(); 134 | log.info("BaseHysenDevice get status: D123456_7 " + info); 135 | assert LoopMode.D123456_7.equals(info.getLoopMode()); 136 | fld.setMode(true, LoopMode.D1234567); 137 | Thread.sleep(100); 138 | info = fld.getAdvancedStatus(); 139 | log.info("BaseHysenDevice get status: D1234567 " + info); 140 | assert LoopMode.D1234567.equals(info.getLoopMode()); 141 | fld.setMode(true, LoopMode.D12345_67); 142 | Thread.sleep(100); 143 | info = fld.getAdvancedStatus(); 144 | log.info("BaseHysenDevice get status: D12345_67 " + info); 145 | assert LoopMode.D12345_67.equals(info.getLoopMode()); 146 | } 147 | break; 148 | 149 | } 150 | 151 | } 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /src/test/resources/simplelogger.properties: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # MIT License 3 | # 4 | # Copyright (c) 2016, 2017 Anthony Law 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | # Contributors: 25 | # - Anthony Law (mob41) - Initial API Implementation 26 | # - bwssytems 27 | # - Christian Fischer (computerlyrik) 28 | ############################################################################### 29 | # org.slf4j.simpleLogger.logFile=broadlink-java-api.trace.log 30 | org.slf4j.simpleLogger.defaultLogLevel=trace 31 | org.slf4j.simpleLogger.showLogName=true --------------------------------------------------------------------------------