3.1.0 (2026-08-14)
Failures that used to crash now come back as errors: commands return an error
tuple for a response they do not recognize, FritzApi.Client.login/3 refuses
the unauthenticated session ID instead of handing back an unusable client, and
FritzApi.Error messages render for every reason rather than only for atoms.
Check your device function names
Two of the device function classes reported in FritzApi.Actor were
misspelled and are now correct. If you match on either string, update it:
"0AVM DECT Repeater"is now"AVM DECT Repeater""AVM- Button"is now"AVM-Button"
The function list is also ordered by bit position now, which moves
"AVM DECT Repeater" earlier for devices that report it.
Fixed
- Commands no longer raise when the FritzBox returns a response they do not
recognize. They return
{:error, %FritzApi.Error{reason: {:unexpected_response, body}}}, which is what their specs always promised. Previously these escaped asFunctionClauseError,MatchErrororCaseClauseError. A FritzBox with no paired devices was enough to trigger it. FritzApi.Client.login/3fails with{:error, %FritzApi.Error{reason: :login_failed}}instead of returning a client holding the unauthenticated session ID"0000000000000000". Such a client failed every later call with a misleading:user_not_authorized.FritzApi.Client.login/3reports a login response it does not recognize as{:error, %FritzApi.Error{reason: {:unexpected_response, body}}}. A response without aSessionInfoelement used to escape as aCaseClauseError.Exception.message/1on aFritzApi.Errorno longer raises for the most common reasons. A transport error struct returned by the HTTP client or a{:login_failed, block_time: 60}tuple used to produce aProtocol.UndefinedErrorblob in logs and stack traces.FritzApi.get_switch_list/1returns{:ok, []}for a FritzBox without switches, instead of{:ok, [""]}.FritzApi.get_device_list_infos/1likewise returns{:ok, []}for an empty device list.- A malformed XML response is reported as
{:error, %FritzApi.Error{reason: {:unexpected_response, body}}}. The XML parser throws, so a truncated response or a captive portal page served astext/xmlused to escape as an uncaught{:error, ~c"Malformed: ..."}throw that nocaseorwithat the call site could catch. FritzApi.get_device_list_infos/1decodes values it does not recognize asnilinstead of raising. A single unexpected switch state or a non-numericfunctionbitmaskused to fail the whole device list.- Two device function classes were misspelled; see the note above.
FritzApi.ActortypedidasString.t()while it is parsed into an integer, and fields that decode tonilfor an empty XML element were typed as non-nil.FritzApi.set_hkr_target_temperature/3was specced as8..28, an integer range that excludes the21.5in its own example, and its documentation claimed it returns{:ok, temperature}rather than:ok. The radiator controller getters listed:unknownamong their return values, which they never returned.- The
FritzApi.HTTPClientdocumentation showed an example implementing a different library's behaviour and arequest/5callback that does not exist. - The README and
FritzApiexamples showed a device list the library cannot produce: a stringid, and plain maps whereFritzApi.Powermeter,FritzApi.SwitchandFritzApi.Temperaturestructs are returned.
Added
FritzApi.Client.execute_command/3is now public, for the AHA commands thatFritzApidoes not wrap.FritzApi.Client.session_id/1to read the session ID of a logged in client.FritzApi.Errordefines aFritzApi.Error.reason/0type listing the possible failure reasons.FritzApi.result/0,FritzApi.result/1andFritzApi.hkr_temperature/0name the shapes every command returns.FritzApi.Client.new/1documents its:http_client,:request_optsand:session_idoptions. All three were already accepted;:session_idtakes an existing session ID, which is how a session survives a restart.
Changed
- The minimum Elixir version is now 1.15, up from 1.11. Finch and its dependencies require it, so the built-in HTTP client could not run on an older Elixir anyway.
FritzApi.Client.t/0is no longer@opaque. It never was in practice, and Dialyzer reported an opacity violation for everyFritzApicommand. The struct fields remain private; useFritzApi.Client.session_id/1to read the session ID.FritzApi.HTTPClient.method/0andFritzApi.HTTPClient.params/0were removed. Neither was used by a callback.FritzApi.HTTPClient.child_spec/1may return a list of child specifications in addition to a single one ornil, for clients that need more than one supervised process.- The check for a missing
:finchdependency moved from the application callback intoFritzApi.HTTPClient.Finch, so the error now names the module that needs it.
3.0.0 (2023-08-12)
Breaking Changes
- Migrate built-in HTTP from
hackneytoFinch - Replace the
:adapterwith the:clientoption
Upgrade instructions
Dependencies
FritzApi now ships with an HTTP client based on :finch instead of :hackney.
Add :finch to your list of dependencies in mix.exs:
def deps do
[
{:fritz_api, "~> 3.0"},
{:finch, "~> 0.16"},
]
endHTTP client (optional)
Remove the
:adapterconfiguration fromFritzApi.Client.new/1:{:ok, client} = FritzApi.Client.new( - adapter: {Tesla.Adapter.Gun, []} )In
config/runtime.exsset the:fritz_api, :clientoption and to your own module that implements theFritzApi.HTTPClientbehaviour:+ config :fritz_api, + client: MyGunAdapter
See the documentation for FritzApi.HTTPClient for more information.
2.2.0 (2022-12-29)
- Fix deprecation warning
- Update dependencies
2.1.0 (2022-02-22)
- Bump elixir_xml_to_map to 3.0
2.0.0 (2020-11-27)
FritzApi 2.0 is a major release containing significant changes, particularly around the FritzApi.Client.
Enhancements
- Use tesla to make the underlying HTTP client configurable
Breaking Changes
- Replace the stateful
FritzApi.Clientthat would need to be started as part of a supervision tree with a simpler struct based approach:- Call
FritzApi.Client.new()to create a new client andFritzApi.Client.login(client, "user", "password")to authenticate with the Fritz API - See README for an example
- Call
- Switch functions like
FritzApi.get_switch_state/2return:on,:offatoms instead of a boolean, and:unknownif the actor is unavailable - Introduce a custom error struct (
FritzApi.Error) and use custom structs for actors (e.g.FritzApi.Switch) - Make hackney an optional dependency. To use the default
hackneybased adapter, add{:hackney, "~> 1.16"}to the list of dependencies.