-
-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace deprecated constants #36
Comments
This solves it in const.py: """
Home Assistant - Nexa Bridge X Integration
Author: Anders Evenrud <[email protected]>
Homepage: https://github.com/andersevenrud/ha-nexa-bridge-x
License: MIT
"""
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorStateClass,
)
from homeassistant.const import (
UnitOfPower,
UnitOfEnergy,
UnitOfElectricPotential,
UnitOfElectricCurrent,
UnitOfTemperature,
PERCENTAGE,
LIGHT_LUX
)
DOMAIN = "nexa_bridge_x"
POLL_INTERVAL = 30
POLL_TIMEOUT = 25
CALL_TIMEOUT = 30
RECONNECT_SLEEP = 5
DEFAULT_USERNAME = "nexa"
DEFAULT_PASSWORD = "nexa"
WS_PORT = 8887
HTTP_BASIC_AUTH = False
SWITCH_LEVEL_SENSOR = False
SWITCH_BINARY_SENSOR = False
FORCE_NODE_ENUM = False
NODE_MEDIA_CAPABILITIES = [
"mediaVolume",
"mediaPlayPause",
"mediaMute"
]
NODE_BINARY_CAPABILITIES = [
"notificationContact",
"notificationMotion",
"notificationSmoke",
"notificationWater",
"notificationTwilight",
"notificationTamper",
"notificationButton",
"notification"
] + (SWITCH_BINARY_SENSOR and ["switchBinary"] or [])
NODE_SENSOR_CAPABILITIES = [
"meter",
"power",
"electric_voltage",
"electric_ampere",
"temperature",
"humidity",
"luminance",
"battery",
"customEvent"
] + (SWITCH_LEVEL_SENSOR and ["switchLevel"] or [])
ENERGY_ATTRS = [
"total_kilowatt_hours",
"current_wattage",
"current_kilowatt_hours",
"today_kilowatt_hours",
"yesterday_kilowatt_hours",
"month_kilowatt_hours"
]
# TODO: Add support for legacy energy metering
LEGACY_ENERGY_ATTRS = [
#"total_kilowatt_hours",
#"current_wattage",
]
BINARY_MAP = {
"notificationContact": {
"name": "Contact"
},
"notificationMotion": {
"name": "Motion"
},
"notificationSmoke": {
"name": "Smoke"
},
"notificationWater": {
"name": "Water"
},
"notificationTwilight": {
"name": "Twilight"
},
"notificationTamper": {
"name": "Tamper"
},
"notificationButton": {
"name": "Button"
},
"notification": {
"name": "Value"
},
"switchBinary": {
"name": "Switch"
},
}
SENSOR_MAP = {
"switchLevel": {
"name": "Level",
"unit": PERCENTAGE,
"device": None,
"class": SensorStateClass.MEASUREMENT
},
"meter": {
"name": "Energy",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"device": SensorDeviceClass.ENERGY,
"class": SensorStateClass.TOTAL_INCREASING
},
"power": {
"name": "Wattage",
"unit": UnitOfPower.WATT,
"device": SensorDeviceClass.POWER,
"class": SensorStateClass.MEASUREMENT
},
"electric_voltage": {
"name": "Voltage",
"unit": UnitOfElectricPotential.VOLT,
"device": SensorDeviceClass.VOLTAGE,
"class": SensorStateClass.MEASUREMENT
},
"electric_ampere": {
"name": "Amperage",
"unit": UnitOfElectricCurrent.AMPERE,
"device": SensorDeviceClass.CURRENT,
"class": SensorStateClass.MEASUREMENT
},
"temperature": {
"name": "Temperature",
"unit": UnitOfTemperature.CELSIUS,
"device": SensorDeviceClass.TEMPERATURE,
"class": SensorStateClass.MEASUREMENT
},
"humidity": {
"name": "Humidity",
"unit": PERCENTAGE,
"device": SensorDeviceClass.HUMIDITY,
"class": SensorStateClass.MEASUREMENT
},
"luminance": {
"name": "Luminance",
"unit": LIGHT_LUX,
"device": SensorDeviceClass.ILLUMINANCE,
"class": SensorStateClass.MEASUREMENT
},
"battery": {
"name": "Battery",
"unit": PERCENTAGE,
"device": SensorDeviceClass.BATTERY,
"class": SensorStateClass.MEASUREMENT
},
"customEvent": {
"name": "Trigger",
"unit": None,
"device": SensorDeviceClass.ENUM,
"class": None
},
}
ENERGY_MAP = {
"total_kilowatt_hours": {
"name": "Total kWh",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"device": SensorDeviceClass.ENERGY,
"class": SensorStateClass.TOTAL_INCREASING
},
"current_wattage": {
"name": "Current W",
"unit": UnitOfPower.WATT,
"device": SensorDeviceClass.POWER,
"class": SensorStateClass.MEASUREMENT
},
"current_kilowatt_hours": {
"name": "Current kWh",
"unit": UnitOfPower.KILO_WATT,
"device": SensorDeviceClass.POWER,
"class": SensorStateClass.MEASUREMENT
},
"today_kilowatt_hours": {
"name": "Today kWh",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"device": SensorDeviceClass.ENERGY,
"class": SensorStateClass.TOTAL_INCREASING
},
"yesterday_kilowatt_hours": {
"name": "Yesterday kWh",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"device": SensorDeviceClass.ENERGY,
"class": SensorStateClass.TOTAL_INCREASING
},
"month_kilowatt_hours": {
"name": "Month kWh",
"unit": UnitOfEnergy.KILO_WATT_HOUR,
"device": SensorDeviceClass.ENERGY,
"class": SensorStateClass.TOTAL_INCREASING
},
} |
2024-03-30 22:12:52.582 WARNING (MainThread) [homeassistant.components.light] None (<class 'custom_components.nexa_bridge_x.entities.NexaDimmerEntity'>) sets invalid supported color modes {<ColorMode.BRIGHTNESS: 'brightness'>, <ColorMode.ONOFF: 'onoff'>}, this will stop working in Home Assistant Core 2025.3, please create a bug report at https://github.com/andersevenrud/ha-nexa-bridge-x/issues |
I don't know fully what I'm doing, but this solved the issue in entities.py: """
Home Assistant - Nexa Bridge X Integration
Author: Anders Evenrud <[email protected]>
Homepage: https://github.com/andersevenrud/ha-nexa-bridge-x
License: MIT
"""
from __future__ import annotations
from homeassistant.core import callback
from homeassistant.components.sensor import SensorEntity
from homeassistant.components.switch import SwitchEntity
from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.components.media_player import (
MediaPlayerDeviceClass,
MediaPlayerEntity,
MediaPlayerEntityFeature,
MediaPlayerState,
MediaType,
)
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator
)
from homeassistant.components.light import (
LightEntity,
ColorMode,
ATTR_BRIGHTNESS,
ATTR_COLOR_MODE,
LightEntityFeature,
filter_supported_color_modes
)
from .const import (DOMAIN, SENSOR_MAP, ENERGY_MAP, BINARY_MAP)
from .nexa import (NexaNode, NexaNodeValueType)
import logging
_LOGGER = logging.getLogger(__name__)
class NexaEntity(CoordinatorEntity):
"""Representation of a Nexa Bridge entity"""
_attr_has_entity_name = True
def __init__(self, coordinator: CoordinatorEntity) -> None:
super().__init__(coordinator)
self._server_unique_id = coordinator.config_entry.entry_id
self._attr_device_info = DeviceInfo(
manufacturer="Nexa",
entry_type=DeviceEntryType.SERVICE,
model=coordinator.data.info.model,
name=coordinator.data.info.name,
sw_version=coordinator.data.info.version,
identifiers={
(DOMAIN, coordinator.config_entry.entry_id)
},
configuration_url=(
f"http://{coordinator.api.host}"
)
)
class NexaNodeEntity(NexaEntity):
"""Representation of a Nexa Device entity"""
_attr_has_entity_name = True
def __init__(self, node: NexaNode, coordinator: CoordinatorEntity) -> None:
super().__init__(coordinator)
self._attr_device_info = DeviceInfo(
name=f"{coordinator.data.info.name} {node.name or node.id}",
via_device=(DOMAIN, coordinator.config_entry.entry_id),
identifiers={
(DOMAIN, node.id)
}
)
class NexaDimmerEntity(NexaNodeEntity, LightEntity):
"""Entity for light"""
supported_color_modes = {ColorMode.BRIGHTNESS}
color_mode = ColorMode.BRIGHTNESS
def __init__(self, coordinator: DataUpdateCoordinator, node: NexaNode):
_LOGGER.info("Found light %s: %s", node.id, node.name)
super().__init__(node, coordinator)
self.id = node.id
self._attr_name = "Light"
self._attr_unique_id = f"dimmer_{node.id}"
@callback
def _handle_coordinator_update(self) -> None:
node = self.coordinator.get_node_by_id(self.id)
if node:
value = node.get_value("switchLevel")
value_percentage = int(value * 100)
self._attr_is_on = value_percentage > 0
self._attr_brightness = int(value * 255)
self.async_write_ha_state()
async def async_turn_on(self, **kwargs) -> None:
"""Turn the device on or Light command."""
if ATTR_BRIGHTNESS in kwargs:
value = kwargs.get(ATTR_BRIGHTNESS, 255) / 255
else:
value = 1.0
await self._api_call(value)
async def async_turn_off(self, **kwargs) -> None:
"""Turn device off."""
await self._api_call(0.0)
async def _api_call(self, value: float):
await self.coordinator.api.node_call(self.id, "switchLevel", value)
class NexaSwitchEntity(NexaNodeEntity, SwitchEntity):
"""Entity for swtich"""
def __init__(self, coordinator: DataUpdateCoordinator, node: NexaNode):
_LOGGER.info("Found switch %s: %s", node.id, node.name)
super().__init__(node, coordinator)
self.id = node.id
self._attr_name = "Switch"
self._attr_unique_id = f"switch_{node.id}"
@callback
def _handle_coordinator_update(self) -> None:
node = self.coordinator.get_node_by_id(self.id)
if node:
self._attr_is_on = node.get_value("switchBinary")
self.async_write_ha_state()
async def async_turn_on(self, **kwargs) -> None:
"""Send turn on command"""
await self._api_call(True)
async def async_turn_off(self, **kwargs) -> None:
"""Send turn off command"""
await self._api_call(False)
async def _api_call(self, value: bool):
await self.coordinator.api.node_call(self.id, "switchBinary", value)
class NexaSensorEntity(NexaNodeEntity, SensorEntity):
"""Entity for sensor"""
def __init__(
self,
coordinator: DataUpdateCoordinator,
node: NexaNode,
key: str
):
_LOGGER.info("Found %s sensor %s: %s", key, node.id, node.name)
super().__init__(node, coordinator)
self.id = node.id
self.key = key
self._attr_native_value = None
self._attr_name = "Sensor"
self._attr_unique_id = f"sensor_{node.id}_{key}"
if key in SENSOR_MAP:
self._attr_name = f"{SENSOR_MAP[key]['name']} Sensor"
self._attr_native_unit_of_measurement = SENSOR_MAP[key]["unit"]
self._attr_device_class = SENSOR_MAP[key]["device"]
self._attr_state_class = SENSOR_MAP[key]["class"]
if key == "customEvent":
self._attr_name = f"Last {SENSOR_MAP[key]['name']}"
self._attr_options = node.custom_events
@callback
def _handle_coordinator_update(self) -> None:
node = self.coordinator.get_node_by_id(self.id)
if node:
value = node.get_value(self.key)
if self.key == "switchLevel":
self._attr_native_value = int(value * 100)
else:
self._attr_native_value = value
self.async_write_ha_state()
class NexaBinarySensorEntity(NexaNodeEntity, BinarySensorEntity):
"""Entity for binary sensor"""
def __init__(
self,
coordinator: DataUpdateCoordinator,
node: NexaNode,
key: str
):
_LOGGER.info("Found binary sensor %s: %s", node.id, node.name)
super().__init__(node, coordinator)
self.id = node.id
self.key = key
self._attr_is_on = None
self._attr_unique_id = f"binary_sensor_{node.id}_{key}"
if key in BINARY_MAP:
self._attr_name = f"{BINARY_MAP[key]['name']} Sensor"
else:
self._attr_name = "Binary Sensor"
@callback
def _handle_coordinator_update(self) -> None:
node = self.coordinator.get_node_by_id(self.id)
if node:
self._attr_is_on = node.get_value(self.key)
self.async_write_ha_state()
class NexaEnergyEntity(NexaEntity, SensorEntity):
"""Entity for global energy usage"""
def __init__(self, coordinator: DataUpdateCoordinator, attr: str):
_LOGGER.info("Found energy information: %s", attr)
super().__init__(coordinator)
self.id = attr
self._attr_native_value = None
self._attr_unique_id = f"nexa_energy_{attr}"
self._attr_name = ENERGY_MAP[attr]["name"]
self._attr_native_unit_of_measurement = ENERGY_MAP[attr]["unit"]
self._attr_device_class = ENERGY_MAP[attr]["device"]
self._attr_state_class = ENERGY_MAP[attr]["class"]
@callback
def _handle_coordinator_update(self) -> None:
self._attr_native_value = getattr(
self.coordinator.data.energy,
self.id
)
self.async_write_ha_state()
class NexaMediaPlayerEntity(NexaNodeEntity, MediaPlayerEntity):
"""Entity for media player"""
_attr_device_class = MediaPlayerDeviceClass.SPEAKER
_attr_media_content_type = MediaType.MUSIC
_attr_is_volume_muted: bool | None = None
_attr_state: MediaPlayerState | None = None
_attr_volume_level: float | None = None
_attr_supported_features: MediaPlayerEntityFeature = (
MediaPlayerEntityFeature.PAUSE
| MediaPlayerEntityFeature.VOLUME_SET
| MediaPlayerEntityFeature.VOLUME_MUTE
| MediaPlayerEntityFeature.PLAY
)
def __init__(
self,
coordinator: DataUpdateCoordinator,
node: NexaNode
):
_LOGGER.info("Found media player %s: %s", node.id, node.name)
super().__init__(node, coordinator)
self.id = node.id
self._attr_native_value = None
self._attr_unique_id = f"media_player_{node.id}"
self._attr_name = node.name or node.id
@callback
def _handle_coordinator_update(self) -> None:
node = self.coordinator.get_node_by_id(self.id)
if node:
if node.get_value("mediaPlayPause"):
self._attr_state = MediaPlayerState.PLAYING
else:
self._attr_state = MediaPlayerState.PAUSED
self._attr_volume_level = node.get_value("mediaVolume")
self._attr_is_volume_muted = node.get_value("mediaMute")
self.async_write_ha_state()
async def async_media_play(self) -> None:
"""Send play command to media player"""
await self._api_call("mediaPlayPause", True)
async def async_media_pause(self) -> None:
"""Send pause command to media player"""
await self._api_call("mediaPlayPause", False)
async def async_set_volume_level(self, volume: float) -> None:
"""Send volume level to media player"""
await self._api_call("mediaVolume", int(volume * 100))
async def async_mute_volume(self, mute: bool) -> None:
"""Send mute command to media player."""
await self._api_call("mediaMute", mute)
async def _api_call(self, cap: str, value: NexaNodeValueType):
await self.coordinator.api.node_call(self.id, cap, value) |
If you're up for a small quest: You can create a fork of this repository, edit the changes on your own account (you can use the built-in Github file editor/IDE) and then submit a pull request to this repository. Makes it much easier to review/test the changes. |
I'll give it a try tomorrow. It's a great way to learn GH |
And a nice bonus is that you will get credited for the changes because you made a physical commit to this repository instead of me copy/pasting stuff. So you'll get a mention in the changelogs etc. atuomatically :) You don't have to be credited if you don't want to of course (I can remove that in the pull request). |
Should'nt this depend on the choices of the device setup in Nexa: e.g. """Entity for light"""
if node:
if capability = node.get_switchBinary(TRUE)
Supported_color_modes = ColorMode.ONOFF, ColorMode.BRIGHTNESS
else:
Supported_color_modes = ColorMode.BRIGHTNESS Nexa has dimmers and not lights. So it can have properties of on/off and/or only level. Whilst Home assistant Light entity assumes its a light and minimum property is always ONOFF? https://developers.home-assistant.io/docs/core/entity/light/ But the code also declares """Must be the only supported mode"""? |
Not sure I understand what you mean by this. If your device reports as a switch, you'll get a switch in HA, if it reports as a light/dimmer you'll get that. Or both. |
I think I misunderstand the context between Nexas units and the information being sent, how HA's library works, and finally this module. Sorry for that. Fixed. |
I'm a little bit confused 😅 Open a new issue if there's some issue with your dimmers/lights so that this one does not go off-topic. |
... Silly me. I understand now after looking up the documentation myself. The fix for this error is simply to remove the Would you mind creating a PR for this as well ? |
Also related: #25 |
I saw this when a user reported issues in #34. These need to be changed ASAP
The text was updated successfully, but these errors were encountered: