├── .github ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ ├── dependency-submission.yml │ ├── gh-pages.yml │ └── gradle.yml ├── .gitignore ├── .settings ├── org.eclipse.jdt.core.prefs └── org.eclipse.jdt.ui.prefs ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.gradle ├── gradle ├── license-header.txt └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main └── java │ └── com │ └── github │ └── kaklakariada │ └── fritzbox │ ├── AbstractTestHelper.java │ ├── Config.java │ ├── EnergyStatisticsService.java │ ├── FritzBoxException.java │ ├── FritzBoxSession.java │ ├── HomeAutomation.java │ ├── LoginFailedException.java │ ├── MissingClassException.java │ ├── TestBlind.java │ ├── TestDriver.java │ ├── TestHkrDriver.java │ ├── helper │ └── StringHelper.java │ ├── http │ ├── AccessForbiddenException.java │ ├── HttpException.java │ ├── HttpTemplate.java │ ├── NullHostnameVerifier.java │ ├── NullTrustManager.java │ ├── QueryParameters.java │ └── TrustSelfSignedCertificates.java │ ├── login │ ├── ChallengeResponse.java │ ├── Md5LoginChallengeResponse.java │ ├── Md5Service.java │ └── Pbkdf2ChallengeResponse.java │ ├── mapping │ ├── Deserializer.java │ └── DeserializerException.java │ └── model │ ├── Rights.java │ ├── SessionInfo.java │ ├── User.java │ ├── UserRight.java │ └── homeautomation │ ├── AbstractDeviceStatistics.java │ ├── Alert.java │ ├── Blind.java │ ├── Button.java │ ├── ColorControl.java │ ├── Device.java │ ├── DeviceList.java │ ├── DeviceStats.java │ ├── Energy.java │ ├── EtsiUnitInfo.java │ ├── Group.java │ ├── GroupInfo.java │ ├── Hkr.java │ ├── Humidity.java │ ├── LevelControl.java │ ├── MeasurementUnit.java │ ├── NextChange.java │ ├── Power.java │ ├── PowerMeter.java │ ├── SimpleOnOffState.java │ ├── Statistics.java │ ├── SwitchState.java │ ├── Temperature.java │ └── Voltage.java └── test ├── java └── com │ └── github │ └── kaklakariada │ └── fritzbox │ ├── assertions │ ├── DeviceListAssert.java │ ├── GroupAssert.java │ ├── GroupInfoAssert.java │ ├── HomeAutomationAssertions.java │ ├── PowerMeterAssert.java │ └── SwitchStateAssert.java │ ├── helper │ └── StringHelperTest.java │ ├── login │ ├── ChallengeResponseTest.java │ ├── Md5LoginChallengeResponseTest.java │ ├── Md5ServiceTest.java │ └── Pbkdf2ChallengeResponseTest.java │ ├── mapping │ └── DeserializerTest.java │ └── model │ └── homeautomation │ └── StatisticsTest.java └── resources ├── FritzOS29 ├── deviceListAllTogetherWithBlind.xml ├── deviceListEmpty.xml ├── deviceListFritzDectBlind.xml ├── devicestatsFritzDect200.xml ├── devicestatsFritzDect301.xml └── devicestatsFritzDect440.xml ├── deviceList.xml ├── deviceListAllTogetherPayload.xml ├── deviceListConnectedFritzDect200Payload.xml ├── deviceListConnectedFritzDect301Payload.xml ├── deviceListConnectedFritzDect440Payload.xml ├── deviceListConnectedFritzDect440x2Payload.xml ├── deviceListConnectedFritzDect500Payload.xml ├── deviceListNotConnectedFritzDect500Payload.xml ├── devicelist6840.xml ├── logback-test.xml └── sessionInfo.xml /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # - package-ecosystem: "gradle" 4 | # directory: "/" 5 | # schedule: 6 | # interval: "monthly" 7 | 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "monthly" 12 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | # schedule: 9 | # - cron: '0 7 * * 6' 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | runs-on: ubuntu-latest 15 | 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | language: ['java'] 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v4 24 | with: 25 | fetch-depth: 2 26 | 27 | - uses: actions/setup-java@v4 28 | with: 29 | distribution: 'adopt' 30 | java-version: 11 31 | 32 | - name: Setup Gradle 33 | uses: gradle/actions/setup-gradle@v4 34 | 35 | - name: Initialize CodeQL 36 | uses: github/codeql-action/init@v3 37 | with: 38 | languages: ${{ matrix.language }} 39 | 40 | - name: Autobuild 41 | uses: github/codeql-action/autobuild@v3 42 | 43 | - name: Perform CodeQL Analysis 44 | uses: github/codeql-action/analyze@v3 45 | -------------------------------------------------------------------------------- /.github/workflows/dependency-submission.yml: -------------------------------------------------------------------------------- 1 | name: Dependency Submission 2 | 3 | on: 4 | # Only runs on main branch 5 | push: 6 | branches: [ main ] 7 | 8 | permissions: 9 | contents: write # Required for dependency submission 10 | 11 | jobs: 12 | dependency-submission: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout sources 16 | uses: actions/checkout@v4 17 | - name: Setup Java 18 | uses: actions/setup-java@v4 19 | with: 20 | distribution: 'temurin' 21 | java-version: 17 22 | - name: Generate and submit dependency graph 23 | uses: gradle/actions/dependency-submission@v4 24 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: Deploy GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | workflow_dispatch: 7 | 8 | concurrency: 9 | group: "pages" 10 | cancel-in-progress: false 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: read 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | - name: Set up JDK 17 21 | uses: actions/setup-java@v4 22 | with: 23 | distribution: 'temurin' 24 | java-version: 17 25 | - name: Setup Gradle 26 | uses: gradle/actions/setup-gradle@v4 27 | - name: Setup Pages 28 | uses: actions/configure-pages@v5 29 | - name: Build Javadoc 30 | run: ./gradlew javadoc --info 31 | - name: Build Reports 32 | run: ./gradlew check jacocoTestReport --info 33 | - name: Collect artifacts 34 | run: cp -r build/reports/ build/docs/ 35 | - name: Upload artifact 36 | uses: actions/upload-pages-artifact@v3 37 | with: 38 | path: ./build/docs/ 39 | 40 | deploy: 41 | if: ${{ github.ref == 'refs/heads/main' }} 42 | permissions: 43 | id-token: write 44 | pages: write 45 | environment: 46 | name: github-pages 47 | url: ${{ steps.deployment.outputs.page_url }} 48 | runs-on: ubuntu-latest 49 | needs: build 50 | steps: 51 | - name: Deploy to GitHub Pages 52 | id: deployment 53 | uses: actions/deploy-pages@v4 54 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | java: [11, 17, 21, 23] 16 | env: 17 | DEFAULT_JAVA: 11 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - name: Setup Java ${{ matrix.java }} 23 | uses: actions/setup-java@v4 24 | with: 25 | distribution: 'temurin' 26 | java-version: ${{ matrix.java }} 27 | 28 | - name: Setup Gradle 29 | uses: gradle/actions/setup-gradle@v4 30 | 31 | - name: Cache SonarQube packages 32 | uses: actions/cache@v4 33 | with: 34 | path: ~/.sonar/cache 35 | key: ${{ runner.os }}-java-${{ matrix.java }}-sonar 36 | restore-keys: ${{ runner.os }}-java-${{ matrix.java }}-sonar 37 | 38 | - name: Build with Gradle 39 | run: ./gradlew clean build --info --warning-mode all -PjavaVersion=${{ matrix.java }} 40 | 41 | - name: Sonar analysis 42 | if: ${{ env.DEFAULT_JAVA == matrix.java && env.SONAR_TOKEN != null }} 43 | run: ./gradlew sonarqube -Dsonar.token=$SONAR_TOKEN --info --warning-mode=summary 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .gradle/ 3 | build/ 4 | bin/ 5 | application.properties 6 | /gradle.properties 7 | .project 8 | .settings/org.eclipse.buildship.core.prefs 9 | .settings/org.eclipse.core.resources.prefs 10 | .settings/org.eclipse.core.runtime.prefs 11 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.ui.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true 3 | formatter_profile=_formatter 4 | formatter_settings_version=21 5 | org.eclipse.jdt.ui.text.custom_code_templates= 6 | sp_cleanup.add_all=false 7 | sp_cleanup.add_default_serial_version_id=true 8 | sp_cleanup.add_generated_serial_version_id=false 9 | sp_cleanup.add_missing_annotations=true 10 | sp_cleanup.add_missing_deprecated_annotations=true 11 | sp_cleanup.add_missing_methods=false 12 | sp_cleanup.add_missing_nls_tags=false 13 | sp_cleanup.add_missing_override_annotations=true 14 | sp_cleanup.add_missing_override_annotations_interface_methods=true 15 | sp_cleanup.add_serial_version_id=false 16 | sp_cleanup.always_use_blocks=true 17 | sp_cleanup.always_use_parentheses_in_expressions=false 18 | sp_cleanup.always_use_this_for_non_static_field_access=false 19 | sp_cleanup.always_use_this_for_non_static_method_access=false 20 | sp_cleanup.array_with_curly=false 21 | sp_cleanup.arrays_fill=false 22 | sp_cleanup.bitwise_conditional_expression=false 23 | sp_cleanup.boolean_literal=false 24 | sp_cleanup.boolean_value_rather_than_comparison=false 25 | sp_cleanup.break_loop=false 26 | sp_cleanup.collection_cloning=false 27 | sp_cleanup.comparing_on_criteria=false 28 | sp_cleanup.comparison_statement=false 29 | sp_cleanup.controlflow_merge=false 30 | sp_cleanup.convert_functional_interfaces=false 31 | sp_cleanup.convert_to_enhanced_for_loop=false 32 | sp_cleanup.convert_to_enhanced_for_loop_if_loop_var_used=false 33 | sp_cleanup.convert_to_switch_expressions=false 34 | sp_cleanup.correct_indentation=false 35 | sp_cleanup.do_while_rather_than_while=false 36 | sp_cleanup.double_negation=false 37 | sp_cleanup.else_if=false 38 | sp_cleanup.embedded_if=false 39 | sp_cleanup.evaluate_nullable=false 40 | sp_cleanup.extract_increment=false 41 | sp_cleanup.format_source_code=true 42 | sp_cleanup.format_source_code_changes_only=false 43 | sp_cleanup.hash=false 44 | sp_cleanup.if_condition=false 45 | sp_cleanup.insert_inferred_type_arguments=false 46 | sp_cleanup.instanceof=false 47 | sp_cleanup.instanceof_keyword=false 48 | sp_cleanup.invert_equals=false 49 | sp_cleanup.join=false 50 | sp_cleanup.lazy_logical_operator=false 51 | sp_cleanup.make_local_variable_final=true 52 | sp_cleanup.make_parameters_final=false 53 | sp_cleanup.make_private_fields_final=true 54 | sp_cleanup.make_type_abstract_if_missing_method=false 55 | sp_cleanup.make_variable_declarations_final=true 56 | sp_cleanup.map_cloning=false 57 | sp_cleanup.merge_conditional_blocks=false 58 | sp_cleanup.multi_catch=false 59 | sp_cleanup.never_use_blocks=false 60 | sp_cleanup.never_use_parentheses_in_expressions=true 61 | sp_cleanup.no_string_creation=false 62 | sp_cleanup.no_super=false 63 | sp_cleanup.number_suffix=false 64 | sp_cleanup.objects_equals=false 65 | sp_cleanup.on_save_use_additional_actions=true 66 | sp_cleanup.one_if_rather_than_duplicate_blocks_that_fall_through=false 67 | sp_cleanup.operand_factorization=false 68 | sp_cleanup.organize_imports=true 69 | sp_cleanup.overridden_assignment=false 70 | sp_cleanup.plain_replacement=false 71 | sp_cleanup.precompile_regex=false 72 | sp_cleanup.primitive_comparison=false 73 | sp_cleanup.primitive_parsing=false 74 | sp_cleanup.primitive_rather_than_wrapper=false 75 | sp_cleanup.primitive_serialization=false 76 | sp_cleanup.pull_out_if_from_if_else=false 77 | sp_cleanup.pull_up_assignment=false 78 | sp_cleanup.push_down_negation=false 79 | sp_cleanup.qualify_static_field_accesses_with_declaring_class=false 80 | sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true 81 | sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true 82 | sp_cleanup.qualify_static_member_accesses_with_declaring_class=false 83 | sp_cleanup.qualify_static_method_accesses_with_declaring_class=false 84 | sp_cleanup.reduce_indentation=false 85 | sp_cleanup.redundant_comparator=false 86 | sp_cleanup.redundant_falling_through_block_end=false 87 | sp_cleanup.remove_private_constructors=true 88 | sp_cleanup.remove_redundant_modifiers=false 89 | sp_cleanup.remove_redundant_semicolons=false 90 | sp_cleanup.remove_redundant_type_arguments=true 91 | sp_cleanup.remove_trailing_whitespaces=false 92 | sp_cleanup.remove_trailing_whitespaces_all=true 93 | sp_cleanup.remove_trailing_whitespaces_ignore_empty=false 94 | sp_cleanup.remove_unnecessary_array_creation=false 95 | sp_cleanup.remove_unnecessary_casts=true 96 | sp_cleanup.remove_unnecessary_nls_tags=false 97 | sp_cleanup.remove_unused_imports=false 98 | sp_cleanup.remove_unused_local_variables=false 99 | sp_cleanup.remove_unused_private_fields=true 100 | sp_cleanup.remove_unused_private_members=false 101 | sp_cleanup.remove_unused_private_methods=true 102 | sp_cleanup.remove_unused_private_types=true 103 | sp_cleanup.return_expression=false 104 | sp_cleanup.simplify_lambda_expression_and_method_ref=false 105 | sp_cleanup.single_used_field=false 106 | sp_cleanup.sort_members=false 107 | sp_cleanup.sort_members_all=false 108 | sp_cleanup.standard_comparison=false 109 | sp_cleanup.static_inner_class=false 110 | sp_cleanup.strictly_equal_or_different=false 111 | sp_cleanup.stringbuffer_to_stringbuilder=false 112 | sp_cleanup.stringbuilder=false 113 | sp_cleanup.stringbuilder_for_local_vars=false 114 | sp_cleanup.stringconcat_to_textblock=false 115 | sp_cleanup.substring=false 116 | sp_cleanup.switch=false 117 | sp_cleanup.system_property=false 118 | sp_cleanup.system_property_boolean=false 119 | sp_cleanup.system_property_file_encoding=false 120 | sp_cleanup.system_property_file_separator=false 121 | sp_cleanup.system_property_line_separator=false 122 | sp_cleanup.system_property_path_separator=false 123 | sp_cleanup.ternary_operator=false 124 | sp_cleanup.try_with_resource=false 125 | sp_cleanup.unlooped_while=false 126 | sp_cleanup.unreachable_block=false 127 | sp_cleanup.use_anonymous_class_creation=false 128 | sp_cleanup.use_autoboxing=false 129 | sp_cleanup.use_blocks=true 130 | sp_cleanup.use_blocks_only_for_return_and_throw=false 131 | sp_cleanup.use_directly_map_method=false 132 | sp_cleanup.use_lambda=true 133 | sp_cleanup.use_parentheses_in_expressions=false 134 | sp_cleanup.use_string_is_blank=false 135 | sp_cleanup.use_this_for_non_static_field_access=false 136 | sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true 137 | sp_cleanup.use_this_for_non_static_method_access=false 138 | sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true 139 | sp_cleanup.use_type_arguments=false 140 | sp_cleanup.use_unboxing=false 141 | sp_cleanup.use_var=false 142 | sp_cleanup.useless_continue=false 143 | sp_cleanup.useless_return=false 144 | sp_cleanup.valueof_rather_than_instantiation=false 145 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.configuration.updateBuildConfiguration": "automatic", 3 | "editor.formatOnSave": true, 4 | "editor.codeActionsOnSave": { 5 | "source.organizeImports": "explicit", 6 | "source.generate.finalModifiers": "explicit", 7 | "source.fixAll": "explicit" 8 | }, 9 | "java.codeGeneration.useBlocks": true, 10 | "java.saveActions.organizeImports": true, 11 | "java.sources.organizeImports.starThreshold": 3, 12 | "java.sources.organizeImports.staticStarThreshold": 3, 13 | "sonarlint.connectedMode.project": { 14 | "connectionId": "kaklakariada-github", 15 | "projectKey": "com.github.kaklakariada:fritzbox-java-api" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [1.8.0] - unreleased 9 | 10 | - [#65](https://github.com/kaklakariada/fritzbox-java-api/pull/65) Upgrade dependencies 11 | 12 | ## [1.7.0] - 2023-10-07 13 | 14 | ### Added 15 | 16 | - [#29](https://github.com/kaklakariada/fritzbox-java-api/issues/29) Add support for login via Pbkdf2 challenge-response (propsed by [@linnex81](https://github.com/linnex81)) 17 | - [#58](https://github.com/kaklakariada/fritzbox-java-api/pull/58) Datatime attribute added in devicestats XML response since Fritz OS 7.50 (thanks to [@LutzHelling](https://github.com/LutzHelling)) 18 | 19 | ### Fixed 20 | 21 | - [#37](https://github.com/kaklakariada/fritzbox-java-api/issues/37) Support empty device lists 22 | 23 | ### Refactoring 24 | 25 | - [#50](https://github.com/kaklakariada/fritzbox-java-api/pull/50) Migrate to Junit 5 26 | - [#59](https://github.com/kaklakariada/fritzbox-java-api/pull/59) Upgrade dependencies 27 | 28 | ## [1.6.0] - 2021-12-04 29 | 30 | ### Added 31 | 32 | - [#39](https://github.com/kaklakariada/fritzbox-java-api/pull/39) Add new command "getdeviceinfo" (thanks to [@odin568](https://github.com/odin568)) 33 | - [#46](https://github.com/kaklakariada/fritzbox-java-api/pull/46) Add additional attributes to group (thanks to [@TDesjardins](https://github.com/TDesjardins)) 34 | - [#47](https://github.com/kaklakariada/fritzbox-java-api/pull/47) Add hkr attribute supported by FritzOS 7.29 (thanks to [@philippn](https://github.com/philippn)) 35 | - [#38](https://github.com/kaklakariada/fritzbox-java-api/pull/38) Add commands to set status for hkr (tsoll) and for blind (open, close etc.) (thanks to [@JunkerMartin](https://github.com/JunkerMartin)) 36 | 37 | ### Updated 38 | 39 | - [#45](https://github.com/kaklakariada/fritzbox-java-api/pull/45) Upgrade dependencies 40 | 41 | ## [1.5.0] - 2021-12-04 42 | 43 | ### Added 44 | 45 | - [#34](https://github.com/kaklakariada/fritzbox-java-api/pull/34) Support new device "Blind" and supply device statistics (thanks to [@JunkerMartin](https://github.com/JunkerMartin) for his contribution!) 46 | 47 | ### Updated 48 | 49 | - [#35](https://github.com/kaklakariada/fritzbox-java-api/pull/35) Upgraded dependencies 50 | 51 | ## [1.4.0] - 2021-03-03 52 | 53 | ### Added 54 | 55 | - [#18](https://github.com/kaklakariada/fritzbox-java-api/pull/18) Added new fields to be compatible with Fritz!OS 7.25 56 | - Device (Fritz!Dect440) serves now relative humidity 57 | - ColorControl (Fritz!Dect500) got some more fields 58 | - Added some getter for fields that did miss them 59 | - [#19](https://github.com/kaklakariada/fritzbox-java-api/pull/19) Support for device groups 60 | 61 | ## [1.3.1] - 2021-02-26 62 | 63 | - No changes, update deployment to Maven Central 64 | 65 | ## [1.3.0] - 2021-02-22 66 | 67 | ### Breaking Changes 68 | 69 | - Requires Java 11 instead of Java 8 70 | - Moved from JCenter to Maven Central due to [deprecation of JCenter](https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/). In your build script use 71 | 72 | ```gradle 73 | repositories { 74 | mavenCentral() 75 | } 76 | ``` 77 | 78 | ## [1.2.2] - 2021-01-16 79 | 80 | ### Added 81 | 82 | - [#17](https://github.com/kaklakariada/fritzbox-java-api/pull/17): Update for Fritz-Dect!4XX devices. Thanks to [SmokingDevices](https://github.com/SmokingDevices)! 83 | 84 | ## [1.2.1] - 2020-12-06 85 | 86 | ### Added 87 | 88 | - [#16](https://github.com/kaklakariada/fritzbox-java-api/pull/16): Update for FRITZ-Dect 500 devices. Thanks to [SmokingDevices](https://github.com/SmokingDevices)! 89 | 90 | ## [1.2.0] - 2020-12-06 - invalid 91 | 92 | ## [1.1.0] - 2020-10-25 93 | 94 | ### Added 95 | 96 | - [#15](https://github.com/kaklakariada/fritzbox-java-api/pull/15): Added new fields to be compatible with Fritz!OS 7.21. Thanks to [philippn](https://github.com/philippn)! 97 | 98 | ## [1.0.0] - 2019-12-27 99 | 100 | ### Breaking change 101 | 102 | - Make Device properties `battery` and `batterylow` optional to distinguish between missing values and zero, see [issue #11](https://github.com/kaklakariada/fritzbox-java-api/issues/11). 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fritzbox-java-api 2 | 3 | [![Java CI](https://github.com/kaklakariada/fritzbox-java-api/workflows/Java%20CI/badge.svg)](https://github.com/kaklakariada/fritzbox-java-api/actions?query=workflow%3A%22Java+CI%22) 4 | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=com.github.kaklakariada%3Afritzbox-java-api&metric=alert_status)](https://sonarcloud.io/dashboard?id=com.github.kaklakariada%3Afritzbox-java-api) 5 | [![Maven Central](https://img.shields.io/maven-central/v/com.github.kaklakariada/fritzbox-java-api)](https://search.maven.org/artifact/com.github.kaklakariada/fritzbox-java-api) 6 | 7 | Java API for managing FritzBox HomeAutomation using [AVM Home Automation HTTP Interface](https://avm.de/fileadmin/user_upload/Global/Service/Schnittstellen/AHA-HTTP-Interface.pdf) inspired by grundid's [fritzbox-java-api](https://github.com/grundid/fritzbox-java-api). This also runs on Android devices (see [Andect](https://github.com/kaklakariada/Andect)). 8 | 9 | ## Important: Migration to Maven Central 10 | 11 | Due to the [deprecation of JCenter](https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/) new versions will be published to [Maven Central](https://search.maven.org/artifact/com.github.kaklakariada/fritzbox-java-api). In your build script please use 12 | 13 | ```groovy 14 | repositories { 15 | mavenCentral() 16 | } 17 | ``` 18 | 19 | ## Changelog 20 | 21 | See [CHANGELOG.md](CHANGELOG.md). 22 | 23 | ## Usage 24 | 25 | ### Gradle 26 | 27 | * Add Maven Central maven repository: 28 | 29 | ```groovy 30 | repositories { 31 | mavenCentral() 32 | } 33 | ``` 34 | 35 | * Add dependency 36 | 37 | ```groovy 38 | dependencies { 39 | compile 'com.github.kaklakariada:fritzbox-java-api:1.6.1' 40 | } 41 | ``` 42 | 43 | ### Maven 44 | 45 | ```xml 46 | 47 | com.github.kaklakariada 48 | fritzbox-java-api 49 | 1.6.1 50 | 51 | ``` 52 | 53 | ### Run sample program 54 | 55 | 1. Create file `application.properties` with the following content and enter settings for your device: 56 | 57 | ``` properties 58 | fritzbox.url = https://fritz.box 59 | fritzbox.username = user 60 | fritzbox.password = secret 61 | ``` 62 | 2. Run example class [`TestDriver`](https://github.com/kaklakariada/fritzbox-java-api/blob/main/src/main/java/com/github/kaklakariada/fritzbox/TestDriver.java). 63 | 64 | ## Development 65 | 66 | ### Generate / update license header 67 | 68 | ```sh 69 | ./gradlew licenseFormat 70 | ``` 71 | 72 | ### Check if dependencies are up-to-date 73 | 74 | ```sh 75 | ./gradlew dependencyUpdates 76 | ``` 77 | 78 | ### Check dependencies for vulnerabilities 79 | 80 | ```sh 81 | ./gradlew ossIndexAudit 82 | ``` 83 | 84 | ### Building 85 | 86 | Install to local maven repository: 87 | ```sh 88 | ./gradlew clean publishToMavenLocal 89 | ``` 90 | 91 | ### Publish to Maven Central 92 | 93 | 1. Add the following to your `~/.gradle/gradle.properties`: 94 | 95 | ```properties 96 | ossrhUsername= 97 | ossrhPassword= 98 | 99 | signing.keyId= 100 | signing.password= 101 | signing.secretKeyRingFile= 102 | ``` 103 | 104 | 2. Increment version number in `build.gradle` and `README.md`, update [CHANGELOG.md](CHANGELOG.md), commit and push. 105 | 106 | 3. Optional: run the following command to do a dry-run: 107 | 108 | ```sh 109 | ./gradlew clean check build publishToSonatype closeSonatypeStagingRepository --info 110 | ``` 111 | 112 | 4. Run the following command to publish to Maven Central: 113 | 114 | ```sh 115 | ./gradlew clean check build publishToSonatype closeAndReleaseSonatypeStagingRepository --info 116 | ``` 117 | 118 | 5. Create a new [release](https://github.com/kaklakariada/fritzbox-java-api/releases) on GitHub. 119 | 6. After some time the release will be available at [Maven Central](https://repo1.maven.org/maven2/com/github/kaklakariada/fritzbox-java-api/). 120 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "java-library" 3 | id "signing" 4 | id "maven-publish" 5 | id "jacoco" 6 | id "com.github.hierynomus.license" version "0.16.1" 7 | id "org.sonarqube" version "6.0.1.5171" 8 | id "io.github.gradle-nexus.publish-plugin" version "2.0.0" 9 | id "com.github.ben-manes.versions" version "0.51.0" 10 | id "org.sonatype.gradle.plugins.scan" version "2.8.3" 11 | } 12 | 13 | group = 'com.github.kaklakariada' 14 | version = '1.7.0' 15 | 16 | java { 17 | toolchain { 18 | def javaVersion = project.hasProperty('javaVersion') ? project.getProperty('javaVersion') : 11 19 | languageVersion = JavaLanguageVersion.of(javaVersion) 20 | } 21 | withJavadocJar() 22 | withSourcesJar() 23 | } 24 | 25 | javadoc { 26 | failOnError = false 27 | options.addBooleanOption('html5', true) 28 | } 29 | 30 | tasks.withType(JavaCompile) { 31 | options.compilerArgs << '-Xlint:all' 32 | options.encoding = 'UTF-8' 33 | } 34 | 35 | testing { 36 | suites { 37 | test { 38 | useJUnitJupiter() 39 | } 40 | } 41 | } 42 | 43 | dependencies { 44 | implementation 'com.squareup.okhttp3:okhttp:4.12.0' 45 | implementation 'com.subshell.simpleframework:simple-xml:2.9.0' 46 | implementation 'org.slf4j:slf4j-api:2.0.16' 47 | 48 | testRuntimeOnly 'ch.qos.logback:logback-classic:1.5.15' 49 | 50 | testImplementation 'org.mockito:mockito-core:5.14.2' 51 | testImplementation 'org.assertj:assertj-core:3.27.0' 52 | } 53 | 54 | license { 55 | header = file('gradle/license-header.txt') 56 | exclude('**/deviceList*Payload.xml') 57 | exclude('devicelist6840.xml') 58 | exclude('**/FritzOS29/*.xml') 59 | } 60 | 61 | jacocoTestReport { 62 | reports { 63 | xml.required = true 64 | } 65 | } 66 | 67 | sonar { 68 | properties { 69 | property("sonar.organization", "kaklakariada-github") 70 | property("sonar.host.url", "https://sonarcloud.io") 71 | property("sonar.gradle.skipCompile", "true") 72 | } 73 | } 74 | 75 | rootProject.tasks["sonarqube"].dependsOn(tasks["jacocoTestReport"]) 76 | 77 | def getOptionalProperty(String name) { 78 | if(project.hasProperty(name)) { 79 | return project.property(name) 80 | } 81 | logger.info("Project property '${name}' not available. Please add it to ~/.gradle/gradle.properties") 82 | return null 83 | } 84 | 85 | publishing { 86 | publications { 87 | mavenJava(MavenPublication) { 88 | from components.java 89 | pom { 90 | name = 'FritzBox Java API' 91 | description = 'Java API for managing FritzBox HomeAutomation ' 92 | url = 'https://github.com/kaklakariada/fritzbox-java-api' 93 | 94 | licenses { 95 | license { 96 | name = 'GNU General Public License, Version 3.0' 97 | url = 'https://www.gnu.org/licenses/gpl-3.0.txt' 98 | } 99 | } 100 | developers { 101 | developer { 102 | id = 'kaklakariada' 103 | name = 'Christoph' 104 | email = 'kaklakariada@chp1.net' 105 | } 106 | } 107 | scm { 108 | connection = 'scm:git:https://github.com/kaklakariada/fritzbox-java-api.git' 109 | developerConnection = 'scm:git:https://github.com/kaklakariada/fritzbox-java-api.git' 110 | url = 'https://github.com/kaklakariada/fritzbox-java-api' 111 | } 112 | } 113 | } 114 | } 115 | 116 | repositories { 117 | maven { 118 | def releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 119 | def snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/" 120 | url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl 121 | allowInsecureProtocol = false 122 | credentials(PasswordCredentials) { 123 | username = getOptionalProperty("ossrhUsername") 124 | password = getOptionalProperty("ossrhPassword") 125 | } 126 | } 127 | } 128 | } 129 | 130 | 131 | signing { 132 | sign publishing.publications.mavenJava 133 | } 134 | 135 | nexusPublishing { 136 | packageGroup = project.group 137 | repositories { 138 | sonatype { 139 | stagingProfileId = "4f1b95618be44" 140 | username = getOptionalProperty("ossrhUsername") 141 | password = getOptionalProperty("ossrhPassword") 142 | } 143 | } 144 | } 145 | 146 | def isNonStable = { String version -> 147 | def stableKeyword = ['RELEASE', 'FINAL', 'GA'].any { it -> version.toUpperCase().contains(it) } 148 | def regex = /^[0-9,.v-]+(-r)?$/ 149 | return !stableKeyword && !(version ==~ regex) 150 | } 151 | 152 | tasks.named("dependencyUpdates").configure { 153 | gradleReleaseChannel = "current" 154 | rejectVersionIf { 155 | isNonStable(it.candidate.version) && !isNonStable(it.currentVersion) 156 | } 157 | } 158 | 159 | ossIndexAudit { 160 | allConfigurations = false 161 | useCache = true 162 | excludeVulnerabilityIds = [] 163 | printBanner = false 164 | } 165 | 166 | rootProject.tasks["build"].dependsOn(tasks["ossIndexAudit"]) 167 | -------------------------------------------------------------------------------- /gradle/license-header.txt: -------------------------------------------------------------------------------- 1 | A Java API for managing FritzBox HomeAutomation 2 | Copyright (C) 2017 Christoph Pirkl 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaklakariada/fritzbox-java-api/9fbac0d7f2161b6a25589c6b1cc2d107dcaf6436/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0' 3 | } 4 | 5 | rootProject.name='fritzbox-java-api' 6 | 7 | dependencyResolutionManagement { 8 | repositories { 9 | mavenCentral() 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/AbstractTestHelper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox; 19 | 20 | public abstract class AbstractTestHelper { 21 | 22 | protected String getAin(final String identifier) { 23 | return identifier.replaceAll("\\s*", ""); 24 | } 25 | 26 | protected double getCelsius(int code) { 27 | return code / 2D; 28 | } 29 | 30 | protected int getDegreeCode(double celsius) { 31 | return (int) (celsius * 2D); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/Config.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox; 19 | 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.io.UncheckedIOException; 23 | import java.nio.file.Files; 24 | import java.nio.file.Path; 25 | import java.nio.file.Paths; 26 | import java.util.Optional; 27 | import java.util.Properties; 28 | 29 | import org.slf4j.Logger; 30 | import org.slf4j.LoggerFactory; 31 | 32 | public class Config { 33 | private static final Logger LOG = LoggerFactory.getLogger(Config.class); 34 | private static final Path DEFAULT_CONFIG = Paths.get("application.properties"); 35 | private final Properties properties; 36 | 37 | private Config(final Properties properties) { 38 | this.properties = properties; 39 | } 40 | 41 | public static Config read() { 42 | return read(DEFAULT_CONFIG); 43 | } 44 | 45 | private static Config read(final Path configFile) { 46 | final Path file = configFile.normalize(); 47 | return new Config(loadProperties(file)); 48 | } 49 | 50 | private static Properties loadProperties(final Path configFile) { 51 | if (!Files.exists(configFile)) { 52 | throw new IllegalStateException("Config file not found at '" + configFile + "'"); 53 | } 54 | LOG.info("Reading config file from {}", configFile); 55 | try (InputStream stream = Files.newInputStream(configFile)) { 56 | final Properties props = new Properties(); 57 | props.load(stream); 58 | return props; 59 | } catch (final IOException e) { 60 | throw new UncheckedIOException("Error reading config file " + configFile, e); 61 | } 62 | } 63 | 64 | public String getUrl() { 65 | return getMandatoryValue("fritzbox.url"); 66 | } 67 | 68 | public String getUsername() { 69 | return getMandatoryValue("fritzbox.username"); 70 | } 71 | 72 | public String getPassword() { 73 | return getMandatoryValue("fritzbox.password"); 74 | } 75 | 76 | private String getMandatoryValue(final String param) { 77 | return getOptionalValue(param).orElseThrow( 78 | () -> new IllegalStateException("Property '" + param + "' not found in config file")); 79 | } 80 | 81 | private Optional getOptionalValue(final String param) { 82 | return Optional.ofNullable(this.properties.getProperty(param)); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/EnergyStatisticsService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox; 19 | 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import com.github.kaklakariada.fritzbox.http.QueryParameters; 24 | 25 | /** 26 | * This class retrieves energy statistics. 27 | */ 28 | public class EnergyStatisticsService { 29 | 30 | private static final Logger LOG = LoggerFactory.getLogger(EnergyStatisticsService.class); 31 | private static final String QUERY_PATH = "/net/home_auto_query.lua"; 32 | 33 | public enum EnergyStatsTimeRange { 34 | TEN_MINUTES("EnergyStats_10"), // 35 | ONE_HOUR("EnergyStats_hour"), // 36 | ONE_DAY("EnergyStats_24h"), // 37 | ONE_WEEK("EnergyStats_week"), // 38 | ONE_MONTH("EnergyStats_month"), // 39 | ONE_YEAR("EnergyStats_year"); 40 | 41 | private final String command; 42 | 43 | private EnergyStatsTimeRange(String command) { 44 | this.command = command; 45 | } 46 | } 47 | 48 | private final FritzBoxSession session; 49 | 50 | public EnergyStatisticsService(FritzBoxSession session) { 51 | this.session = session; 52 | } 53 | 54 | public String getEnergyStatistics(String deviceId, EnergyStatsTimeRange timeRange) { 55 | return executeDeviceCommand(deviceId, timeRange.command); 56 | } 57 | 58 | private String executeDeviceCommand(String deviceId, String command) { 59 | final QueryParameters parameters = QueryParameters.builder().add("command", command).add("id", deviceId) 60 | .add("xhr", "1").build(); 61 | final String statisticsJson = session.getAutenticated(QUERY_PATH, parameters, String.class); 62 | LOG.trace("Got statistics json for command '{}': {}", command, statisticsJson); 63 | return statisticsJson; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/FritzBoxException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox; 19 | 20 | public class FritzBoxException extends RuntimeException { 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | public FritzBoxException(String message, Throwable cause) { 25 | super(message, cause); 26 | } 27 | 28 | public FritzBoxException(String message) { 29 | super(message); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/FritzBoxSession.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox; 19 | 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import com.github.kaklakariada.fritzbox.http.HttpTemplate; 24 | import com.github.kaklakariada.fritzbox.http.QueryParameters; 25 | import com.github.kaklakariada.fritzbox.login.ChallengeResponse; 26 | import com.github.kaklakariada.fritzbox.model.SessionInfo; 27 | 28 | /** 29 | * This class allows logging in to a fritzbox and execute authenticated requests. 30 | */ 31 | public class FritzBoxSession { 32 | private static final Logger LOG = LoggerFactory.getLogger(FritzBoxSession.class); 33 | 34 | private static final String LOGIN_PATH = "/login_sid.lua"; 35 | private static final String WEBCM_PATH = "/home/home.lua"; 36 | private static final String EMPTY_SESSION_ID = "0000000000000000"; 37 | 38 | private String sid; 39 | 40 | private final HttpTemplate httpTemplate; 41 | 42 | FritzBoxSession(final String baseUrl) { 43 | this(new HttpTemplate(baseUrl)); 44 | } 45 | 46 | FritzBoxSession(final HttpTemplate httpTemplate) { 47 | this(httpTemplate, null); 48 | } 49 | 50 | private FritzBoxSession(final HttpTemplate httpTemplate, final String sid) { 51 | this.httpTemplate = httpTemplate; 52 | this.sid = sid; 53 | } 54 | 55 | public String getSid() { 56 | return sid; 57 | } 58 | 59 | public void login(final String username, final String password) { 60 | final SessionInfo sessionWithChallenge = httpTemplate.get(LOGIN_PATH, 61 | QueryParameters.builder().add("version", "2").build(), SessionInfo.class); 62 | if (!EMPTY_SESSION_ID.equals(sessionWithChallenge.getSid())) { 63 | throw new FritzBoxException("Already logged in: " + sessionWithChallenge); 64 | } 65 | final String response = createResponse(sessionWithChallenge.getChallenge(), password); 66 | LOG.debug("Got response {} for challenge {}", response, sessionWithChallenge.getChallenge()); 67 | 68 | final QueryParameters arguments = QueryParameters.builder() // 69 | .add("username", username == null ? "" : username) // 70 | .add("response", response) // 71 | .build(); 72 | final SessionInfo sessionInfo = httpTemplate.get(LOGIN_PATH, arguments, SessionInfo.class); 73 | if (EMPTY_SESSION_ID.equals(sessionInfo.getSid())) { 74 | throw new LoginFailedException(sessionInfo); 75 | } 76 | LOG.debug("Logged in with session id {} and rights {}", sessionInfo.getSid(), sessionInfo.getRights()); 77 | this.sid = sessionInfo.getSid(); 78 | } 79 | 80 | private String createResponse(final String challenge, final String password) { 81 | return ChallengeResponse.getAlgorithm(challenge).calculateResponse(challenge, password); 82 | } 83 | 84 | public T getAutenticated(final String path, final QueryParameters parameters, final Class resultType) { 85 | if (sid == null) { 86 | throw new FritzBoxException("Not logged in, session id is null"); 87 | } 88 | final QueryParameters parametersWithSessionId = parameters.newBuilder().add("sid", this.sid).build(); 89 | return httpTemplate.get(path, parametersWithSessionId, resultType); 90 | } 91 | 92 | public void logout() { 93 | httpTemplate.get(WEBCM_PATH, QueryParameters.builder().add("sid", sid).add("logout", "1").build(), 94 | String.class); 95 | LOG.debug("Logged out, invalidate sid"); 96 | sid = null; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/LoginFailedException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox; 19 | 20 | import com.github.kaklakariada.fritzbox.model.SessionInfo; 21 | 22 | public class LoginFailedException extends FritzBoxException { 23 | 24 | private static final long serialVersionUID = 1L; 25 | 26 | public LoginFailedException(SessionInfo session) { 27 | super("Login failed, blocked for " + session.getBlockTime() + " min"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/MissingClassException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox; 19 | 20 | public class MissingClassException extends RuntimeException { 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | public MissingClassException(String message, Throwable cause) { 25 | super(message, cause); 26 | } 27 | 28 | public MissingClassException(String message) { 29 | super(message); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/TestBlind.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox; 19 | 20 | import java.util.List; 21 | import java.util.stream.Collectors; 22 | 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | import com.github.kaklakariada.fritzbox.model.homeautomation.Device; 27 | import com.github.kaklakariada.fritzbox.model.homeautomation.DeviceList; 28 | 29 | /** 30 | * Sample driver for Blind 31 | * 32 | * @author Junker Martin 33 | * 34 | */ 35 | public class TestBlind extends AbstractTestHelper { 36 | 37 | private static final Logger LOG = LoggerFactory.getLogger(TestBlind.class); 38 | private static final int WAIT_SECONDS = 20; 39 | 40 | private final HomeAutomation homeAutomation; 41 | 42 | public TestBlind() throws InterruptedException { 43 | this.homeAutomation = HomeAutomation.connect(Config.read()); 44 | 45 | // make sure to set back blind to original state 46 | final List blindDevices = getBlindDevices(); 47 | if (blindDevices.isEmpty()) { 48 | LOG.warn("No blind devices found"); 49 | return; 50 | } 51 | final int wasPercenClosed = blindDevices.get(0).getLevelControl().getLevelpercentage(); 52 | 53 | toggleBlindOpenClose(); 54 | 55 | togglePercentOpen(); 56 | 57 | setPercentOpen(blindDevices.get(0), wasPercenClosed); 58 | } 59 | 60 | private void toggleBlindOpenClose() throws InterruptedException { 61 | // Start first move 62 | LOG.info(""); 63 | LOG.info("Initial setting"); 64 | List blindDevices = getBlindDevices(); 65 | if (blindDevices.isEmpty()) { 66 | return; 67 | } 68 | showStatus(blindDevices); 69 | toggleBlindOpenClose(blindDevices.get(0)); 70 | 71 | // Start move back 72 | sleep(); 73 | LOG.info(""); 74 | LOG.info("Status after change"); 75 | blindDevices = getBlindDevices(); 76 | showStatus(blindDevices); 77 | toggleBlindOpenClose(blindDevices.get(0)); 78 | 79 | // Show status at end of test 80 | LOG.info(""); 81 | sleep(); 82 | blindDevices = getBlindDevices(); 83 | showStatus(blindDevices); 84 | } 85 | 86 | private void sleep() throws InterruptedException { 87 | LOG.info("Wait {} seconds", WAIT_SECONDS); 88 | Thread.sleep(WAIT_SECONDS * 1000L); 89 | } 90 | 91 | private void toggleBlindOpenClose(final Device blind) { 92 | final String ain = getAin(blind.getIdentifier()); 93 | final boolean wasOpen = blind.getLevelControl().getLevel() == 0; 94 | final String newStatus = wasOpen ? "close" : "open"; 95 | 96 | LOG.info(""); 97 | LOG.info("Changing status of blind {} (ain='{}') to {}", blind.getName(), ain, 98 | newStatus); 99 | homeAutomation.setBlind(ain, newStatus); 100 | } 101 | 102 | private void togglePercentOpen() throws InterruptedException { 103 | // Start first move 104 | LOG.info(""); 105 | LOG.info("Initial setting"); 106 | List blindDevices = getBlindDevices(); 107 | if (blindDevices.isEmpty()) { 108 | return; 109 | } 110 | showStatus(blindDevices); 111 | final int wasPercenClosed = blindDevices.get(0).getLevelControl().getLevelpercentage(); 112 | final int newPercenClosed = wasPercenClosed == 0 ? 50 : wasPercenClosed / 2; 113 | 114 | setPercentOpen(blindDevices.get(0), newPercenClosed); 115 | 116 | sleep(); 117 | LOG.info(""); 118 | LOG.info("Status after change"); 119 | blindDevices = getBlindDevices(); 120 | showStatus(blindDevices); 121 | setPercentOpen(blindDevices.get(0), wasPercenClosed); 122 | 123 | // Show status at end of test 124 | LOG.info(""); 125 | sleep(); 126 | blindDevices = getBlindDevices(); 127 | showStatus(blindDevices); 128 | } 129 | 130 | private void setPercentOpen(final Device blind, final int percent) { 131 | final String ain = getAin(blind.getIdentifier()); 132 | final String newLevel = String.valueOf(percent); 133 | 134 | LOG.info(""); 135 | LOG.info("Changing status of blind {} (ain='{}') to {}", blind.getName(), ain, 136 | newLevel); 137 | homeAutomation.setLevelPercentage(ain, newLevel); 138 | } 139 | 140 | private List getBlindDevices() { 141 | final DeviceList devices = homeAutomation.getDeviceListInfos(); 142 | return devices.getDevices() 143 | .stream() 144 | .filter(device -> device.getBlind() != null) 145 | .collect(Collectors.toList()); 146 | } 147 | 148 | private void showStatus(final List blindDevices) { 149 | blindDevices.forEach(blind -> { 150 | final String message = String.format("%-15s Mode: %s Percent-Closed: %s%%", 151 | blind.getName(), 152 | blind.getBlind().getMode(), 153 | blind.getLevelControl().getLevelpercentage()); 154 | LOG.info(message); 155 | }); 156 | } 157 | 158 | public static void main(final String[] args) throws InterruptedException { 159 | new TestBlind(); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/TestHkrDriver.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox; 19 | 20 | import java.util.List; 21 | import java.util.stream.Collectors; 22 | 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | import com.github.kaklakariada.fritzbox.model.homeautomation.Device; 27 | import com.github.kaklakariada.fritzbox.model.homeautomation.DeviceList; 28 | 29 | /** 30 | * Sample driver for Hkr "Heizkörperregler(DE), Radiator regulator(EN)" 31 | * 32 | * @author Junker Martin 33 | * 34 | */ 35 | public class TestHkrDriver extends AbstractTestHelper { 36 | 37 | private static final Logger LOG = LoggerFactory.getLogger(TestHkrDriver.class); 38 | 39 | private final HomeAutomation homeAutomation; 40 | 41 | public TestHkrDriver() { 42 | this.homeAutomation = HomeAutomation.connect(Config.read()); 43 | 44 | LOG.info(""); 45 | LOG.info("Initial temperature"); 46 | List hkrDevices = getHkrDevices(); 47 | 48 | if (hkrDevices.isEmpty()) { 49 | LOG.warn("No HKR devices found"); 50 | return; 51 | } 52 | showTemperatures(hkrDevices); 53 | 54 | final String ain = hkrDevices.get(0).getIdentifier().replaceAll("\\s*", ""); 55 | final double wasTemperature = getCelsius(hkrDevices.get(0).getHkr().getTsoll()); 56 | final double newTsoll = 25D; 57 | LOG.info(""); 58 | LOG.info("Changing temperature of {} (ain='{}')to {} degrees", hkrDevices.get(0).getName(), ain, 59 | newTsoll); 60 | homeAutomation.setHkrTsoll(ain, String.valueOf(getDegreeCode(newTsoll))); 61 | 62 | LOG.info(""); 63 | LOG.info("Temperature after change"); 64 | hkrDevices = getHkrDevices(); 65 | showTemperatures(hkrDevices); 66 | 67 | homeAutomation.setHkrTsoll(ain, String.valueOf(getDegreeCode(wasTemperature))); 68 | LOG.info(""); 69 | LOG.info("Changing back temperature of {} (ain='{}')to {} degrees", hkrDevices.get(0).getName(), ain, 70 | wasTemperature); 71 | LOG.info(""); 72 | LOG.info("Temperature after changing back"); 73 | hkrDevices = getHkrDevices(); 74 | showTemperatures(hkrDevices); 75 | } 76 | 77 | private List getHkrDevices() { 78 | final DeviceList devices = homeAutomation.getDeviceListInfos(); 79 | final List hkrDevices = devices.getDevices() 80 | .stream() 81 | .filter(device -> device.getHkr() != null) 82 | .collect(Collectors.toList()); 83 | return hkrDevices; 84 | } 85 | 86 | private void showTemperatures(final List hkrDevices) { 87 | hkrDevices.forEach(hkr -> { 88 | final String message = String.format("%-15s tist: %s(%s\u00B0), tsoll: %s(%s\u00B0)", 89 | hkr.getName(), 90 | hkr.getHkr().getTist(), getCelsius(hkr.getHkr().getTist()), 91 | hkr.getHkr().getTsoll(), getCelsius(hkr.getHkr().getTsoll())); 92 | LOG.info(message); 93 | }); 94 | } 95 | 96 | public static void main(final String[] args) { 97 | new TestHkrDriver(); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/helper/StringHelper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.helper; 19 | 20 | public class StringHelper { 21 | 22 | private StringHelper() { 23 | // Not instantiable 24 | } 25 | 26 | /** 27 | *

