Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,52 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.0] - Unreleased

See [MIGRATION.md](MIGRATION.md) for a step-by-step upgrade guide.

### Added

- `Homex.Adapter` behaviour — transports are now pluggable. The MQTT logic
(emqtt lifecycle, discovery publishing, wire serialization) moved from
`Homex.Manager` into `Homex.Adapter.MQTT`
- `Homex.Descriptor` — a transport-neutral description of every entity;
adapters render it into their wire format
- Runtime entity construction: entities no longer require a dedicated module.
Specs can be `MySwitch`, `{MySwitch, name: "other"}` (several instances of
one module) or `{Homex.Entity.Switch, name: "relay", handler: MyHandlers}`
- `handler:` option on every platform — a plain module implementing the
optional callbacks (`handle_on/1`, `handle_press/1`, ...)
- `Homex.Entity.Handler` behaviour — the platform-independent callbacks
(`handle_init/1` and the OTP passthroughs `handle_info/2`, `handle_call/2`,
`handle_cast/2`), so handler implementations can be checked with `@impl`
- `Homex.Entity.snapshot/1` returns the current values of an entity;
`Homex.descriptors/0` lists all running entities
- Entity fields are typed `state` or `event`: event fields (button press,
device trigger) publish on every fire instead of being deduplicated, so
repeated triggers are no longer swallowed
- `Homex.Entity.Platform.using_helper/3` — third-party platforms get the
`use` sugar in one line

### Changed (breaking)

- Configuration moved from the application environment to supervision-tree
options: `{Homex, broker: [...], entities: [...]}`. `config :homex, ...` is
no longer read
- Entity values are typed (`true`/`false`, numbers) instead of wire strings
(`"ON"`/`"OFF"`); adapters serialize at the edge
- `update_interval` option and `handle_timer/1` callback removed — start a
timer in `handle_init/1` and react in `handle_info/2`
- `unique_id` now derives from the device identity plus entity kind and name,
so entities from two machines no longer collide on one broker (#34). Home
Assistant will treat existing entities as new ones after the upgrade — and
since the default device identity derives from the hostname, a hostname
change re-identifies them too; set `device: [identifiers: [...]]` to pin it
- Entity modules are plain handler modules; the GenServer lives in the core
- `MyTrigger.trigger()` is now `Homex.Entity.DeviceTrigger.trigger("my-name")`
- Entities are addressed by their `name` string (e.g. in `Homex.notify/2`),
not by module

## [0.1.2] - 2026-07-01

### Fixed
Expand Down
113 changes: 113 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Migrating from 0.1 to 0.2

0.2 enables a few missing features which couldn't be done with 0.1. Most of you entity code survives unchanged - what changes is how homex is configured, how entities are constructed, and how timers work.

## 1. Move configuration into your supervision tree

The application environment is no longer read. Pass everything as start
options instead:

```elixir
# before — config/config.exs
config :homex,
broker: [host: "localhost", port: 1883],
entities: [MySwitch]

# and in your application
children = [Homex]
```

```elixir
# after — everything at the start site, no config files
children = [
{Homex, broker: [host: "localhost", port: 1883], entities: [MySwitch]}
]
```

The options themselves (`broker`, `device`, `origin`, `discovery_prefix`,
`entities`) are unchanged — see `Homex.Config`. This enables you to start and configure Homex when and however you like it.

## 2. Replace `update_interval` / `handle_timer`

The built-in timer is gone. Start your own in `handle_init/1` and react in
`handle_info/2` — the message arrives in the entity process and changes are
published automatically:

```elixir
# before
use Homex.Entity.Sensor, name: "my-temperature", update_interval: 10_000

def handle_timer(entity) do
set_value(entity, Sensor.read())
end
```

```elixir
# after
use Homex.Entity.Sensor, name: "my-temperature"

def handle_init(entity) do
:timer.send_interval(10_000, :measure)
entity
end

def handle_info(:measure, entity) do
set_value(entity, Sensor.read())
end
```

## 3. Fire device triggers by entity name

```elixir
# before
MyTrigger.trigger()

# after
alias Homex.Entity.DeviceTrigger
DeviceTrigger.trigger("my-device-trigger")
```

Entities are addressed by their `name` string everywhere (`Homex.notify/2`,
`Homex.Entity.snapshot/1`, ...), not by module.

## 4. Expect re-created entities in Home Assistant

`unique_id` now includes the device identity (its `identifiers` and `name`),
fixing collisions when two machines expose same-named entities to one broker.
After upgrading, Home Assistant sees your entities as new: history detaches
and per-entity customizations (area, icon, entity id overrides) need to be
reapplied once.

This also means the device identity is now part of every entity's identity.
By default both `identifiers` and `name` derive from the hostname — so a
hostname change re-creates all entities in Home Assistant. If your hostname
isn't stable, pin the identity explicitly:

```elixir
{Homex, device: [identifiers: ["my-device-id"], name: "My Device"], entities: [...]}
```

## 5. Entity values are typed

State is stored as `true`/`false` and numbers instead of wire strings like
`"ON"`. If you inspected `entity.values` or `entity.changes` in callbacks,
match on the typed values. The MQTT wire format is unchanged — adapters
serialize at the edge.

## New in 0.2 (nothing to migrate, worth knowing)

Entities no longer need a dedicated module. All of these are valid entries in
`entities:` (and arguments to `Homex.add_entity/1`):

```elixir
entities: [
MySwitch, # as before
{MySwitch, name: "second-switch"}, # same module, second instance
{Homex.Entity.Switch, name: "relay", handler: MyHandlers}, # callbacks in a plain module
{Homex.Entity.DeviceTrigger, name: "doorbell"} # no module needed at all
]
```

The `handler` module implements any subset of the platform's optional
callbacks (`handle_init/1`, `handle_on/1`, ...) plus the OTP passthroughs
(`handle_info/2`, `handle_call/2`, `handle_cast/2`).
Loading