Skip to content

Commit

Permalink
Merge pull request #564 from MTrab/Add-input_number-entities
Browse files Browse the repository at this point in the history
Migrate services to input_numbers
  • Loading branch information
MTrab authored Apr 15, 2024
2 parents 86e5e0f + 7a97c6a commit aa1a940
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 78 deletions.
2 changes: 1 addition & 1 deletion custom_components/landroid_cloud/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
DEFAULT_NAME = "landroid"
DOMAIN = "landroid_cloud"
PLATFORMS_SECONDARY = []
PLATFORMS_PRIMARY = ["lawn_mower", "sensor", "switch", "binary_sensor", "button"]
PLATFORMS_PRIMARY = ["lawn_mower", "sensor", "switch", "binary_sensor", "number", "button"]
UPDATE_SIGNAL = "landroid_cloud_update"
LOGLEVEL = LogLevel.DEBUG
ENTITY_ID_FORMAT = DOMAIN + ".{}"
Expand Down
147 changes: 129 additions & 18 deletions custom_components/landroid_cloud/device_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
LawnMowerEntity,
LawnMowerEntityFeature,
)
from homeassistant.components.number import NumberEntity, NumberEntityDescription
from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
Expand All @@ -34,7 +35,7 @@
from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send
from homeassistant.helpers.entity_registry import EntityRegistry
from homeassistant.util import slugify as util_slugify
from pyworxcloud import WorxCloud
from pyworxcloud import DeviceCapability, WorxCloud
from pyworxcloud.exceptions import (
NoOneTimeScheduleError,
NoPartymodeError,
Expand Down Expand Up @@ -574,6 +575,8 @@ async def async_added_to_hass(self):

self.register_services()

self._attributes.update({"protocol": self.api.device.protocol})

await self.hass.config_entries.async_forward_entry_setups(
self.api.entry, PLATFORMS_SECONDARY
)
Expand Down Expand Up @@ -825,23 +828,6 @@ async def async_config(self, data: dict | None = None) -> None:
tmpdata = {}
device: WorxCloud = self.api.device

if "raindelay" in data:
self.log(
LoggerType.SERVICE_CALL,
"Setting raindelayto %s minutes",
data["raindelay"],
)
tmpdata["rd"] = int(data["raindelay"])

if "timeextension" in data:
self.log(
LoggerType.SERVICE_CALL,
"Setting timeextension to %s%%",
data["timeextension"],
)
tmpdata["sc"] = {}
tmpdata["sc"]["p"] = int(data["timeextension"])

if "multizone_distances" in data:
self.log(
LoggerType.SERVICE_CALL,
Expand Down Expand Up @@ -935,6 +921,16 @@ class LandroidBinarySensorEntityDescription(
"""Describes a Landroid binary_sensor."""


@dataclass
class LandroidNumberEntityDescription(NumberEntityDescription):
"""Describes a Landroid number."""

value_fn: Callable[[LandroidAPI], bool | str | int | float | None] = None
command_fn: Callable[[LandroidAPI, str], None] = None
required_protocol: int | None = None
required_capability: DeviceCapability | None = None


@dataclass
class LandroidSwitchEntityDescription(
SwitchEntityDescription, LandroidBaseEntityDescriptionMixin
Expand Down Expand Up @@ -1156,6 +1152,121 @@ async def handle_update(self) -> None:
pass


class LandroidNumber(NumberEntity):
"""Representation of a Landroid number."""

_attr_has_entity_name = True

def __init__(
self,
hass: HomeAssistant,
description: LandroidNumberEntityDescription,
api: LandroidAPI,
config: ConfigEntry,
) -> None:
"""Initialize a Landroid number entity."""
super().__init__()

self.entity_description = description
self.hass = hass
self.device = api.device

self._api = api
self._config = config

self._value = None

self._attr_name = self.entity_description.name

_LOGGER.debug(
"(%s, Setup) Added switch '%s'",
self._api.friendly_name,
self._attr_name,
)

self._attr_unique_id = util_slugify(
f"{self._attr_name}_{self._config.entry_id}_{self._api.device.serial_number}"
)
self._attr_should_poll = False

self._attr_device_info = {
"identifiers": {
(
DOMAIN,
self._api.unique_id,
self._api.entry_id,
self._api.device.serial_number,
)
},
"name": str(f"{self._api.friendly_name}"),
"sw_version": self._api.device.firmware["version"],
"manufacturer": self._api.config["type"].capitalize(),
"model": self._api.device.model,
"serial_number": self._api.device.serial_number,
}

if self.device.mac_address != "__UUID__":
_connections = {(dr.CONNECTION_NETWORK_MAC, self.device.mac_address)}
self._attr_device_info.update({"connections": _connections})

async_dispatcher_connect(
self.hass,
util_slugify(f"{UPDATE_SIGNAL}_{self._api.device.name}"),
self.handle_update,
)

@property
def available(self) -> bool:
"""Return if the entity is available."""
return self._api.device.online

@property
def native_value(self) -> float | None:
"""Return the entity value to represent the entity state."""
val = self.entity_description.value_fn(self._api)
_LOGGER.debug(
"(%s, Show Value) Returning number '%s' with value '%s'",
self._api.friendly_name,
self._attr_name,
val,
)

return val

async def async_added_to_hass(self) -> None:
"""Set state on adding to home assistant."""
# await self.handle_update()
return await super().async_added_to_hass()

async def handle_update(self) -> None:
"""Handle the updates when recieving an update signal."""
try:
self._value = self.entity_description.value_fn(self.device)
except AttributeError:
return

_LOGGER.debug(
"(%s, Update signal) Updating number '%s'",
self._api.friendly_name,
self._attr_name,
)
try:
self.async_write_ha_state()
except RuntimeError:
pass

def set_native_value(self, value: float) -> None:
"""Set number value"""
_LOGGER.debug(
"(%s, Set value) Setting number value for '%s' to %s",
self._api.friendly_name,
self._attr_name,
value,
)

self.entity_description.command_fn(self._api, value)


class LandroidSwitch(SwitchEntity):
"""Representation of a Landroid switch."""

Expand Down
92 changes: 92 additions & 0 deletions custom_components/landroid_cloud/number.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"""Input numbers for landroid_cloud."""

from __future__ import annotations

import json

from homeassistant.components.number import NumberDeviceClass, NumberMode
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from pyworxcloud import DeviceCapability

from .api import LandroidAPI
from .const import ATTR_DEVICES, DOMAIN
from .device_base import LandroidNumber, LandroidNumberEntityDescription

INPUT_NUMBERS = [
LandroidNumberEntityDescription(
key="timeextention",
name="Time extention",
entity_category=EntityCategory.CONFIG,
device_class=None,
entity_registry_enabled_default=True,
native_unit_of_measurement="%",
native_min_value=-100,
native_max_value=100,
native_step=1,
mode=NumberMode.SLIDER,
value_fn=lambda api: api.cloud.device.schedules["time_extension"],
command_fn=lambda api, value: api.cloud.send(
api.device.serial_number, json.dumps({"sc": {"p": value}})
),
required_protocol=0,
),
LandroidNumberEntityDescription(
key="torque",
name="Torque",
entity_category=EntityCategory.CONFIG,
device_class=NumberDeviceClass.POWER_FACTOR,
entity_registry_enabled_default=True,
native_unit_of_measurement=None,
native_min_value=-50,
native_max_value=50,
native_step=1,
mode=NumberMode.SLIDER,
value_fn=lambda api: api.device.torque,
command_fn=lambda api, value: api.cloud.send(
api.device.serial_number, json.dumps({"tq": value})
),
required_capability=DeviceCapability.TORQUE,
),
LandroidNumberEntityDescription(
key="raindelay",
name="Raindelay",
entity_category=EntityCategory.CONFIG,
device_class=None,
entity_registry_enabled_default=True,
native_unit_of_measurement="minutes",
native_min_value=0,
native_max_value=300,
native_step=1,
mode=NumberMode.BOX,
value_fn=lambda api: api.device.rainsensor["delay"],
command_fn=lambda api, value: api.cloud.raindelay(
api.device.serial_number, value
),
icon="mdi:weather-rainy",
),
]


async def async_setup_entry(
hass: HomeAssistant,
config: ConfigEntry,
async_add_devices,
) -> None:
"""Set up the switch platform."""
entities = []
for _, info in hass.data[DOMAIN][config.entry_id][ATTR_DEVICES].items():
api: LandroidAPI = info["api"]
for number in INPUT_NUMBERS:
if (
isinstance(number.required_protocol, type(None))
or number.required_protocol == api.device.protocol
) and (
isinstance(number.required_capability, type(None))
or api.device.capabilities.check(number.required_capability)
):
entity = LandroidNumber(hass, number, api, config)
entities.append(entity)

async_add_devices(entities)
13 changes: 0 additions & 13 deletions custom_components/landroid_cloud/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,19 +182,6 @@
suggested_display_precision=1,
icon="mdi:axis-z-rotate-clockwise",
),
LandroidSensorEntityDescription(
key="rainsensor_delay",
name="Rainsensor Delay",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=None,
device_class=SensorDeviceClass.DURATION,
entity_registry_enabled_default=True,
native_unit_of_measurement="min",
value_fn=lambda landroid: (
landroid.rainsensor["delay"] if "delay" in landroid.rainsensor else None
),
icon="mdi:weather-rainy",
),
LandroidSensorEntityDescription(
key="rainsensor_remaining",
name="Rainsensor Remaining",
Expand Down
3 changes: 0 additions & 3 deletions custom_components/landroid_cloud/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,6 @@ class LandroidServiceDescription:
schema=SCHEDULE_SCHEME,
feature=LandroidFeatureSupport.SCHEDULES,
),
LandroidServiceDescription(
key=SERVICE_TORQUE, schema=TORQUE_SCHEME, feature=LandroidFeatureSupport.TORQUE
),
LandroidServiceDescription(
key=SERVICE_SEND_RAW, feature=LandroidFeatureSupport.CONFIG, schema=RAW_SCHEME
),
Expand Down
43 changes: 0 additions & 43 deletions custom_components/landroid_cloud/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,6 @@ config:
integration: landroid_cloud
domain: lawn_mower
fields:
raindelay:
name: Rain delay
description: Set rain delay. Time in minutes ranging from 0 to 300. 0 = Disabled
example: 30
selector:
number:
min: 0
max: 300
step: 1
unit_of_measurement: "minutes"
mode: slider
timeextension:
name: Time extension
description: Set time extension. Extension in % ranging from -100 to 100
example: -23
selector:
number:
min: -100
max: 100
step: 1
unit_of_measurement: "%"
mode: slider
multizone_distances:
name: Multi zone distances
description: 'Set multizone distance array in meters. 0 = Disabled. Format: 15, 80, 120, 155'
Expand Down Expand Up @@ -235,27 +213,6 @@ schedule:
selector:
boolean:

torque:
description: Set wheel torque
target:
entity:
integration: landroid_cloud
domain: lawn_mower
fields:
torque:
name: Wheel torque
description: Set wheel torque. Ranging from -50% to 50%
example: 22
required: true
default: 0
selector:
number:
min: -50
max: 50
step: 1
unit_of_measurement: "%"
mode: slider

send_raw:
description: Send a raw JSON command to the device
target:
Expand Down

0 comments on commit aa1a940

Please sign in to comment.