28 | * Note that the method does not allow for a leading sign, either positive or negative. 29 | *

30 | * 31 | *
 32 |      * StringUtils.isIntegerNumber(null)  = false
 33 |      * StringHelper.isIntegerNumber(""))  = false
 34 |      * StringHelper.isIntegerNumber(" ")  = false
 35 |      * StringHelper.isIntegerNumber(" 1 ") = true
 36 |      * StringHelper.isIntegerNumber("123")  = true
 37 |      * StringUtils.isIntegerNumber("\u0967\u0968\u0969")  = true
 38 |      * StringHelper.isIntegerNumber("1.1") = false
 39 |      * StringHelper.isIntegerNumber("1.1D") = false
 40 |      * 
41 | * 42 | * 43 | * @param cs 44 | * the String to check, may be null 45 | * @return {@code true} if only contains digits or is enclosed by blanks, and is non-null 46 | */ 47 | public static boolean isIntegerNumber(final String cs) { 48 | if (isEmpty(cs) || !isNumeric(cs.trim())) { 49 | return false; 50 | } 51 | try { 52 | Integer.parseInt(cs.trim()); 53 | } catch (NumberFormatException nfe) { 54 | return false; 55 | } 56 | return true; 57 | } 58 | 59 | /** 60 | * Code copied 'as is' from apache-commons-lang3, class StringUtils.isNumeric() 61 | * 62 | *

63 | * Checks if the CharSequence contains only Unicode digits. A decimal point is not a Unicode digit and returns 64 | * false. 65 | *

66 | * 67 | *

68 | * {@code null} will return {@code false}. An empty CharSequence (length()=0) will return {@code false}. 69 | *

70 | * 71 | *

72 | * Note that the method does not allow for a leading sign, either positive or negative. Also, if a String passes the 73 | * numeric test, it may still generate a NumberFormatException when parsed by Integer.parseInt or Long.parseLong, 74 | * e.g. if the value is outside the range for int or long respectively. 75 | *

76 | * 77 | *
 78 |      * StringUtils.isNumeric(null)   = false
 79 |      * StringUtils.isNumeric("")     = false
 80 |      * StringUtils.isNumeric("  ")   = false
 81 |      * StringUtils.isNumeric("123")  = true
 82 |      * StringUtils.isNumeric("\u0967\u0968\u0969")  = true
 83 |      * StringUtils.isNumeric("12 3") = false
 84 |      * StringUtils.isNumeric("ab2c") = false
 85 |      * StringUtils.isNumeric("12-3") = false
 86 |      * StringUtils.isNumeric("12.3") = false
 87 |      * StringUtils.isNumeric("-123") = false
 88 |      * StringUtils.isNumeric("+123") = false
 89 |      * 
90 | * 91 | * @param cs 92 | * the CharSequence to check, may be null 93 | * @return {@code true} if only contains digits, and is non-null 94 | */ 95 | public static boolean isNumeric(final CharSequence cs) { 96 | if (isEmpty(cs)) { 97 | return false; 98 | } 99 | final int sz = cs.length(); 100 | for (int i = 0; i < sz; i++) { 101 | if (!Character.isDigit(cs.charAt(i))) { 102 | return false; 103 | } 104 | } 105 | return true; 106 | } 107 | 108 | /** 109 | * Code copied 'as is' from apache-commons-lang3, class StringUtils.isEmpty() 110 | * 111 | *

112 | * Checks if a CharSequence is empty ("") or null. 113 | *

114 | * 115 | *
116 |      * StringUtils.isEmpty(null)      = true
117 |      * StringUtils.isEmpty("")        = true
118 |      * StringUtils.isEmpty(" ")       = false
119 |      * StringUtils.isEmpty("bob")     = false
120 |      * StringUtils.isEmpty("  bob  ") = false
121 |      * 
122 | * 123 | * 124 | * @param cs 125 | * the CharSequence to check, may be null 126 | * @return {@code true} if the CharSequence is empty or null 127 | */ 128 | public static boolean isEmpty(final CharSequence cs) { 129 | return cs == null || cs.length() == 0; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/http/AccessForbiddenException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.http; 19 | 20 | public class AccessForbiddenException extends HttpException { 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | public AccessForbiddenException(String message, Throwable cause) { 25 | super(message, cause); 26 | } 27 | 28 | public AccessForbiddenException(String message) { 29 | super(message); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/http/HttpException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.http; 19 | 20 | import com.github.kaklakariada.fritzbox.FritzBoxException; 21 | 22 | public class HttpException extends FritzBoxException { 23 | 24 | private static final long serialVersionUID = 1L; 25 | 26 | public HttpException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | public HttpException(String message) { 31 | super(message); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/http/HttpTemplate.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.http; 19 | 20 | import java.io.IOException; 21 | import java.util.Map.Entry; 22 | 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | import com.github.kaklakariada.fritzbox.FritzBoxException; 27 | import com.github.kaklakariada.fritzbox.mapping.Deserializer; 28 | 29 | import okhttp3.HttpUrl; 30 | import okhttp3.HttpUrl.Builder; 31 | import okhttp3.MediaType; 32 | import okhttp3.OkHttpClient; 33 | import okhttp3.Request; 34 | import okhttp3.RequestBody; 35 | import okhttp3.Response; 36 | 37 | /** 38 | * This class allows executing http requests against a server. Responses are converted using the given 39 | * {@link Deserializer}. 40 | */ 41 | public class HttpTemplate { 42 | private static final Logger LOG = LoggerFactory.getLogger(HttpTemplate.class); 43 | 44 | private final OkHttpClient httpClient; 45 | private final HttpUrl baseUrl; 46 | private final Deserializer deserializer; 47 | 48 | public HttpTemplate(String baseUrl) { 49 | this(createUnsafeOkHttpClient(), new Deserializer(), HttpUrl.parse(baseUrl)); 50 | } 51 | 52 | HttpTemplate(OkHttpClient httpClient, Deserializer deserializer, HttpUrl baseUrl) { 53 | this.httpClient = httpClient; 54 | this.deserializer = deserializer; 55 | this.baseUrl = baseUrl; 56 | } 57 | 58 | private static OkHttpClient createUnsafeOkHttpClient() { 59 | final okhttp3.OkHttpClient.Builder builder = new OkHttpClient.Builder(); 60 | builder.sslSocketFactory(TrustSelfSignedCertificates.getUnsafeSslSocketFactory(), new NullTrustManager()); 61 | builder.hostnameVerifier(new NullHostnameVerifier()); 62 | return builder.build(); 63 | } 64 | 65 | public T get(String path, Class resultType) { 66 | return get(path, QueryParameters.builder().build(), resultType); 67 | } 68 | 69 | public T get(String path, QueryParameters parameters, Class resultType) { 70 | final HttpUrl url = createUrl(path, parameters); 71 | return get(resultType, url); 72 | } 73 | 74 | public T post(String path, QueryParameters parameters, Class resultType) { 75 | final HttpUrl url = createUrl(path, parameters); 76 | return post(resultType, url); 77 | } 78 | 79 | private T get(Class resultType, HttpUrl url) { 80 | final Request request = new Request.Builder().url(url).get().build(); 81 | final Response response = execute(request); 82 | return parse(response, resultType); 83 | } 84 | 85 | private T post(Class resultType, HttpUrl url) { 86 | final MediaType mediaType = MediaType.parse("application/xml"); 87 | final RequestBody emptyBody = RequestBody.create(new byte[0], mediaType); 88 | final Request request = new Request.Builder().url(url).post(emptyBody).build(); 89 | final Response response = execute(request); 90 | return parse(response, resultType); 91 | } 92 | 93 | private T parse(final Response response, Class resultType) { 94 | if (!response.isSuccessful()) { 95 | throw new FritzBoxException("Request failed: " + response); 96 | } 97 | if (response.code() == 500) { 98 | throw new FritzBoxException("Request failed: " + deserializer.getStringFromStream(response.body().byteStream())); 99 | } 100 | return deserializer.parse(response.body().byteStream(), resultType); 101 | } 102 | 103 | private HttpUrl createUrl(String path, QueryParameters parameters) { 104 | final Builder builder = baseUrl.newBuilder().encodedPath(path); 105 | for (final Entry param : parameters.getParameters().entrySet()) { 106 | builder.addQueryParameter(param.getKey(), param.getValue()); 107 | } 108 | return builder.build(); 109 | } 110 | 111 | private Response execute(Request request) { 112 | LOG.trace("Executing request {}", request); 113 | try { 114 | final Response response = httpClient.newCall(request).execute(); 115 | if (!response.isSuccessful()) { 116 | if (response.code() == 403) { 117 | throw new AccessForbiddenException( 118 | "Authentication failed, session id outdated or invalid: " + response); 119 | } 120 | throw new HttpException("Request failed with response " + response); 121 | } 122 | return response; 123 | } catch (final IOException e) { 124 | throw new HttpException("Error executing requst " + request, e); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/http/NullHostnameVerifier.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.http; 19 | 20 | import javax.net.ssl.HostnameVerifier; 21 | import javax.net.ssl.SSLSession; 22 | 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | public class NullHostnameVerifier implements HostnameVerifier { 27 | private static final Logger LOG = LoggerFactory.getLogger(NullHostnameVerifier.class); 28 | 29 | @Override 30 | public boolean verify(String hostname, SSLSession session) { 31 | LOG.trace("Ignore ssl certificate for {}: {}", hostname, session); 32 | return true; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/http/NullTrustManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.http; 19 | 20 | import java.security.cert.X509Certificate; 21 | 22 | import javax.net.ssl.X509TrustManager; 23 | 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | 27 | class NullTrustManager implements X509TrustManager { 28 | private static final Logger LOG = LoggerFactory.getLogger(NullTrustManager.class); 29 | 30 | // Don't throw exception, we want to accept any certificate 31 | @SuppressWarnings("squid:S4424") 32 | @Override 33 | public void checkClientTrusted(final X509Certificate[] xcs, final String authType) { 34 | LOG.trace("Check client trusted auth type '{}'", authType); 35 | } 36 | 37 | // Don't throw exception, we want to accept any certificate 38 | @SuppressWarnings("squid:S4424") 39 | @Override 40 | public void checkServerTrusted(final X509Certificate[] xcs, final String authType) { 41 | LOG.trace("Check server trusted auth type '{}'", authType); 42 | } 43 | 44 | @Override 45 | public X509Certificate[] getAcceptedIssuers() { 46 | LOG.trace("Get accepted issuers"); 47 | return new X509Certificate[0]; 48 | } 49 | } -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/http/QueryParameters.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.http; 19 | 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | 23 | public class QueryParameters { 24 | 25 | private final Map parameters; 26 | 27 | private QueryParameters(Map parameters) { 28 | this.parameters = parameters; 29 | } 30 | 31 | public static Builder builder() { 32 | return new Builder(); 33 | } 34 | 35 | public Map getParameters() { 36 | return parameters; 37 | } 38 | 39 | public Builder newBuilder() { 40 | return new Builder(new HashMap<>(this.parameters)); 41 | } 42 | 43 | public static class Builder { 44 | private final Map parameters; 45 | 46 | private Builder() { 47 | this(new HashMap()); 48 | } 49 | 50 | public Builder(Map parameters) { 51 | this.parameters = parameters; 52 | } 53 | 54 | public Builder add(String name, String value) { 55 | parameters.put(name, value); 56 | return this; 57 | } 58 | 59 | public QueryParameters build() { 60 | return new QueryParameters(parameters); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/http/TrustSelfSignedCertificates.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.http; 19 | 20 | import java.security.KeyManagementException; 21 | import java.security.NoSuchAlgorithmException; 22 | import java.security.SecureRandom; 23 | 24 | import javax.net.ssl.KeyManager; 25 | import javax.net.ssl.SSLContext; 26 | import javax.net.ssl.SSLSocketFactory; 27 | import javax.net.ssl.TrustManager; 28 | 29 | /** 30 | * This class creates an {@link SSLSocketFactory} that trusts all certificates. 31 | * 32 | * @see #getUnsafeSslSocketFactory() 33 | */ 34 | public class TrustSelfSignedCertificates { 35 | 36 | private TrustSelfSignedCertificates() { 37 | // Not instantiable 38 | } 39 | 40 | public static SSLSocketFactory getUnsafeSslSocketFactory() { 41 | final SSLContext sslContext = getSSLContext("TLS"); 42 | initializeSslContext(sslContext); 43 | return sslContext.getSocketFactory(); 44 | } 45 | 46 | private static void initializeSslContext(SSLContext sslContext) { 47 | final KeyManager[] keyManagers = null; 48 | final TrustManager[] trustManagers = new TrustManager[] { new NullTrustManager() }; 49 | final SecureRandom secureRandom = new SecureRandom(); 50 | final SSLContext sslContext1 = sslContext; 51 | try { 52 | sslContext1.init(keyManagers, trustManagers, secureRandom); 53 | } catch (final KeyManagementException e) { 54 | throw new HttpException("Error initializing ssl context", e); 55 | } 56 | } 57 | 58 | private static SSLContext getSSLContext(String algorithm) { 59 | try { 60 | return SSLContext.getInstance(algorithm); 61 | } catch (final NoSuchAlgorithmException e) { 62 | throw new HttpException("Algorithm " + algorithm + " not found", e); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/login/ChallengeResponse.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.login; 19 | 20 | public interface ChallengeResponse { 21 | String calculateResponse(final String challenge, final String password); 22 | 23 | static ChallengeResponse getAlgorithm(final String challenge) { 24 | if (challenge.startsWith("2$")) { 25 | return new Pbkdf2ChallengeResponse(); 26 | } else { 27 | return new Md5LoginChallengeResponse(new Md5Service()); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/login/Md5LoginChallengeResponse.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.login; 19 | 20 | class Md5LoginChallengeResponse implements ChallengeResponse { 21 | 22 | private final Md5Service md5Service; 23 | 24 | Md5LoginChallengeResponse(final Md5Service md5Service) { 25 | this.md5Service = md5Service; 26 | } 27 | 28 | @Override 29 | public String calculateResponse(final String challenge, final String password) { 30 | final String text = (challenge + "-" + password); 31 | return challenge + "-" + md5Service.md5(text); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/login/Md5Service.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.login; 19 | 20 | import java.nio.charset.StandardCharsets; 21 | import java.security.MessageDigest; 22 | import java.security.NoSuchAlgorithmException; 23 | 24 | class Md5Service { 25 | 26 | String md5(final String s) { 27 | final MessageDigest digest = getMd5MessageDigest(); 28 | final byte[] binary = digest.digest(s.getBytes(StandardCharsets.UTF_16LE)); 29 | return buildHexString(binary); 30 | } 31 | 32 | // Concatenating strings in a loop is ok here 33 | @SuppressWarnings("squid:S1643") 34 | private String buildHexString(final byte[] data) { 35 | final StringBuilder hexString = new StringBuilder(); 36 | for (final byte aMessageDigest : data) { 37 | String h = Integer.toHexString(0xFF & aMessageDigest); 38 | while (h.length() < 2) { 39 | h = "0" + h; 40 | } 41 | hexString.append(h); 42 | } 43 | return hexString.toString(); 44 | } 45 | 46 | @SuppressWarnings("java:S4790") // MD5 still required as fallback 47 | private MessageDigest getMd5MessageDigest() { 48 | try { 49 | return MessageDigest.getInstance("MD5"); 50 | } catch (final NoSuchAlgorithmException e) { 51 | throw new AssertionError("Error getting MD5 message digest", e); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/login/Pbkdf2ChallengeResponse.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.login; 19 | 20 | import java.nio.charset.StandardCharsets; 21 | import java.security.InvalidKeyException; 22 | import java.security.NoSuchAlgorithmException; 23 | 24 | import javax.crypto.Mac; 25 | import javax.crypto.spec.SecretKeySpec; 26 | 27 | /** 28 | * PBKDF2 challenge-response. See 29 | * https://avm.de/fileadmin/user_upload/Global/Service/Schnittstellen/AVM_Technical_Note_-_Session_ID_english_2021-05-03.pdf 30 | */ 31 | class Pbkdf2ChallengeResponse implements ChallengeResponse { 32 | 33 | @Override 34 | public String calculateResponse(final String challenge, final String password) { 35 | final String[] challengeParts = challenge.split("\\$"); 36 | if (challengeParts.length != 5) { 37 | throw new IllegalArgumentException("Challenge '" + challenge + "' has an invalid format"); 38 | } 39 | final int iter1 = Integer.parseInt(challengeParts[1]); 40 | final byte[] salt1 = fromHex(challengeParts[2]); 41 | final int iter2 = Integer.parseInt(challengeParts[3]); 42 | final byte[] salt2 = fromHex(challengeParts[4]); 43 | final byte[] hash1 = pbkdf2HmacSha256(password.getBytes(StandardCharsets.UTF_8), salt1, iter1); 44 | final byte[] hash2 = pbkdf2HmacSha256(hash1, salt2, iter2); 45 | return challengeParts[4] + "$" + toHex(hash2); 46 | } 47 | 48 | /** Hex string to bytes */ 49 | static byte[] fromHex(final String hexString) { 50 | final int len = hexString.length() / 2; 51 | final byte[] ret = new byte[len]; 52 | for (int i = 0; i < len; i++) { 53 | ret[i] = (byte) Short.parseShort(hexString.substring(i * 2, i * 54 | 2 + 2), 16); 55 | } 56 | return ret; 57 | } 58 | 59 | /** Byte array to hex string */ 60 | static String toHex(final byte[] bytes) { 61 | final StringBuilder s = new StringBuilder(bytes.length * 2); 62 | for (final byte b : bytes) { 63 | s.append(String.format("%02x", b)); 64 | } 65 | return s.toString(); 66 | } 67 | 68 | /** 69 | * Create a pbkdf2 HMAC by appling the Hmac iter times as specified. We can't use the Android-internal PBKDF2 here, 70 | * as it only accepts char[] arrays, not bytes (for multi-stage hashing) 71 | */ 72 | static byte[] pbkdf2HmacSha256(final byte[] password, final byte[] salt, final int iters) { 73 | final String algorithm = "HmacSHA256"; 74 | try { 75 | final Mac sha256mac = Mac.getInstance(algorithm); 76 | sha256mac.init(new SecretKeySpec(password, algorithm)); 77 | final byte[] ret = new byte[sha256mac.getMacLength()]; 78 | byte[] tmp = new byte[salt.length + 4]; 79 | System.arraycopy(salt, 0, tmp, 0, salt.length); 80 | tmp[salt.length + 3] = 1; 81 | for (int i = 0; i < iters; i++) { 82 | tmp = sha256mac.doFinal(tmp); 83 | for (int k = 0; k < ret.length; k++) { 84 | ret[k] ^= tmp[k]; 85 | } 86 | } 87 | return ret; 88 | } catch (NoSuchAlgorithmException | InvalidKeyException e) { 89 | throw new IllegalStateException("Failed to calculate HMAC", e); 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/mapping/Deserializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.mapping; 19 | 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.nio.charset.StandardCharsets; 23 | import java.util.Scanner; 24 | 25 | import org.simpleframework.xml.Serializer; 26 | import org.simpleframework.xml.core.Persister; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | /** 31 | * This class allows deserializing {@link String}s to a requested type. 32 | */ 33 | public class Deserializer { 34 | private static final Logger LOG = LoggerFactory.getLogger(Deserializer.class); 35 | 36 | private final Serializer xmlSerializer; 37 | 38 | public Deserializer() { 39 | this(new Persister()); 40 | } 41 | 42 | Deserializer(Serializer xmlSerializer) { 43 | this.xmlSerializer = xmlSerializer; 44 | } 45 | 46 | public T parse(InputStream data, Class resultType) { 47 | try { 48 | final T resultObject; 49 | if (resultType == String.class 50 | || resultType == Boolean.class 51 | || resultType == Integer.class) { 52 | resultObject = parseSimpleType(resultType, data); 53 | } else { 54 | resultObject = xmlSerializer.read(resultType, data); 55 | } 56 | LOG.trace("Parsed response: {}", resultObject); 57 | return resultObject; 58 | } catch (final Exception e) { 59 | throw new DeserializerException("Error parsing response body", e); 60 | } 61 | } 62 | 63 | private T parseSimpleType(Class resultType, InputStream data) throws IOException { 64 | final String string = getStringFromStream(data); 65 | if (resultType == String.class) { 66 | return resultType.cast(string); 67 | } else if (resultType == Boolean.class) { 68 | return resultType.cast("1".equals(string)); 69 | } else if (resultType == Integer.class) { 70 | if (string.isEmpty() || "inval".equals(string)) { 71 | return null; 72 | } 73 | return resultType.cast(Integer.parseInt(string)); 74 | } 75 | throw new IOException("Type '" + resultType + "' is not supported: " + string); 76 | } 77 | 78 | public String getStringFromStream(InputStream data) { 79 | final Scanner scanner = new Scanner(data, StandardCharsets.UTF_8.toString()); 80 | final String string = scanner.next(); 81 | scanner.close(); 82 | return string; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/mapping/DeserializerException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.mapping; 19 | 20 | import com.github.kaklakariada.fritzbox.FritzBoxException; 21 | 22 | public class DeserializerException extends FritzBoxException { 23 | 24 | private static final long serialVersionUID = 1L; 25 | 26 | public DeserializerException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | public DeserializerException(String message) { 31 | super(message); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/model/Rights.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.model; 19 | 20 | import java.util.List; 21 | 22 | import org.simpleframework.xml.ElementList; 23 | import org.simpleframework.xml.Root; 24 | 25 | @Root(name = "Rights") 26 | public class Rights { 27 | 28 | @ElementList(inline = true, required = false, type = UserRight.class) 29 | private List userRights; 30 | 31 | public List getRights() { 32 | return userRights; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "Rights [rights=" + userRights + "]"; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/model/SessionInfo.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.model; 19 | 20 | import java.util.List; 21 | 22 | import org.simpleframework.xml.Element; 23 | import org.simpleframework.xml.ElementList; 24 | import org.simpleframework.xml.Root; 25 | 26 | @Root(name = "SessionInfo") 27 | public class SessionInfo { 28 | 29 | @Element(name = "SID") 30 | private String sid; 31 | 32 | @Element(name = "Challenge") 33 | private String challenge; 34 | 35 | @Element(name = "BlockTime") 36 | private String blockTime; 37 | 38 | @ElementList(name = "Rights", inline = false, required = false) 39 | private List rights; 40 | 41 | @ElementList(name = "Users", inline = false, required = false) 42 | private List users; 43 | 44 | public String getSid() { 45 | return sid; 46 | } 47 | 48 | public String getChallenge() { 49 | return challenge; 50 | } 51 | 52 | public String getBlockTime() { 53 | return blockTime; 54 | } 55 | 56 | public List getRights() { 57 | return rights; 58 | } 59 | 60 | public List getUsers() { 61 | return users; 62 | } 63 | 64 | @Override 65 | public String toString() { 66 | return "SessionInfo [sid=" + sid + ", challenge=" + challenge + ", blockTime=" + blockTime + ", rights=" 67 | + rights + "]"; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/model/User.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.model; 19 | 20 | import org.simpleframework.xml.Attribute; 21 | import org.simpleframework.xml.Text; 22 | 23 | public class User { 24 | 25 | @Attribute(name = "last", required = false) 26 | private int last; 27 | 28 | @Text 29 | private String name; 30 | 31 | public boolean isLast() { 32 | return (last == 1); 33 | } 34 | 35 | public String getName() { 36 | return name; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/model/UserRight.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.model; 19 | 20 | import org.simpleframework.xml.Element; 21 | 22 | public class UserRight { 23 | 24 | @Element(name = "Name", required = false) 25 | private String name; 26 | @Element(name = "Access", required = false) 27 | private int access; 28 | 29 | @Override 30 | public String toString() { 31 | return "UserRight [name=" + name + ", access=" + access + "]"; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/model/homeautomation/AbstractDeviceStatistics.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.model.homeautomation; 19 | 20 | import java.util.List; 21 | import java.util.Optional; 22 | import java.util.stream.Collectors; 23 | 24 | public abstract class AbstractDeviceStatistics { 25 | 26 | /** 27 | * Supply the Statistics gathered for a chosen grid 28 | * 29 | * @param grid 30 | * grid 31 | * @return Optional - avoid NPE if no statistics present 32 | */ 33 | public Optional getStatisticsByGrid(final int grid) { 34 | return getStats() 35 | .stream() 36 | .filter(stats -> stats.getGrid() == grid) 37 | .findAny(); 38 | } 39 | 40 | /** 41 | * All classes implementing this abstract class need to provide a "getStats"-method 42 | * 43 | * @return List 44 | */ 45 | public abstract List getStats(); 46 | 47 | /** 48 | * AVM gathers just integer numbers. We know the precision only from documentation, it is never provided by returned 49 | * responses from Fritz!Box. So we add this information here to the statistics. 50 | * 51 | * @param stats statistics to which to add the measurment unit 52 | * @param measurementUnit the unit to add to the statistics 53 | * @return statistics with measurement units 54 | */ 55 | protected List getStats(final List stats, final MeasurementUnit measurementUnit) { 56 | return stats 57 | .stream() 58 | .map(stat -> { 59 | stat.setMeasurementUnit(measurementUnit); 60 | return stat; 61 | }) 62 | .collect(Collectors.toList()); 63 | } 64 | 65 | /** 66 | * All classes implementing this abstract class need to provide a "statisticsToString"-method 67 | * 68 | * @return List 69 | */ 70 | protected abstract List statisticsToString(); 71 | 72 | /** 73 | * @param type statistics type 74 | * @return statistics as one line per grid 75 | */ 76 | protected List statisticsToString(final String type) { 77 | return getStats() 78 | .stream() 79 | .map(stats -> statisticsToString(type, stats)) 80 | .collect(Collectors.toList()); 81 | } 82 | 83 | /** 84 | * Form a line from a single statistic. 85 | * 86 | * @param type statistics type 87 | * @param statistics statistic to convert to {@link String} 88 | * @return statistic as a line 89 | */ 90 | private String statisticsToString(final String type, final Statistics statistics) { 91 | return String.format("[%s] count=%s,grid=%s values=[%s]", type, statistics.getCount(), statistics.getGrid(), 92 | statistics.getCsvValues()); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/model/homeautomation/Alert.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.model.homeautomation; 19 | 20 | import org.simpleframework.xml.Element; 21 | import org.simpleframework.xml.Root; 22 | 23 | @Root(name = "alert") 24 | public class Alert { 25 | 26 | @Element(name = "state", required = false) 27 | private int state; 28 | 29 | @Element(name = "lastalertchgtimestamp", required = false) 30 | private long lastAlertChgTimestamp; 31 | 32 | public int getState() { 33 | return state; 34 | } 35 | 36 | public void setState(int state) { 37 | this.state = state; 38 | } 39 | 40 | public long getLastAlertChgTimestamp() { 41 | return lastAlertChgTimestamp; 42 | } 43 | 44 | public void setLastAlertChgTimestamp(long lastAlertChgTimestamp) { 45 | this.lastAlertChgTimestamp = lastAlertChgTimestamp; 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/model/homeautomation/Blind.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.model.homeautomation; 19 | 20 | import org.simpleframework.xml.Element; 21 | import org.simpleframework.xml.Root; 22 | 23 | @Root(name = "blind") 24 | public class Blind { 25 | 26 | @Element(name = "endpositionsset", required = false) 27 | private int endPositionsSet; 28 | 29 | @Element(name = "mode", required = false) 30 | private String mode; 31 | 32 | public int getEndPositionsSet() { 33 | return endPositionsSet; 34 | } 35 | 36 | public void setEndPositionsSet(int endPositionsSet) { 37 | this.endPositionsSet = endPositionsSet; 38 | } 39 | 40 | public String getMode() { 41 | return mode; 42 | } 43 | 44 | public void setMode(String mode) { 45 | this.mode = mode; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/model/homeautomation/Button.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.model.homeautomation; 19 | 20 | import org.simpleframework.xml.Attribute; 21 | import org.simpleframework.xml.Element; 22 | import org.simpleframework.xml.Root; 23 | 24 | @Root(name = "button") 25 | public class Button { 26 | 27 | @Attribute(name = "identifier") 28 | private String identifier; 29 | @Attribute(name = "id") 30 | private String id; 31 | @Element(name = "name", required = false) 32 | private String name; 33 | @Element(name = "lastpressedtimestamp", required = false) 34 | private String lastpressedtimestamp; 35 | 36 | public String getIdentifier() { 37 | return identifier; 38 | } 39 | 40 | public String getId() { 41 | return id; 42 | } 43 | 44 | public String getName() { 45 | return name; 46 | } 47 | 48 | public String getLastpressedtimestamp() { 49 | return lastpressedtimestamp; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/model/homeautomation/ColorControl.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.model.homeautomation; 19 | 20 | import org.simpleframework.xml.Attribute; 21 | import org.simpleframework.xml.Element; 22 | import org.simpleframework.xml.Root; 23 | 24 | @Root(name = "colorcontrol") 25 | public class ColorControl { 26 | @Element(name = "hue", required = false) 27 | private String hue; 28 | 29 | @Element(name = "saturation", required = false) 30 | private String saturation; 31 | 32 | @Element(name = "temperature", required = false) 33 | private String temperature; 34 | 35 | @Attribute(name = "supported_modes") 36 | private String supportedModes; 37 | 38 | @Attribute(name = "current_mode", required = false) 39 | private String currentMode; 40 | 41 | @Attribute(name = "fullcolorsupport", required = false) 42 | private String fullColorSupport; 43 | 44 | @Attribute(name = "mapped", required = false) 45 | private String mapped; 46 | 47 | @Element(name = "unmapped_hue", required = false) 48 | private String unmappedHue; 49 | 50 | @Element(name = "unmapped_saturation", required = false) 51 | private String unmappedSaturation; 52 | 53 | public String getHue() { 54 | return hue; 55 | } 56 | 57 | public String getSaturation() { 58 | return saturation; 59 | } 60 | 61 | public String getTemperature() { 62 | return temperature; 63 | } 64 | 65 | public String getSupportedModes() { 66 | return supportedModes; 67 | } 68 | 69 | public String getCurrentMode() { 70 | return currentMode; 71 | } 72 | 73 | public String getFullColorSupport() { 74 | return fullColorSupport; 75 | } 76 | 77 | public String getMapped() { 78 | return mapped; 79 | } 80 | 81 | public String getUnmappedHue() { 82 | return unmappedHue; 83 | } 84 | 85 | public String getUnmappedSaturation() { 86 | return unmappedSaturation; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/github/kaklakariada/fritzbox/model/homeautomation/Device.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A Java API for managing FritzBox HomeAutomation 3 | * Copyright (C) 2017 Christoph Pirkl 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | package com.github.kaklakariada.fritzbox.model.homeautomation; 19 | 20 | import java.util.List; 21 | import java.util.Optional; 22 | 23 | import org.simpleframework.xml.Attribute; 24 | import org.simpleframework.xml.Element; 25 | import org.simpleframework.xml.ElementList; 26 | import org.simpleframework.xml.Root; 27 | 28 | @Root(name = "device") 29 | public class Device { 30 | 31 | @Attribute(name = "identifier", required = true) 32 | private String identifier; 33 | @Attribute(name = "id") 34 | private String id; 35 | @Attribute(name = "functionbitmask") 36 | private int functionBitmask; 37 | @Attribute(name = "fwversion") 38 | private String firmwareVersion; 39 | @Attribute(name = "manufacturer") 40 | private String manufacturer; 41 | 42 | @Attribute(name = "productname") 43 | private String productName; 44 | 45 | @Element(name = "present") 46 | private String present; 47 | @Element(name = "txbusy", required = false) 48 | private String txbusy; 49 | @Element(name = "name") 50 | private String name; 51 | 52 | @Element(name = "batterylow", required = false) 53 | private Integer batterylow; 54 | @Element(name = "battery", required = false) 55 | private Integer battery; 56 | @Element(name = "switch", required = false) 57 | private SwitchState switchState; 58 | @Element(name = "simpleonoff", required = false) 59 | private SimpleOnOffState simpleOnOff; 60 | @Element(name = "powermeter", required = false) 61 | private PowerMeter powerMeter; 62 | @Element(name = "temperature", required = false) 63 | private Temperature temperature; 64 | @Element(name = "hkr", required = false) 65 | private Hkr hkr; 66 | @Element(name = "levelcontrol", required = false) 67 | private LevelControl levelControl; 68 | @Element(name = "colorcontrol", required = false) 69 | private ColorControl colorControl; 70 | @Element(name = "etsiunitinfo", required = false) 71 | private EtsiUnitInfo etsiUnitInfo; 72 | @ElementList(name = "buttons", required = false, inline = true) 73 | private List 73 | 77 | 81 | 85 |
86 | 87 | 0 88 | 0 89 | FRITZ!DECT 500 power 90 | 91 | 1 92 | 93 | 94 | 153 95 | 60 96 | 97 | 98 | 225 99 | 204 100 | 6500 101 | 102 | 103 | 406 104 | 278 105 | 512,514,513 106 | 107 | 108 | 109 | 0 110 | 0 111 | FRITZ!DECT 500 without power 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 406 126 | 278 127 | 512,514,513 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /src/test/resources/deviceListConnectedFritzDect200Payload.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1 4 | 0 5 | FRITZ!DECT 200 #1 6 | 7 | 0 8 | manuell 9 | 0 10 | 0 11 | 12 | 13 | 0 14 | 15 | 16 | 233810 17 | 0 18 | 14535 19 | 20 | 21 | 270 22 | 0 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/test/resources/deviceListConnectedFritzDect301Payload.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 1 5 | 0 6 | FRITZ!DECT 301 7 | 100 8 | 0 9 | 10 | 235 11 | 0 12 | 13 | 14 | 47 15 | 46 16 | 28 17 | 46 18 | 0 19 | 0 20 | 0 21 | 0 22 | 0 23 | 0 24 | 0 25 | 0 26 | 100 27 | 28 | 1605643200 29 | 28 30 | 31 | 0 32 | 0 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/test/resources/deviceListConnectedFritzDect440Payload.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 1 5 | 0 6 | FRITZ!DECT 440 7 | 100 8 | 0 9 | 10 | 200 11 | 0 12 | 13 | 17 | 21 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /src/test/resources/deviceListConnectedFritzDect440x2Payload.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 1 5 | 0 6 | FRITZ!DECT 440 7 | 100 8 | 0 9 | 10 | 200 11 | 0 12 | 13 | 17 | 21 | 25 | 29 | 30 | 32 | 1 33 | 0 34 | FRITZ!DECT 440 35 | 100 36 | 0 37 | 38 | 200 39 | 0 40 | 41 | 45 | 49 | 53 | 57 | 58 | -------------------------------------------------------------------------------- /src/test/resources/deviceListConnectedFritzDect500Payload.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 0 4 | 0 5 | FRITZ!DECT 500 power 6 | 7 | 1 8 | 9 | 10 | 153 11 | 60 12 | 13 | 14 | 225 15 | 204 16 | 6500 17 | 18 | 19 | 406 20 | 278 21 | 512,514,513 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/test/resources/deviceListNotConnectedFritzDect500Payload.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 0 4 | 0 5 | FRITZ!DECT 500 without power 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 406 20 | 278 21 | 512,514,513 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/test/resources/devicelist6840.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 1 5 | Photovoltaik 6 | 7 | 1 8 | manuell 9 | 0 10 | 0 11 | 12 | 13 | 38480 14 | 875112 15 | 16 | 17 | 200 18 | 0 19 | 20 | 21 | 22 | 1 23 | PV 24 | 25 | 1 26 | manuell 27 | 0 28 | 0 29 | 30 | 31 | 38480 32 | 875112 33 | 34 | 35 | 0 36 | 16 37 | 38 | 39 | 41 | 1 42 | Schreibtischleuchte 43 | 44 | 1 45 | manuell 46 | 0 47 | 0 48 | 49 | 50 | 5720 51 | 936161 52 | 53 | 54 | 240 55 | 0 56 | 57 | 58 | 59 | 1 60 | Yppsenwautschi 61 | 62 | 1 63 | manuell 64 | 0 65 | 0 66 | 67 | 68 | 5720 69 | 936161 70 | 71 | 72 | 0 73 | 17,18 74 | 75 | 76 | 78 | 1 79 | Schalter 80 | 81 | 0 82 | manuell 83 | 0 84 | 0 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 20 | 21 | 22 | 23 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/test/resources/sessionInfo.xml: -------------------------------------------------------------------------------- 1 | 2 | 21 | 22 | 0000000000000000 23 | 00000000 24 | 0 25 | 26 | 27 | UserA 28 | UserB 29 | UserC 30 | 31 | 32 | --------------------------------------------------------------------------------