Skip to content

Commit be2c7a4

Browse files
committed
0.7.0
1 parent 294bb99 commit be2c7a4

17 files changed

+304
-235
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# CHANGELOG
22

3+
## 0.7.0
4+
5+
- Fix default conf missing
6+
- Lint and code improvments
7+
- Bump pyserial-asyncio
8+
- Add service translation
9+
- Handle no USB device found during init
10+
311
## 0.6.1
412

513
- Fix incapacity to create switch entity when automatic entity add is disabled at bootstrap

custom_components/rfplayer/__init__.py

+27-34
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
"""Support for Rfplayer devices."""
2-
import asyncio
2+
from asyncio import timeout
33
from collections import defaultdict
44
import copy
55
import logging
66

7-
import async_timeout
87
from serial import SerialException
9-
from homeassistant.util import slugify
108
import voluptuous as vol
119

10+
from homeassistant.config_entries import ConfigEntry
1211
from homeassistant.const import (
1312
ATTR_ENTITY_ID,
1413
ATTR_STATE,
@@ -19,14 +18,14 @@
1918
CONF_PROTOCOL,
2019
EVENT_HOMEASSISTANT_STOP,
2120
)
22-
from homeassistant.core import CoreState, callback
21+
from homeassistant.core import CoreState, HomeAssistant, callback
22+
from homeassistant.helpers import device_registry as dr
2323
import homeassistant.helpers.config_validation as cv
24+
from homeassistant.helpers.device_registry import DeviceInfo
2425
from homeassistant.helpers.dispatcher import (
2526
async_dispatcher_connect,
2627
async_dispatcher_send,
2728
)
28-
from homeassistant.helpers import device_registry as dr
29-
from homeassistant.helpers.entity import DeviceInfo
3029
from homeassistant.helpers.restore_state import RestoreEntity
3130

3231
from .const import (
@@ -76,17 +75,19 @@ def identify_event_type(event):
7675
return "unknown"
7776

7877

79-
async def async_setup_entry(hass, entry):
78+
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
8079
"""Set up GCE RFPlayer from a config entry."""
80+
hass.data.setdefault(DOMAIN, {})
81+
8182
config = entry.data
8283
options = entry.options
8384

8485
async def async_send_command(call):
8586
"""Send Rfplayer command."""
8687
_LOGGER.debug("Rfplayer send command for %s", str(call.data))
8788
if not await hass.data[DOMAIN][RFPLAYER_PROTOCOL].send_command_ack(
88-
protocol=call.data[CONF_PROTOCOL],
89-
command=call.data[CONF_COMMAND],
89+
call.data[CONF_PROTOCOL],
90+
call.data[CONF_COMMAND],
9091
device_address=call.data.get(CONF_DEVICE_ADDRESS),
9192
device_id=call.data.get(CONF_DEVICE_ID),
9293
):
@@ -138,17 +139,15 @@ def event_callback(event):
138139
# Propagate event to every entity matching the device id
139140
_LOGGER.debug("passing event to %s", entity_id)
140141
async_dispatcher_send(hass, SIGNAL_HANDLE_EVENT.format(entity_id), event)
142+
elif event_type in hass.data[DOMAIN][DATA_DEVICE_REGISTER]:
143+
_LOGGER.debug("device_id not known, adding new device")
144+
hass.data[DOMAIN][DATA_ENTITY_LOOKUP][event_type][event_id] = event
145+
_add_device_to_base_config(event, event_id)
146+
hass.async_create_task(
147+
hass.data[DOMAIN][DATA_DEVICE_REGISTER][event_type](event)
148+
)
141149
else:
142-
# If device is not yet known, register with platform (if loaded)
143-
if event_type in hass.data[DOMAIN][DATA_DEVICE_REGISTER]:
144-
_LOGGER.debug("device_id not known, adding new device")
145-
hass.data[DOMAIN][DATA_ENTITY_LOOKUP][event_type][event_id] = event
146-
_add_device_to_base_config(event, event_id)
147-
hass.async_create_task(
148-
hass.data[DOMAIN][DATA_DEVICE_REGISTER][event_type](event)
149-
)
150-
else:
151-
_LOGGER.debug("device_id not known and automatic add disabled")
150+
_LOGGER.debug("device_id not known and automatic add disabled")
152151

153152
@callback
154153
def _add_device_to_base_config(event, event_id):
@@ -182,14 +181,10 @@ async def connect():
182181
)
183182

184183
try:
185-
with async_timeout.timeout(CONNECTION_TIMEOUT):
184+
with timeout(CONNECTION_TIMEOUT):
186185
transport, protocol = await connection
187186

