Skip to content

Commit

Permalink
Config flow (#27)
Browse files Browse the repository at this point in the history
* initial config-flow checkin

* initial config-flow checkin

bump revision

* import mostly working

* import mostly working

config flow somewhat working

* Tried to work on README.

Added an icon.

* Got reload working.

Devices can now be added and removed on the fly.

* Got reload working.

Devices can now be added and removed on the fly.

* Update README.md

* Fix naming, we now add a + to signify we need to add momentary to the switch name.

* Fix naming, we now add a + to signify we need to add momentary to the switch name.

* Fix naming, we now add a + to signify we need to add momentary to the switch name.

* Fix naming, we now add a + to signify we need to add momentary to the switch name.

* Added group support.

Added config flow validation and error messages.

Fix deletion.

Tidied code.

Remove the swearing.

* Added group support.

Added config flow validation and error messages.

Fix deletion.

Tidied code.

Remove the swearing.

* Added group support.

Added config flow validation and error messages.

Fix deletion.

Tidied code.

Remove the swearing.
  • Loading branch information
twrecked authored Oct 16, 2023
1 parent 0dfb2a3 commit d240b97
Show file tree
Hide file tree
Showing 11 changed files with 806 additions and 83 deletions.
161 changes: 126 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,53 @@
# hass-momentary
![icon](images/momentary-icon.png)

### Momentary Switch Component for Home Assistant
A simple switch component that once turned on will turn itself off.

### NOTES!
This release includes a `mode` configuration allowing the switch to be
momentarily on or off. Existing configurations will work with the new code but
use the new configuration going forward.
**This documentation is for the 0.7x version, you can find the
0.6.x version** [here](https://github.com/twrecked/hass-momentary/blob/version-0.6.x/README.md).

This is a big update that moves the component over to the `config_flow`
system. The update should be seamless but if you run into any problems:

- You can revert back to the previous version (0.6) and it will still work.
- But if you can re-run the upgrade operation with debug enabled and create a
bug report I would greatly appreciate it.


## Table Of Contents

1. [Notes](#Notes)
1. [Thanks](#Thanks)
1. [Installation](#Installation)
1. [Migrating from Old Layout](#Migrating-from-Old-Layout)
1. [HACS](#HACS)
1. [Manually](#Manually)
1. [From Script](#From-Script)
1. [Component Configuration](#Component-Configuration)
2. [Upgrade Notes](#Upgrade-notes)


## Notes

Wherever you see `/config` in this README it refers to your home-assistant
configuration directory. For me, for example, it's `/home/steve/ha` that is
mapped to `/config` inside my docker container.


## Thanks

Many thanks to:
* [JetBrains](https://www.jetbrains.com/?from=hass-aarlo) for the excellent
**PyCharm IDE** and providing me with an open source license to speed up the
project development.

[![JetBrains](/images/jetbrains.svg)](https://www.jetbrains.com/?from=hass-aarlo)

* Icon adapted from [svg icons](https://www.onlinewebfonts.com/icon)
and is licensed by CC BY 4.0


## Installation

### HACS
Expand All @@ -57,23 +72,67 @@ install go /config


## Component Configuration
Add the following to your `configuration.yaml` to enable the component:
Add the component using the standard _Home Assistant_ _Integration_ method.

Because this component creates fake entries and because I'm still figuring out
the _Config Flow_ interface you still have to configure it by file.

The default file is named `/config/momentary.yaml` and is a similar format to
the standard _Home Assistant_ configuration files. This file will be created
during an upgrade or when you add in a _Momentary_ integration. An empty
file looks like this:


```yaml
momentary:
version: 1
switches:
```
To create a momentary on switch use the following:
To create a single momentary device add the following:
```yaml
switch:
- platform: momentary
name: Empty House Trigger
mode: "on"
toggle_for: 5
cancellable: True
version: 1
switches:
- name: Empty House Trigger
```
To create a single momentary device with custom options use:
```yaml
version: 1
switches:
- name: Empty House Trigger
mode: "on"
toggle_for: 5
cancellable: True
```
To create multiple momentary device add more devices at the bottom:
```yaml
version: 1
switches:
- name: Empty House Trigger
mode: "on"
toggle_for: 5
cancellable: True
- name: Full House Trigger
- name: Overflowing House Trigger
toggle_for: 2
```
Once you've updated the file you will need to reload the component from its
integration setting.
The integration uses the name to distinguish different switches. If you can
the name of a switch the old name will be deleted and the new name created
on reload.
The integration supports mutliple entries, you can use this group similar
momentary devices together.
### Options
The following additional parameters can be specified against the switches:
| Field | Type | Default | Description |
Expand All @@ -83,34 +142,66 @@ The following additional parameters can be specified against the switches:
| toggle_for | seconds | 1 | Amount of time to turn toggle switch for. |
| cancellable | Boolean | False | Allow switched to be untoggled manually. |
To add multiple switches repeat the whole component configuration:
## Upgrade Notes
```yaml
switch:
- platform: momentary
name: Empty House Trigger
mode: "on"
toggle_for: 5
- platform: momentary
name: Bad Weather Trigger
mode: "on"
toggle_for:
milliseconds: 500
```
### After the Upgrade
Check the devices and entities on the _Integrations_ page, if everything looks
good then you can comment out the old configuration. _Don't delete it yet, this
is alpha and I might change some settings..._
## Naming
### Names
By default, the code creates entities with `momentary` as part of their name.
`Switch 1` in the previous example will give an entity of
`switch.momentary_switch_1`. If you don't want the `momentary_` prefix add a `!`
to the device name. For example:
The qualifier applied to the name has changed. The old qualify of `!` has been
removed, not adding _momentary_ to the device/entity name is the default. A
new qualify of `+` has been created, adding this tells the integration to add
_momentary_ to the device/entity name.

The upgrade code will change this for you.

| Old Name | New Name | Entity Name |
|----------------------| ----------------------------- | ------------------------------------ |
| Empty House Trigger | +Empty House Trigger | switch.momentary_empty_house_trigger |
| !Empty House Trigger | Empty House Trigger | switch.empty_house_trigger |

### `unique_id`

During an upgrade the original name based unique id will be created. For a new
install a _UUID_ based unique id will be created.

If you want to move to the new _UUID_ based unique IDs you can manage this
with the following:

- Delete the imported _Momentary_ integration.
- Re-add the imported _Momentary_ integration. You can use the default values
for group and file name.

The system will re-create your devices with the correct entity ids but with
new _UUID_ based unique ids.

_I will look at how this can be made cleaner... maybe a config flow button
or as part of the upgrade._

### Known Issues

You will see errors like this on upgrade. They don't seem to cause an issue
and they disappear once you remove the legacy configuration.

```yaml
switch:
- platform: momentary
name: !Switch 1
```
2023-10-13 17:28:09.154 ERROR (MainThread) [homeassistant.components.switch] The momentary platform for the switch integration does not support platform setup. Please remove it from your config.
```
### Turning on Debug
If you do run into issues you can enable debug with the following configration.
```yaml
logger:
default: info
logs:
custom_components.momentary: debug
```

If you can create a bug report with this debug output it would help me track
down the problem.
2 changes: 2 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
0.7.0a1: support device/entity and standard integrations

0.6.3: restore switch state
0.6.2: restore wrongly removed options
0.6.1: code tidy
Expand Down
129 changes: 124 additions & 5 deletions custom_components/momentary/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,135 @@
"""

from __future__ import annotations

import logging

__version__ = '0.6.3'
from homeassistant.config_entries import ConfigEntry, SOURCE_IMPORT
from homeassistant.const import CONF_SOURCE, Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.typing import ConfigType
import homeassistant.helpers.device_registry as dr

from .const import (
ATTR_FILE_NAME,
ATTR_GROUP_NAME,
ATTR_SWITCHES,
ATTR_UNIQUE_ID,
CONF_NAME,
DOMAIN,
MANUFACTURER,
MODEL
)
from .db import Db


__version__ = '0.7.0a1'

_LOGGER = logging.getLogger(__name__)

COMPONENT_DOMAIN = 'momentary'

def setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up an momentary component.
"""

hass.data.setdefault(DOMAIN, {})

# See if we have already imported the data. If we haven't then do it now.
config_entry = _async_find_matching_config_entry(hass)
if not config_entry:
_LOGGER.debug('importing a YAML setup')
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={CONF_SOURCE: SOURCE_IMPORT},
data=config[Platform.SWITCH]
)
)
return True

def setup(_hass, _config):
"""Set up an momentary component."""
_LOGGER.debug('setup')
_LOGGER.debug('ignoring a YAML setup')
return True


@callback
def _async_find_matching_config_entry(hass):
""" If we have anything in config_entries for momentary we consider it
configured and will ignore the YAML.
"""
for entry in hass.config_entries.async_entries(DOMAIN):
# if entry.source == SOURCE_IMPORT:
return entry


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
_LOGGER.debug(f'async setup {entry.data}')

# Database of devices
group_name = entry.data[ATTR_GROUP_NAME]
file_name = entry.data[ATTR_FILE_NAME]
db = Db(group_name, file_name)
db.load()

# Load and create devices.
for switch, values in db.switches.items():
_LOGGER.debug(f"would try to add {switch}")
# _LOGGER.debug(f"would try to add {values}")
await _async_get_or_create_momentary_device_in_registry(hass, entry, switch, values)

# Delete orphaned entries.
for switch, values in db.orphaned_switches.items():
_LOGGER.debug(f"would try to delete {switch}")
await _async_delete_momentary_device_from_registry(hass, entry, switch, values)

# Update hass data and queue entry creation.
hass.data[DOMAIN].update({
group_name: {
ATTR_SWITCHES: db.switches,
ATTR_FILE_NAME: file_name
}
})
_LOGGER.debug(f"update hass data {hass.data[DOMAIN]}")
await hass.config_entries.async_forward_entry_setup(entry, Platform.SWITCH)

return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
_LOGGER.debug(f"unloading it {entry.data[ATTR_GROUP_NAME]}")
unload_ok = await hass.config_entries.async_unload_platforms(entry, [Platform.SWITCH])
if unload_ok:
hass.data[DOMAIN].pop(entry.data[ATTR_GROUP_NAME])
Db.delete_group(entry.data[ATTR_GROUP_NAME])
_LOGGER.debug(f"after hass={hass.data[DOMAIN]}")

return unload_ok


async def _async_get_or_create_momentary_device_in_registry(
hass: HomeAssistant, entry: ConfigEntry, unique_id, switch
) -> None:
device_registry = dr.async_get(hass)
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={(DOMAIN, unique_id)},
manufacturer=MANUFACTURER,
name=switch[CONF_NAME],
model=MODEL,
sw_version=__version__
)


async def _async_delete_momentary_device_from_registry(
hass: HomeAssistant, entry: ConfigEntry, unique_id, switch
) -> None:
device_registry = dr.async_get(hass)
device = device_registry.async_get_device(
identifiers={(DOMAIN, unique_id)},
)
if device:
_LOGGER.debug(f"found something to delete! {device.id}")
device_registry.async_remove_device(device.id)
else:
_LOGGER.info(f"have orphaned device in meta {unique_id}")
Loading

0 comments on commit d240b97

Please sign in to comment.