Skip to content

Commit

Permalink
Added set-event-listener-address step to options-flow (cf. #1)
Browse files Browse the repository at this point in the history
  • Loading branch information
twystd committed Mar 12, 2024
1 parent e3d633b commit 12db094
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 29 deletions.
7 changes: 5 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
- https://github.com/home-assistant/brands

- [ ] Set controller event listener
- https://stackoverflow.com/questions/5281409/get-destination-address-of-a-received-udp-packet
- https://stackoverflow.com/questions/39059418/python-sockets-how-can-i-get-the-ip-address-of-a-socket-after-i-bind-it-to-an
- [x] config-flow
- [x] options-flow
- [ ] events-coordinator
- [ ] controller-info

- [ ] DataCoordinator
- [ ] communalize data
Expand All @@ -26,6 +28,7 @@

- [ ] _config-flow_
- [ ] Use _self.options_ struct and _self.configured_ list
- [ ] Use options for defaults (Ref. https://developers.home-assistant.io/docs/data_entry_flow_index/#defaults--suggestions)
- [ ] (somehow) commonalise config-flow and options-flow
- (?) Single instance only (https://developers.home-assistant.io/blog/2024/02/26/single-instance-only-manifest-option)

Expand Down
10 changes: 10 additions & 0 deletions custom_components/uhppoted/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import socket
import uuid
import urllib
import netifaces

from typing import Any

Expand Down Expand Up @@ -146,6 +147,15 @@ def get_IPv4(defaults):
CONF_DEBUG: debug,
}

def get_IPv4_addresses():
addresses = []
for interface in netifaces.interfaces():
addrs = netifaces.ifaddresses(interface).get(netifaces.AF_INET)
if addrs:
for addr_info in addrs:
addresses.append(addr_info['addr'])

return addresses

def get_all_controllers(options):
controllers = set()
Expand Down
50 changes: 32 additions & 18 deletions custom_components/uhppoted/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from homeassistant.config_entries import ConfigFlow
from homeassistant.config_entries import OptionsFlow
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers import selector
from homeassistant.helpers.selector import SelectSelector
from homeassistant.helpers.selector import SelectSelectorConfig
from homeassistant.helpers.selector import SelectSelectorMode
Expand Down Expand Up @@ -52,6 +51,12 @@
from .const import CONF_CARD_ENDDATE
from .const import CONF_CARD_DOORS

from .const import DEFAULT_BIND_ADDRESS
from .const import DEFAULT_BROADCAST_ADDRESS
from .const import DEFAULT_LISTEN_ADDRESS
from .const import DEFAULT_EVENTS_DEST_ADDR
from .const import DEFAULT_DEBUG

from .const import DEFAULT_CONTROLLER_ID
from .const import DEFAULT_CONTROLLER_ADDR
from .const import DEFAULT_CONTROLLER_TIMEZONE
Expand All @@ -72,7 +77,9 @@
from .config import validate_door_duplicates
from .config import validate_card_id
from .config import validate_all_cards

from .config import get_IPv4
from .config import get_IPv4_addresses
from .config import get_all_controllers
from .config import get_all_cards
from .config import default_card_start_date
Expand All @@ -91,6 +98,11 @@ def async_get_options_flow(config_entry: ConfigEntry) -> UhppotedOptionsFlow:
async def async_step_user(self, user_input: Optional[Dict[str, Any]] = None):
defaults = self.hass.data[DOMAIN] if DOMAIN in self.hass.data else {}

self._bind = defaults.get(CONF_BIND_ADDR, DEFAULT_BIND_ADDRESS)
self._broadcast = defaults.get(CONF_BROADCAST_ADDR, DEFAULT_BROADCAST_ADDRESS)
self._listen = defaults.get(CONF_LISTEN_ADDR, DEFAULT_LISTEN_ADDRESS)
self._events_dest_addr = defaults.get(CONF_EVENTS_DEST_ADDR, DEFAULT_EVENTS_DEST_ADDR)
self._debug = defaults.get(CONF_DEBUG, DEFAULT_DEBUG)
self._timezone = defaults.get(CONF_TIMEZONE, DEFAULT_CONTROLLER_TIMEZONE)
self._max_cards = defaults.get(CONF_MAX_CARDS, DEFAULT_MAX_CARDS)
self._preferred_cards = defaults.get(CONF_PREFERRED_CARDS, DEFAULT_PREFERRED_CARDS)
Expand All @@ -117,10 +129,10 @@ async def async_step_IPv4(self, user_input: Optional[Dict[str, Any]] = None):
self.options.update(user_input)
return await self.async_step_events()

bind = self.options[CONF_BIND_ADDR]
broadcast = self.options[CONF_BROADCAST_ADDR]
listen = self.options[CONF_LISTEN_ADDR]
debug = self.options[CONF_DEBUG]
bind = self.options.get(CONF_BIND_ADDR,self._bind)
broadcast = self.options.get(CONF_BROADCAST_ADDR, self._broadcast)
listen = self.options.get(CONF_LISTEN_ADDR,self._listen)
debug = self.options.get(CONF_DEBUG, self._debug)

schema = vol.Schema({
vol.Optional(CONF_BIND_ADDR, default=bind): str,
Expand All @@ -135,26 +147,28 @@ async def async_step_events(self, user_input: Optional[Dict[str, Any]] = None):
errors: Dict[str, str] = {}

if user_input is not None:
address = user_input[CONF_EVENTS_DEST_ADDR]
address = user_input.get(CONF_EVENTS_DEST_ADDR,'')

try:
validate_events_addr(address)
except ValueError as err:
errors[CONF_EVENTS_DEST_ADDR] = f'{err}'

if not errors:
print('>>>>>>>>>>>>>>> awoooooogahhhhhh', address)
# if dest != '':
# self.options.update([[CONF_EVENTS_DEST_ADDR,dest]])
# else:
# self.options.pop(CONF_EVENTS_DEST_ADDR, None)
self.options.update([[CONF_EVENTS_DEST_ADDR,address]])

return await self.async_step_controllers()

addr = self.options.get(CONF_EVENTS_DEST_ADDR, '')
addr = self.options.get(CONF_EVENTS_DEST_ADDR, self._events_dest_addr)
addresses = [v for v in get_IPv4_addresses() if v != '127.0.0.1']

select = SelectSelectorConfig(options=[{ 'label': f'{v}:60001', 'value': f'{v}:60001' } for v in addresses],
multiple=False,
custom_value=True,
mode=SelectSelectorMode.LIST) # yapf: disable

schema = vol.Schema({
vol.Optional(CONF_EVENTS_DEST_ADDR, default=addr): str,
vol.Optional(CONF_EVENTS_DEST_ADDR, default=''): SelectSelector(select),
})

return self.async_show_form(step_id="events", data_schema=schema, errors=errors)
Expand Down Expand Up @@ -296,13 +310,13 @@ async def async_step_doors(self, user_input: Optional[Dict[str, Any]] = None):
if re.match('^[4].*', f"{controller['serial_no']}"):
doors.append(4)

select = selector.SelectSelectorConfig(options=[{ 'label': f'Door {v}', 'value': f'{v}' } for v in doors],
multiple=True,
custom_value=False,
mode=selector.SelectSelectorMode.LIST) # yapf: disable
select = SelectSelectorConfig(options=[{ 'label': f'Door {v}', 'value': f'{v}' } for v in doors],
multiple=True,
custom_value=False,
mode=SelectSelectorMode.LIST) # yapf: disable

schema = vol.Schema({
vol.Required('doors', default=[f'{v}' for v in doors]): selector.SelectSelector(select),
vol.Required('doors', default=[f'{v}' for v in doors]): SelectSelector(select),
})

placeholders = {
Expand Down
6 changes: 6 additions & 0 deletions custom_components/uhppoted/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@
ATTR_EVENTS = 'events'
ATTR_STATUS = 'status'

DEFAULT_BIND_ADDRESS = '0.0.0.0'
DEFAULT_BROADCAST_ADDRESS = '255.255.255.255:60000'
DEFAULT_LISTEN_ADDRESS = '0.0.0.0:60001'
DEFAULT_EVENTS_DEST_ADDR = ''
DEFAULT_DEBUG = False

DEFAULT_CONTROLLER_ID = 'Alpha'
DEFAULT_CONTROLLER_ADDR = '192.168.1.100'
DEFAULT_CONTROLLER_TIMEZONE = 'LOCAL'
Expand Down
61 changes: 56 additions & 5 deletions custom_components/uhppoted/options_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from .const import CONF_BIND_ADDR
from .const import CONF_BROADCAST_ADDR
from .const import CONF_LISTEN_ADDR
from .const import CONF_EVENTS_DEST_ADDR
from .const import CONF_DEBUG
from .const import CONF_TIMEZONE

Expand All @@ -45,6 +46,12 @@
from .const import CONF_CARD_ENDDATE
from .const import CONF_CARD_DOORS

from .const import DEFAULT_BIND_ADDRESS
from .const import DEFAULT_BROADCAST_ADDRESS
from .const import DEFAULT_LISTEN_ADDRESS
from .const import DEFAULT_EVENTS_DEST_ADDR
from .const import DEFAULT_DEBUG

from .const import DEFAULT_CONTROLLER_ID
from .const import DEFAULT_CONTROLLER_ADDR
from .const import DEFAULT_CONTROLLER_TIMEZONE
Expand All @@ -57,6 +64,7 @@
from .const import DEFAULT_MAX_CARDS
from .const import DEFAULT_PREFERRED_CARDS

from .config import validate_events_addr
from .config import validate_controller_id
from .config import validate_door_duplicates
from .config import validate_door_id
Expand All @@ -65,6 +73,7 @@
from .config import validate_all_doors
from .config import validate_all_cards

from .config import get_IPv4_addresses
from .config import get_all_controllers
from .config import get_all_doors
from .config import get_all_cards
Expand All @@ -76,6 +85,11 @@
class UhppotedOptionsFlow(OptionsFlow):

def __init__(self, entry: ConfigEntry) -> None:
self._bind = DEFAULT_BIND_ADDRESS
self._broadcast = DEFAULT_BROADCAST_ADDRESS
self._listen = DEFAULT_LISTEN_ADDRESS
self._events_dest_addr = DEFAULT_EVENTS_DEST_ADDR
self._debug = DEFAULT_DEBUG
self._timezone = DEFAULT_CONTROLLER_TIMEZONE
self._max_cards = DEFAULT_MAX_CARDS
self._preferred_cards = DEFAULT_PREFERRED_CARDS
Expand All @@ -90,6 +104,11 @@ def __init__(self, entry: ConfigEntry) -> None:
async def async_step_init(self, user_input: dict[str, Any] | None = None) -> FlowResult:
defaults = self.hass.data[DOMAIN] if DOMAIN in self.hass.data else {}

self._bind = defaults.get(CONF_BIND_ADDR, self._bind)
self._broadcast = defaults.get(CONF_BROADCAST_ADDR, self._broadcast)
self._listen = defaults.get(CONF_LISTEN_ADDR, self._listen)
self._events_dest_addr = defaults.get(CONF_EVENTS_DEST_ADDR, self._events_dest_addr)
self._debug = defaults.get(CONF_DEBUG, self._debug)
self._timezone = defaults.get(CONF_TIMEZONE, DEFAULT_CONTROLLER_TIMEZONE)
self._max_cards = defaults.get(CONF_MAX_CARDS, DEFAULT_MAX_CARDS)
self._preferred_cards = defaults.get(CONF_PREFERRED_CARDS, DEFAULT_PREFERRED_CARDS)
Expand All @@ -99,18 +118,19 @@ async def async_step_init(self, user_input: dict[str, Any] | None = None) -> Flo
description_placeholders={})
# return await self.async_step_IPv4()


async def async_step_IPv4(self, user_input: Optional[Dict[str, Any]] = None):
errors: Dict[str, str] = {}

if user_input is not None:
if not errors:
self.options.update(user_input)
return await self.async_step_controllers()
return await self.async_step_events()

bind = self.options[CONF_BIND_ADDR]
broadcast = self.options[CONF_BROADCAST_ADDR]
listen = self.options[CONF_LISTEN_ADDR]
debug = self.options[CONF_DEBUG]
bind = self.options.get(CONF_BIND_ADDR, self._bind)
broadcast = self.options.get(CONF_BROADCAST_ADDR, self._broadcast)
listen = self.options.get(CONF_LISTEN_ADDR, self._listen)
debug = self.options.get(CONF_DEBUG, self._debug)

schema = vol.Schema({
vol.Optional(CONF_BIND_ADDR, default=bind): str,
Expand All @@ -121,6 +141,37 @@ async def async_step_IPv4(self, user_input: Optional[Dict[str, Any]] = None):

return self.async_show_form(step_id="IPv4", data_schema=schema, errors=errors)


async def async_step_events(self, user_input: Optional[Dict[str, Any]] = None):
errors: Dict[str, str] = {}

if user_input is not None:
address = user_input.get(CONF_EVENTS_DEST_ADDR,'')

try:
validate_events_addr(address)
except ValueError as err:
errors[CONF_EVENTS_DEST_ADDR] = f'{err}'

if not errors:
self.options.update([[CONF_EVENTS_DEST_ADDR,address]])

return await self.async_step_controllers()

addr = self.options.get(CONF_EVENTS_DEST_ADDR, self._events_dest_addr)
addresses = [v for v in get_IPv4_addresses() if v != '127.0.0.1']

select = SelectSelectorConfig(options=[{ 'label': f'{v}:60001', 'value': f'{v}:60001' } for v in addresses],
multiple=False,
custom_value=True,
mode=SelectSelectorMode.LIST) # yapf: disable

schema = vol.Schema({
vol.Optional(CONF_EVENTS_DEST_ADDR, default=addr): SelectSelector(select),
})

return self.async_show_form(step_id="events", data_schema=schema, errors=errors)

async def async_step_controllers(self, user_input: Optional[Dict[str, Any]] = None):

def g(v):
Expand Down
15 changes: 13 additions & 2 deletions custom_components/uhppoted/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
"title": "UHPPOTE IPv4 - events configuration",
"description": "Network settings for controller events",
"data": {
"events_destination_address": "(optional) controller events listener address"
"events_destination_address": "controller set-events-listener address"
},
"data_description": {
"events_destination_address": "The destination IPv4 address for controller events e.g. 192.168.1.100:60001'"
"events_destination_address": "The destination IPv4 address for controller events e.g. 192.168.1.100:60001. Leave blank to configure manually."
}
},

Expand Down Expand Up @@ -170,6 +170,17 @@
}
},

"events": {
"title": "UHPPOTE IPv4 - events configuration",
"description": "Network settings for controller events",
"data": {
"events_destination_address": "controller set-events-listener address"
},
"data_description": {
"events_destination_address": "The destination IPv4 address for controller events e.g. 192.168.1.100:60001. Leave blank to configure manually."
}
},

"controllers": {
"title": "UHPPOTE controllers list",
"data": {
Expand Down
15 changes: 13 additions & 2 deletions custom_components/uhppoted/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
"title": "UHPPOTE IPv4 - events configuration",
"description": "Network settings for controller events",
"data": {
"events_destination_address": "controller events listener address (optional)"
"events_destination_address": "controller set-events-listener address"
},
"data_description": {
"events_destination_address": "The destination IPv4 address for controller events e.g. 192.168.1.100:60001'"
"events_destination_address": "The destination IPv4 address for controller events e.g. 192.168.1.100:60001. Leave blank to configure manually."
}
},

Expand Down Expand Up @@ -170,6 +170,17 @@
}
},

"events": {
"title": "UHPPOTE IPv4 - events configuration",
"description": "Network settings for controller events",
"data": {
"events_destination_address": "controller set-events-listener address"
},
"data_description": {
"events_destination_address": "The destination IPv4 address for controller events e.g. 192.168.1.100:60001. Leave blank to configure manually."
}
},

"controllers": {
"title": "UHPPOTE controllers list",
"data": {
Expand Down

0 comments on commit 12db094

Please sign in to comment.