Skip to content

Commit

Permalink
Adds a repair notification when hub server version is wrong (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
JohNan authored Oct 20, 2023
1 parent 6667055 commit 6aeaf91
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 51 deletions.
67 changes: 41 additions & 26 deletions custom_components/flichub/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,19 @@
import logging
from dataclasses import dataclass
from datetime import timedelta
from homeassistant.helpers.issue_registry import async_create_issue, IssueSeverity

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from pyflichub.button import FlicButton
from pyflichub.client import FlicHubTcpClient
from pyflichub.client import FlicHubTcpClient, ServerCommand
from pyflichub.command import Command
from pyflichub.event import Event
from .const import CLIENT_READY_TIMEOUT, EVENT_CLICK, EVENT_DATA_NAME, EVENT_DATA_CLICK_TYPE, \
EVENT_DATA_SERIAL_NUMBER, DATA_BUTTONS, DATA_HUB
EVENT_DATA_SERIAL_NUMBER, DATA_BUTTONS, DATA_HUB, REQUIRED_SERVER_VERSION, DEFAULT_SCAN_INTERVAL
from .const import DOMAIN
from .const import PLATFORMS

Expand All @@ -34,7 +35,7 @@ class FlicHubEntryData:
"""Class for sharing data within the Nanoleaf integration."""

client: FlicHubTcpClient
coordinator: DataUpdateCoordinator[None]
coordinator: DataUpdateCoordinator[dict]


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
Expand All @@ -43,6 +44,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
hass.data.setdefault(DOMAIN, {})

def on_event(button: FlicButton, event: Event):
_LOGGER.debug(f"Event: {event}")
if event.event == "button":
hass.bus.fire(EVENT_CLICK, {
EVENT_DATA_SERIAL_NUMBER: button.serial_number,
Expand All @@ -52,69 +54,82 @@ def on_event(button: FlicButton, event: Event):
if event.event == "buttonReady":
hass.async_create_task(coordinator.async_refresh())

def on_commend(command: Command):
_LOGGER.debug(f"Command: {command.data}")
if command.command == "buttons":
def on_command(command: Command):
_LOGGER.debug(f"Command: {command.command}, data: {command.data}")
if command is None:
return
if command.command == ServerCommand.SERVER_INFO:
hub_version = command.data.version
if hub_version != REQUIRED_SERVER_VERSION:
async_create_issue(
hass,
DOMAIN,
f"invalid_server_version_{entry.entry_id}",
is_fixable=False,
severity=IssueSeverity.ERROR,
translation_key=f"{DOMAIN}_invalid_server_version",
translation_placeholders={
"required_version": REQUIRED_SERVER_VERSION,
"flichub_version": hub_version,
},
)
if command.command == ServerCommand.BUTTONS:
coordinator.async_set_updated_data(
{
DATA_BUTTONS: {button.serial_number: button for button in command.data},
DATA_HUB: coordinator.data.get(DATA_HUB, None) if coordinator.data else None
}
)
if command.command == "network":
coordinator.async_set_updated_data(
{
DATA_BUTTONS: coordinator.data.get(DATA_BUTTONS, None) if coordinator.data else {},
DATA_HUB: command.data
}
)

client = FlicHubTcpClient(
ip=entry.data[CONF_IP_ADDRESS],
port=entry.data[CONF_PORT],
loop=asyncio.get_event_loop(),
event_callback=on_event,
command_callback=on_commend
command_callback=on_command
)
client_ready = asyncio.Event()

async def async_get_buttons() -> [FlicButton]:
async def async_update() -> dict:
buttons = await client.get_buttons()
hub_info = await client.get_hubinfo()
return {
DATA_BUTTONS: {button.serial_number: button for button in buttons},
DATA_HUB: hub_info
DATA_BUTTONS: {button.serial_number: button for button in buttons} if buttons is not None else {},
DATA_HUB: hub_info if hub_info is not None else {}
}

def client_connected():
async def client_connected():
_LOGGER.debug("Connected!")
client_ready.set()
await client.get_server_info()

def client_disconnected():
async def client_disconnected():
_LOGGER.debug("Disconnected!")

client.on_connected = client_connected
client.on_disconnected = client_disconnected

asyncio.create_task(client.async_connect())

def stop_client(event):
client.disconnect()

client.async_on_connected = client_connected
client.async_on_disconnected = client_disconnected

await client.async_connect()

hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_client)

try:
with async_timeout.timeout(CLIENT_READY_TIMEOUT):
await client_ready.wait()
except asyncio.TimeoutError:
print(f"Client not connected after {CLIENT_READY_TIMEOUT} secs so continuing with setup")
_LOGGER.error(f"Client not connected after {CLIENT_READY_TIMEOUT} secs. Discontinuing setup")
client.disconnect()
raise ConfigEntryNotReady

coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
name=entry.title,
update_method=async_get_buttons
update_interval=timedelta(seconds=DEFAULT_SCAN_INTERVAL),
update_method=async_update
)

await coordinator.async_config_entry_first_refresh()
Expand Down
40 changes: 28 additions & 12 deletions custom_components/flichub/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Adds config flow for Flic Hub."""
from typing import Any

import async_timeout
import asyncio
import logging
Expand Down Expand Up @@ -40,7 +42,7 @@ async def async_step_dhcp(self, discovery_info):
self.context["title_placeholders"] = {CONF_IP_ADDRESS: self._ip_address}
return await self.async_step_user()

async def async_step_user(self, user_input=None):
async def async_step_user(self, user_input: dict[str, Any] | None = None):
"""Handle a flow initialized by the user."""
self._errors = {}

Expand All @@ -51,7 +53,7 @@ async def async_step_user(self, user_input=None):
)
if valid:
return await self._create_entry(user_input)
self._errors["base"] = "auth"
self._errors["base"] = "cannot_connect"

return await self._show_config_form(user_input)

Expand All @@ -75,34 +77,47 @@ async def _create_entry(self, user_input):
return self.async_abort(reason="reauth_successful")
return self.async_create_entry(title=user_input[CONF_NAME], data=user_input)

async def _show_config_form(self, user_input): # pylint: disable=unused-argument
async def _show_config_form(self, user_input: dict[str, Any] | None = None):
"""Show the configuration form to edit location data."""
if user_input is None:
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_NAME): str,
vol.Required(CONF_IP_ADDRESS, default=self._ip_address): str,
vol.Required(CONF_PORT, default="8124"): str
}
),
errors=self._errors,
)

return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_NAME): str,
vol.Required(CONF_IP_ADDRESS, default=self._ip_address): str,
vol.Required(CONF_PORT, default=8124): str
vol.Required(CONF_NAME, default=user_input.get(CONF_NAME, None)): str,
vol.Required(CONF_IP_ADDRESS, default=user_input.get(CONF_IP_ADDRESS, self._ip_address)): str,
vol.Required(CONF_PORT, default=user_input.get(CONF_PORT, "8124")): str
}
),
errors=self._errors,
)

async def _test_credentials(self, ip, port):
"""Return true if credentials is valid."""
client = FlicHubTcpClient(ip, port, asyncio.get_event_loop())
try:
client = FlicHubTcpClient(ip, port, asyncio.get_event_loop())
client_ready = asyncio.Event()

def client_connected():
async def client_connected():
client_ready.set()

def client_disconnected():
async def client_disconnected():
_LOGGER.debug("Disconnected")

client.on_connected = client_connected
client.on_disconnected = client_disconnected
client.async_on_connected = client_connected
client.async_on_disconnected = client_disconnected

asyncio.create_task(client.async_connect())

Expand All @@ -112,7 +127,8 @@ def client_disconnected():
client.disconnect()
return True
except Exception as e: # pylint: disable=broad-except
_LOGGER.error("Error connecting: %s", e)
_LOGGER.error("Error connecting", exc_info=e)
client.disconnect()
pass
return False

Expand Down
2 changes: 2 additions & 0 deletions custom_components/flichub/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
DOMAIN = "flichub"
DOMAIN_DATA = f"{DOMAIN}_data"
VERSION = "0.0.0"
REQUIRED_SERVER_VERSION = "0.1.8"
DEFAULT_SCAN_INTERVAL = 60

CLIENT_READY_TIMEOUT = 20.0

Expand Down
2 changes: 1 addition & 1 deletion custom_components/flichub/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@
"iot_class": "local_push",
"issue_tracker": "https://github.com/JohNan/home-assistant-flichub/issues",
"loggers": ["pyflichub"],
"requirements": ["pyflichub-tcpclient==0.1.7"],
"requirements": ["pyflichub-tcpclient==0.1.8"],
"version": "v0.0.0"
}
37 changes: 37 additions & 0 deletions custom_components/flichub/strings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"config": {
"step": {
"user": {
"title": "Flic Hub",
"description": "If you need help with the configuration have a look here: https://github.com/JohNan/home-assistant-flichub. Default port is 8124",
"data": {
"name": "Name your device",
"ip_address": "[%key:common::config_flow::data::ip%]",
"port": "[%key:common::config_flow::data::port%]"
}
}
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
},
"abort": {
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
}
},
"options": {
"step": {
"user": {
"data": {
"binary_sensor": "Binary sensor enabled",
"sensor": "Sensor enabled"
}
}
}
},
"issues": {
"flichub_invalid_server_version": {
"title": "Invalid server version on FlicHub",
"description": "The TCP server on the FlicHub is running version `{flichub_version}` which does not match the required version `{required_version}`. Please go to https://github.com/JohNan/pyflichub-tcpclient and follow the instructions"
}
}
}
21 changes: 9 additions & 12 deletions custom_components/flichub/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,16 @@
"step": {
"user": {
"title": "Flic Hub",
"description": "If you need help with the configuration have a look here: https://github.com/JohNan/home-assistant-flichub",
"description": "If you need help with the configuration have a look here: https://github.com/JohNan/home-assistant-flichub. Default port is 8124",
"data": {
"name": "Name your device",
"ip_address": "IP Address",
"port": "Port (default: 8124)"
}
},
"devices": {
"title": "Flic Hub",
"description": "If you need help with the configuration have a look here: https://github.com/JohNan/home-assistant-flichub",
"data": {
"ip_address": "Discovered devices",
"port": "Port (default: 8124)"
"port": "Port"
}
}
},
"error": {
"auth": "Unable to connect to device",
"no_devices": "No devices found. Enter IP and port manually"
"cannot_connect": "Unable to connect to device"
},
"abort": {
"single_instance_allowed": "Only a single instance is allowed."
Expand All @@ -36,5 +27,11 @@
}
}
}
},
"issues": {
"flichub_invalid_server_version": {
"title": "Invalid server version on FlicHub",
"description": "The TCP server on the FlicHub is running version `{flichub_version}` which does not match the required version `{required_version}`. Please go to https://github.com/JohNan/pyflichub-tcpclient and follow the instructions"
}
}
}

0 comments on commit 6aeaf91

Please sign in to comment.