188-
except (
189-
SerialException,
190-
OSError,
191-
asyncio.TimeoutError,
192-
) as exc:
187+
except (TimeoutError, SerialException, OSError) as exc:
193188
reconnect_interval = config[CONF_RECONNECT_INTERVAL]
194189
_LOGGER.exception(
195190
"Error connecting to Rfplayer, reconnecting in %s", reconnect_interval
@@ -240,8 +235,6 @@ class RfplayerDevice(RestoreEntity):
240235
Contains the common logic for Rfplayer entities.
241236
"""
242237

243-
platform = None
244-
_state = None
245238
_available = True
246239

247240
def __init__(
@@ -251,22 +244,22 @@ def __init__(
251244
device_id=None,
252245
initial_event=None,
253246
name=None,
254-
):
247+
) -> None:
255248
"""Initialize the device."""
256249
# Rflink specific attributes for every component type
257250
self._initial_event = initial_event
258251
self._protocol = protocol
259252
self._device_id = device_id
260253
self._device_address = device_address
261254
self._event = None
262-
self._state: bool = None
263255
self._attr_assumed_state = True
264-
if name is not None:
256+
self._attr_unique_id = "_".join(
257+
[self._protocol, self._device_address or self._device_id]
258+
)
259+
if name:
265260
self._attr_name = name
266-
self._attr_unique_id = slugify(f"{protocol}_{name}")
267261
else:
268-
self._attr_name = f"{protocol} {device_id or device_address}"
269-
self._attr_unique_id = slugify(f"{protocol}_{device_id or device_address}")
262+
self._attr_name = f"{protocol} {device_address or device_id}"
270263

271264
async def _async_send_command(self, command, *args):
272265
rfplayer = self.hass.data[DOMAIN][RFPLAYER_PROTOCOL]
@@ -298,7 +291,7 @@ def handle_event_callback(self, event):
298291

299292
def _handle_event(self, event):
300293
"""Platform specific event handler."""
301-
raise NotImplementedError()
294+
raise NotImplementedError
302295

303296
@property
304297
def should_poll(self):

custom_components/rfplayer/config_flow.py

+30-15
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
"""Config flow to configure the rfplayer integration."""
22
import os
3+
from typing import Any
34

45
import serial
56
import voluptuous as vol
67

7-
from homeassistant import config_entries, exceptions
8+
from homeassistant import exceptions
9+
from homeassistant.config_entries import (
10+
HANDLERS,
11+
ConfigEntry,
12+
ConfigFlow,
13+
ConfigFlowResult,
14+
OptionsFlow,
15+
)
816
from homeassistant.const import CONF_DEVICE, CONF_DEVICES
917
from homeassistant.core import callback
18+
from homeassistant.data_entry_flow import AbortFlow
1019

1120
from .const import (
1221
CONF_AUTOMATIC_ADD,
@@ -16,15 +25,17 @@
1625
)
1726

1827

19-
@config_entries.HANDLERS.register(DOMAIN)
20-
class RfplayerConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
28+
@HANDLERS.register(DOMAIN)
29+
class RfplayerConfigFlow(ConfigFlow, domain=DOMAIN):
2130
"""Handle a rfplayer config flow."""
2231

2332
VERSION = 1
2433

25-
async def async_step_user(self, user_input=None):
34+
async def async_step_user(
35+
self, user_input: dict[str, Any] | None = None
36+
) -> ConfigFlowResult:
2637
"""Config flow started from UI."""
27-
errors = {}
38+
schema_errors: dict[str, Any] = {}
2839

2940
if user_input is not None:
3041
user_input[CONF_DEVICES] = {}
@@ -33,20 +44,22 @@ async def async_step_user(self, user_input=None):
3344
get_serial_by_id, user_input[CONF_DEVICE]
3445
)
3546

36-
if not errors:
47+
if not schema_errors:
3748
return self.async_create_entry(
3849
title=user_input[CONF_DEVICE], data=user_input
3950
)
4051

4152
ports = await self.hass.async_add_executor_job(serial.tools.list_ports.comports)
4253
list_of_ports = {}
4354
for port in ports:
44-
list_of_ports[
45-
port.device
46-
] = f"{port}, s/n: {port.serial_number or 'n/a'}" + (
47-
f" - {port.manufacturer}" if port.manufacturer else ""
55+
list_of_ports[port.device] = (
56+
f"{port}, s/n: {port.serial_number or 'n/a'}"
57+
+ (f" - {port.manufacturer}" if port.manufacturer else "")
4858
)
4959

60+
if not list_of_ports:
61+
raise AbortFlow("no_devices_found")
62+
5063
data = {
5164
vol.Required(CONF_DEVICE): vol.In(list_of_ports),
5265
vol.Required(CONF_AUTOMATIC_ADD, default=True): bool,
@@ -57,24 +70,26 @@ async def async_step_user(self, user_input=None):
5770
return self.async_show_form(
5871
step_id="user",
5972
data_schema=vol.Schema(data),
60-
errors=errors,
73+
errors=schema_errors,
6174
)
6275

6376
@staticmethod
6477
@callback
65-
def async_get_options_flow(config_entry):
78+
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
6679
"""Define the config flow to handle options."""
6780
return RfPlayerOptionsFlowHandler(config_entry)
6881

6982

70-
class RfPlayerOptionsFlowHandler(config_entries.OptionsFlow):
83+
class RfPlayerOptionsFlowHandler(OptionsFlow):
7184
"""Handle a RFPLayer options flow."""
7285

73-
def __init__(self, config_entry):
86+
def __init__(self, config_entry: ConfigEntry) -> None:
7487
"""Initialize."""
7588
self.config_entry = config_entry
7689

77-
async def async_step_init(self, user_input: dict = None):
90+
async def async_step_init(
91+
self, user_input: dict[str, Any] | None = None
92+
) -> ConfigFlowResult:
7893
"""Manage the options."""
7994
if user_input is None:
8095
config = self.config_entry.data

custom_components/rfplayer/const.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
DEFAULT_RECONNECT_INTERVAL = 10
99
DEFAULT_SIGNAL_REPETITIONS = 1
1010

11-
PLATFORMS = ["sensor", "switch", "number"]
11+
PLATFORMS = ["number", "sensor", "switch"]
1212

1313
ATTR_EVENT = "event"
1414

custom_components/rfplayer/icons.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"services": {
3+
"send_command": "mdi:remote"
4+
}
5+
}

custom_components/rfplayer/manifest.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
"documentation": "https://github.com/gce-electronics/HA_RFPlayer",
88
"iot_class": "assumed_state",
99
"issue_tracker": "https://github.com/gce-electronics/HA_RFPlayer/issues",
10-
"requirements": ["pyserial==3.5", "pyserial-asyncio==0.5"],
11-
"version": "0.6.1"
10+
"requirements": ["pyserial==3.5", "pyserial-asyncio==0.6"],
11+
"version": "0.7.0"
1212
}

custom_components/rfplayer/number.py

+28-20
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,61 @@
11
"""Support for Rfplayer number."""
22
import logging
33

4-
from homeassistant.components.number import NumberEntity
5-
from homeassistant.core import callback
6-
from homeassistant.helpers.entity import EntityCategory
4+
from homeassistant.components.number import NumberMode, RestoreNumber
5+
from homeassistant.config_entries import ConfigEntry
6+
from homeassistant.core import HomeAssistant, callback
7+
from homeassistant.helpers.entity_platform import AddEntitiesCallback
78

8-
from . import RfplayerDevice
9-
from .const import DOMAIN, RFPLAYER_PROTOCOL
9+
from . import EVENT_KEY_COMMAND, RfplayerDevice
10+
from .const import DATA_ENTITY_LOOKUP, DOMAIN, EVENT_KEY_ID, RFPLAYER_PROTOCOL
1011

1112
_LOGGER = logging.getLogger(__name__)
1213

1314

14-
async def async_setup_entry(hass, entry, async_add_entities):
15+
async def async_setup_entry(
16+
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
17+
) -> None:
1518
"""Set up the Rfplayer platform."""
1619
_LOGGER.debug("Add jamming number entity")
1720
async_add_entities([RfplayerJammingNumber()])
1821

1922

20-
class RfplayerJammingNumber(RfplayerDevice, NumberEntity):
23+
class RfplayerJammingNumber(RfplayerDevice, RestoreNumber):
2124
"""Representation of a Rfplayer jamming number setting entity."""
2225

23-
def __init__(self):
26+
_attr_native_min_value = 0
27+
_attr_native_max_value = 10
28+
_attr_mode = NumberMode.SLIDER
29+
30+
def __init__(self) -> None:
2431
"""Init the number rfplayer entity."""
25-
self._attr_native_min_value = 0
26-
self._attr_native_max_value = 10
27-
self._attr_native_mode = "slider"
28-
self._attr_native_entity_category = EntityCategory.CONFIG
29-
super().__init__("JAMMING", device_id=0, name="Jamming detection level")
32+
self._state: int | None = None
33+
super().__init__("JAMMING")
3034

31-
async def async_added_to_hass(self):
35+
async def async_added_to_hass(self) -> None:
3236
"""Restore RFPlayer device state."""
3337
await super().async_added_to_hass()
38+
39+
self.hass.data[DOMAIN][DATA_ENTITY_LOOKUP][EVENT_KEY_COMMAND][
40+
self._initial_event[EVENT_KEY_ID]
41+
] = self.entity_id
42+
3443
if self._event is None:
3544
old_state = await self.async_get_last_state()
3645
if old_state is not None:
37-
self._state = old_state.state
46+
self._state = int(old_state.state)
3847

3948
@callback
4049
def _handle_event(self, event):
41-
self._state = int(event["value"])
50+
self._state = int(event["command"])
4251

4352
@property
44-
def native_value(self):
53+
def value(self):
4554
"""Return the current setting."""
4655
return self._state
4756

48-
async def async_set_native_value(self, value) -> None:
57+
async def async_set_native_value(self, value: float) -> None:
4958
"""Update the current value."""
5059
rfplayer = self.hass.data[DOMAIN][RFPLAYER_PROTOCOL]
5160
await rfplayer.send_command_ack(command=int(value), protocol=self._protocol)
52-
self._state = value
53-
self.async_write_ha_state()
61+
self._state = int(value)
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
# noqa
1+
"""rflib."""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""RfPlayer exceptions."""
2+
3+
4+
class RfPlayerException(Exception):
5+
"""Generic RfPlayer exception."""

0 commit comments

Comments
 (0)