-
Notifications
You must be signed in to change notification settings - Fork 38
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
"does not generate unique ID" errors #111
Comments
Thanks for the mention. I changed the sensor.py to: from __future__ import annotations
from .const import DOMAIN
import logging
import datetime
_LOGGER: Final = logging.getLogger(__name__)
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorStateClass,
)
from homeassistant.const import UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
async def async_setup_entry(hass, config_entry, async_add_entities):
hub = hass.data[DOMAIN][config_entry.entry_id]
new_devices = []
new_devices.append(IPSensor(hub))
new_devices.append(SystimeSensor(hub))
new_devices.append(HeapSensor(hub))
new_devices.append(RecordcountSensor(hub))
new_devices.append(DBsizeSensor(hub))
new_devices.append(LitefsfreeSensor(hub))
new_devices.append(APWifiRssiSensor(hub))
new_devices.append(APStateSensor(hub))
new_devices.append(APRunStateSensor(hub))
new_devices.append(APWifiStatusSensor(hub))
new_devices.append(APWifiSssidSensor(hub))
for esls in hub.esls:
new_devices.append(LastSeenSensor(esls,hub))
new_devices.append(NextUpdateSensor(esls,hub))
new_devices.append(NextCheckinSensor(esls,hub))
new_devices.append(PendingSensor(esls,hub))
new_devices.append(WakeupReasonSensor(esls,hub))
new_devices.append(CapabilitiesSensor(esls,hub))
if (hub.data[esls]["lqi"] != 100 or hub.data[esls]["rssi"] != 100) and hub.data[esls]["hwtype"] != 224 and hub.data[esls]["hwtype"] != 240:
new_devices.append(TempSensor(esls,hub))
new_devices.append(RssiSensor(esls,hub))
new_devices.append(BatteryVoltageSensor(esls,hub))
new_devices.append(BatteryPercentageSensor(esls,hub))
new_devices.append(LqiSensor(esls,hub))
async_add_entities(new_devices)
class IPSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_ip_{hub.data['ap']['ip']}"
self._attr_name = "AP IP"
self._hub = hub
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, "ap")},
"configuration_url": "http://" + self._hub.data["ap"]["ip"],
"name": "OpenEpaperLink AP",
"model": "esp32",
"manufacturer": "OpenEpaperLink",
}
def update(self) -> None:
self._attr_native_value = self._hub.data["ap"]["ip"]
class APWifiRssiSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_wifirssi_{hub.data['ap']['ip']}"
self._attr_name = "AP Wifi RSSI"
self._hub = hub
self._attr_native_unit_of_measurement = "dB"
self._attr_device_class = SensorDeviceClass.SIGNAL_STRENGTH
self._attr_state_class = SensorStateClass.MEASUREMENT
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, "ap")}
}
def update(self) -> None:
self._attr_native_value = self._hub.data["ap"]["rssi"]
class APStateSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_state_{hub.data['ap']['ip']}"
self._attr_name = "AP State"
self._hub = hub
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, "ap")}
}
def update(self) -> None:
lut = {0: "offline",1: "online",2: "flashing",3: "wait for reset",4: "requires power cycle",5: "failed",6: "coming online"}
self._attr_native_value = lut[self._hub.data["ap"]["apstate"]]
class APRunStateSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_runstate_{hub.data['ap']['ip']}"
self._attr_name = "AP Run State"
self._hub = hub
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, "ap")}
}
def update(self) -> None:
lut = {0: "stopped",1: "pause",2: "running",3: "init"}
self._attr_native_value = lut[self._hub.data["ap"]["runstate"]]
class APTempSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_temp_{hub.data['ap']['ip']}"
self._attr_name = "AP Temp"
self._hub = hub
self._attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS
self._attr_device_class = SensorDeviceClass.TEMPERATURE
self._attr_state_class = SensorStateClass.MEASUREMENT
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, "ap")}
}
def update(self) -> None:
temp = self._hub.data["ap"]["temp"]
if temp:
self._attr_native_value = round(temp,1)
class APWifiStatusSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_wifistate_{hub.data['ap']['ip']}"
self._attr_name = "AP Wifi State"
self._hub = hub
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, "ap")}
}
def update(self) -> None:
lut = {3: "connected"}
self._attr_native_value = lut[self._hub.data["ap"]["wifistatus"]]
class APWifiSssidSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_wifissid_{hub.data['ap']['ip']}"
self._attr_name = "AP Wifi SSID"
self._hub = hub
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, "ap")}
}
def update(self) -> None:
self._attr_native_value = self._hub.data["ap"]["wifissid"]
class SystimeSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_systime_{hub.data['ap']['ip']}"
self._attr_name = "AP Systime"
self._hub = hub
self._attr_device_class = SensorDeviceClass.TIMESTAMP
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, "ap")}
}
def update(self) -> None:
self._attr_native_value = datetime.datetime.fromtimestamp(self._hub.data["ap"]["systime"], datetime.timezone.utc)
class HeapSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_heap_{hub.data['ap']['ip']}"
self._attr_name = "AP free Heap"
self._hub = hub
self._attr_native_unit_of_measurement = "kB"
self._attr_device_class = SensorDeviceClass.DATA_SIZE
self._attr_state_class = SensorStateClass.MEASUREMENT
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, "ap")}
}
def update(self) -> None:
self._attr_native_value = round(int(self._hub.data["ap"]["heap"]) / 1024,1)
class RecordcountSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_recordcount_{hub.data['ap']['ip']}"
self._attr_name = "AP Recordcount"
self._hub = hub
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, "ap")}
}
def update(self) -> None:
self._attr_native_value = self._hub.data["ap"]["recordcount"]
class DBsizeSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_dbsize_{hub.data['ap']['ip']}"
self._attr_name = "AP DBSize"
self._hub = hub
self._attr_native_unit_of_measurement = "kB"
self._attr_device_class = SensorDeviceClass.DATA_SIZE
self._attr_state_class = SensorStateClass.MEASUREMENT
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, "ap")}
}
def update(self) -> None:
self._attr_native_value = round(int(self._hub.data["ap"]["dbsize"]) / 1024,1)
class LitefsfreeSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_littlefsfree_{hub.data['ap']['ip']}"
self._attr_name = "AP Free Space"
self._hub = hub
self._attr_native_unit_of_measurement = "kB"
self._attr_device_class = SensorDeviceClass.DATA_SIZE
self._attr_state_class = SensorStateClass.MEASUREMENT
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, "ap")}
}
def update(self) -> None:
self._attr_native_value = round(int(self._hub.data["ap"]["littlefsfree"]) / 1024,1)
class TempSensor(SensorEntity):
def __init__(self, esls,hub):
self._attr_unique_id = f"{esls}_temp"
self._eslid = esls
self._attr_name = hub.data[esls]["tagname"] + " Temperature"
self._hub = hub
self._attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS
self._attr_device_class = SensorDeviceClass.TEMPERATURE
self._attr_state_class = SensorStateClass.MEASUREMENT
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, self._eslid)},
}
def update(self) -> None:
eslid = self._eslid
self._attr_native_value = self._hub.data[eslid]["temperature"]
class RssiSensor(SensorEntity):
def __init__(self, esls,hub):
self._attr_unique_id = f"{esls}_rssi"
self._eslid = esls
self._attr_name = hub.data[esls]["tagname"] + " Rssi"
self._hub = hub
self._attr_native_unit_of_measurement = "dB"
self._attr_device_class = SensorDeviceClass.SIGNAL_STRENGTH
self._attr_state_class = SensorStateClass.MEASUREMENT
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, self._eslid)},
}
def update(self) -> None:
eslid = self._eslid
self._attr_native_value = self._hub.data[eslid]["rssi"]
class BatteryVoltageSensor(SensorEntity):
def __init__(self, esls,hub):
self._attr_unique_id = f"{esls}_batteryvoltage"
self._eslid = esls
self._attr_name = hub.data[esls]["tagname"] + " Battery Voltage"
self._hub = hub
self._attr_native_unit_of_measurement = "V"
self._attr_device_class = SensorDeviceClass.VOLTAGE
self._attr_state_class = SensorStateClass.MEASUREMENT
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, self._eslid)},
}
def update(self) -> None:
eslid = self._eslid
self._attr_native_value = self._hub.data[eslid]["battery"] / 1000
class LqiSensor(SensorEntity):
def __init__(self, esls,hub):
self._attr_unique_id = f"{esls}_lqi"
self._eslid = esls
self._attr_name = hub.data[esls]["tagname"] + " Link Quality Index"
self._hub = hub
self._attr_native_unit_of_measurement = ""
self._attr_state_class = SensorStateClass.MEASUREMENT
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, self._eslid)},
}
def update(self) -> None:
eslid = self._eslid
self._attr_native_value = self._hub.data[eslid]["lqi"]
class ContentModeSensor(SensorEntity):
def __init__(self, esls,hub):
self._attr_unique_id = f"{esls}_contentmode"
self._eslid = esls
self._attr_name = hub.data[esls]["tagname"] + " Content Mode"
self._hub = hub
self._attr_native_unit_of_measurement = ""
self._attr_state_class = SensorStateClass.MEASUREMENT
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, self._eslid)},
}
def update(self) -> None:
eslid = self._eslid
self._attr_native_value = self._hub.data[eslid]["contentmode"]
class LastSeenSensor(SensorEntity):
def __init__(self, esls,hub):
self._attr_unique_id = f"{esls}_lastseen"
self._eslid = esls
self._attr_name = hub.data[esls]["tagname"] + " Last Seen"
self._hub = hub
self._attr_device_class = SensorDeviceClass.TIMESTAMP
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, self._eslid)},
"name": self._hub.data[self._eslid]["tagname"],
"sw_version": hex(self._hub.data[self._eslid]["ver"]),
"serial_number": self._eslid,
"model": self._hub.data[self._eslid]["hwstring"],
"manufacturer": "OpenEpaperLink",
"via_device": (DOMAIN, "ap")
}
def update(self) -> None:
eslid = self._eslid
self._attr_native_value = datetime.datetime.fromtimestamp(self._hub.data[eslid]["lastseen"],datetime.timezone.utc)
class NextUpdateSensor(SensorEntity):
def __init__(self, esls,hub):
self._attr_unique_id = f"{esls}_nextupdate"
self._eslid = esls
self._attr_name = hub.data[esls]["tagname"] + " Next Update"
self._hub = hub
self._attr_device_class = SensorDeviceClass.TIMESTAMP
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, self._eslid)},
}
def update(self) -> None:
eslid = self._eslid
self._attr_native_value = datetime.datetime.fromtimestamp(self._hub.data[eslid]["nextupdate"],datetime.timezone.utc)
class NextCheckinSensor(SensorEntity):
def __init__(self, esls,hub):
self._attr_unique_id = f"{esls}_nextcheckin"
self._eslid = esls
self._attr_name = hub.data[esls]["tagname"] + " Next Checkin"
self._hub = hub
self._attr_device_class = SensorDeviceClass.TIMESTAMP
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, self._eslid)},
}
def update(self) -> None:
eslid = self._eslid
self._attr_native_value = datetime.datetime.fromtimestamp(self._hub.data[eslid]["nextcheckin"],datetime.timezone.utc)
class PendingSensor(SensorEntity):
def __init__(self, esls,hub):
self._attr_unique_id = f"{esls}_pending"
self._eslid = esls
self._attr_name = hub.data[esls]["tagname"] + " Pending Transfer"
self._hub = hub
self._attr_native_unit_of_measurement = ""
self._attr_state_class = SensorStateClass.MEASUREMENT
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, self._eslid)},
}
def update(self) -> None:
eslid = self._eslid
self._attr_native_value = self._hub.data[eslid]["pending"]
class WakeupReasonSensor(SensorEntity):
def __init__(self, esls,hub):
self._attr_unique_id = f"{esls}_wakeupReason"
self._eslid = esls
self._attr_name = hub.data[esls]["tagname"] + " Wakeup Reason"
self._hub = hub
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, self._eslid)},
}
def update(self) -> None:
eslid = self._eslid
lut = {0: "TIMED",1: "BOOT",2: "GPIO",3: "NFC",4: "BUTTON1",5: "BUTTON2",252: "FIRSTBOOT",253: "NETWORK_SCAN",254: "WDT_RESET"}
wr = lut[self._hub.data[eslid]["wakeupReason"]]
self._attr_native_value = wr
class CapabilitiesSensor(SensorEntity):
def __init__(self, esls,hub):
self._attr_unique_id = f"{esls}_capabilities"
self._eslid = esls
self._attr_name = hub.data[esls]["tagname"] + " Capabilities"
self._hub = hub
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, self._eslid)},
}
def update(self) -> None:
eslid = self._eslid
self._attr_native_value = self._hub.data[eslid]["capabilities"]
class BatteryPercentageSensor(SensorEntity):
def __init__(self, esls,hub):
self._attr_unique_id = f"{esls}_battery"
self._eslid = esls
self._attr_name = hub.data[esls]["tagname"] + " Battery"
self._hub = hub
self._attr_native_unit_of_measurement = "%"
self._attr_device_class = SensorDeviceClass.BATTERY
self._attr_state_class = SensorStateClass.MEASUREMENT
@property
def device_info(self) -> DeviceInfo:
return {
"identifiers": {(DOMAIN, self._eslid)},
}
def update(self) -> None:
eslid = self._eslid
bperc = ((self._hub.data[eslid]["battery"] / 1000) - 2.20) * 250
if bperc > 100:
bperc = 100
if bperc < 0:
bperc = 0
bperc = int(bperc)
self._attr_native_value = bperc In case that was edited/changed in the meantime, these are the unique_ids needed for each function: class IPSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_ip_{hub.data['ap']['ip']}"
class APWifiRssiSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_wifirssi_{hub.data['ap']['ip']}"
class APStateSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_state_{hub.data['ap']['ip']}"
class APRunStateSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_runstate_{hub.data['ap']['ip']}"
class APTempSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_temp_{hub.data['ap']['ip']}"
class APWifiStatusSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_wifistate_{hub.data['ap']['ip']}"
class APWifiSssidSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_wifissid_{hub.data['ap']['ip']}"
class SystimeSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_systime_{hub.data['ap']['ip']}"
class HeapSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_heap_{hub.data['ap']['ip']}"
class RecordcountSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_recordcount_{hub.data['ap']['ip']}"
class DBsizeSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_dbsize_{hub.data['ap']['ip']}"
class LitefsfreeSensor(SensorEntity):
def __init__(self, hub):
self._attr_unique_id = f"ap_littlefsfree_{hub.data['ap']['ip']}"
# For device-specific sensors, it seems you're already using a dynamic identifier (esls).
# Ensure that 'esls' itself provides uniqueness across devices. No change needed if 'esls' is unique. I have had this running for over 1.5 weeks, and no additional issues. The unique_id error is gone. |
@DrBlokmeister Can you open a PR for this? |
That might take some time. I'm not familiar enough with Github to quickly create a PR. Moreover, I do see that after updating and adding these unique_id lines again, I again get errors that this component is not generating unique ids. So something else might be going on. |
Yeah, I'm not convinced that's the right fix. It almost seems like it's reloading or something. But actually, I stopped seeing this error in the logs now on startup. |
I tried a lot but can not reproduce the error. I know it happened a longer time ago. Does anyone else still get this error? |
Yes, the error is still here. In logs
My setup:
|
Same for me, e.g.
|
I think @DrBlokmeister has the right general approach, but ideally we would take this a step further and use the MAC address of the AP rather than the IP address. As far as I know, the OEPL web server doesn't implement any mechanism to retrieve the MAC address, but I think that is something that could be solved for easily enough. For those that are still seeing this issue -- how long have you been running this integration? I'm wondering if the way these sensors were handled in earlier versions is causing the conflict. EDIT: I think I managed to reproduce this in my development environment by deleting an AP, then attempting to re-add the same AP with the same IP address:
|
I think the MAC address is already used instead of the IP. |
Is it possible that the inability to delete Tags and their entities is also related to this? |
As far as I understand, the ability to delete devices/entities in Home Assistant requires a remove service to be defined in the integration. So I don't think that this is related. However I'm not an expert on this subject. |
On startup, I get lots of these errors in the log:
In #109 , @DrBlokmeister had some ideas for how to fix this.
The text was updated successfully, but these errors were encountered: