diff --git a/custom_components/dirigera_platform/__init__.py b/custom_components/dirigera_platform/__init__.py index 6e54745..d5d38a0 100644 --- a/custom_components/dirigera_platform/__init__.py +++ b/custom_components/dirigera_platform/__init__.py @@ -76,7 +76,7 @@ async def async_setup_entry( hass.data.setdefault(DOMAIN, {}) hass_data = dict(entry.data) - logger.debug(f"hass_data : {hass_data}") + logger.error(f"hass_data : {hass_data}") # for backward compatibility hide_device_set_bulbs : bool = True @@ -111,11 +111,23 @@ async def async_setup_entry( hub_events = hub_event_listener(hub, hass) hub_events.start() - logger.debug("Complete async_setup_entry...") + # Lets get all devices, we do this hear so that the right sensor type can be associated + # Get Lights + from dirigera.devices.motion_sensor import MotionSensor + from dirigera.devices.open_close_sensor import OpenCloseSensor + from dirigera.devices.water_sensor import WaterSensor + from .core.motion_sensor import ikea_motion_sensor_device + + hub_motion_sensors : list[MotionSensor] = await hass.async_add_executor_job(hub.get_motion_sensors) + motion_sensor_devices : list[ikea_motion_sensor_device] = [ikea_motion_sensor_device(hass, hub, m) for m in hub_motion_sensors] + logger.error("Found {} motion_sensor entities to setup...".format(len(motion_sensor_devices))) + + logger.error("Complete async_setup_entry...") return True + async def options_update_listener( hass: core.HomeAssistant, config_entry: config_entries.ConfigEntry ): @@ -139,15 +151,15 @@ async def async_unload_entry( hub = Hub(hass_data[CONF_TOKEN], hass_data[CONF_IP_ADDRESS]) # For each controller if there is an empty scene delete it - logger.error("In unload so forcing delete of scenes...") - scenes: list[DirigeraScene] = await hass.async_add_executor_job(hub.get_scenes) - for scene in scenes: - if scene.info.name is None or not scene.info.name.startswith("dirigera_platform_empty_scene_"): - logger.error(f"Ignoring scene : {scene.info.name}, as not empty scene") - continue - logger.error(f"Deleting scene {scene.id}...") - await hass.async_add_executor_job(hass.delete_scene,scene.id) - logger.error("Done deleting scene....") + #logger.error("In unload so forcing delete of scenes...") + #scenes: list[DirigeraScene] = await hass.async_add_executor_job(hub.get_scenes) + #for scene in scenes: + # if scene.info.name is None or not scene.info.name.startswith("dirigera_platform_empty_scene_"): + # logger.error(f"Ignoring scene : {scene.info.name}, as not empty scene") + # continue + # logger.error(f"Deleting scene {scene.id}...") + # await hass.async_add_executor_job(hass.delete_scene,scene.id) + #logger.error("Done deleting scene....") """Unload a config entry.""" unload_ok = all( diff --git a/custom_components/dirigera_platform/base_classes.py b/custom_components/dirigera_platform/base_classes.py index 20ce528..dc927fc 100644 --- a/custom_components/dirigera_platform/base_classes.py +++ b/custom_components/dirigera_platform/base_classes.py @@ -2,6 +2,8 @@ from homeassistant.components.datetime import DateTimeEntity from homeassistant.helpers.entity import DeviceInfo, Entity, EntityCategory from homeassistant.core import HomeAssistantError +from homeassistant.components.number import NumberEntity, NumberDeviceClass + from .hub_event_listener import hub_event_listener, registry_entry from .const import DOMAIN @@ -158,7 +160,7 @@ def __init__(self, device): uom="A", icon="mdi:current-ac", device_class=SensorDeviceClass.CURRENT) - + @property def native_value(self): return getattr(self._device, "current_amps") diff --git a/custom_components/dirigera_platform/binary_sensor.py b/custom_components/dirigera_platform/binary_sensor.py index ed0a421..98885ae 100644 --- a/custom_components/dirigera_platform/binary_sensor.py +++ b/custom_components/dirigera_platform/binary_sensor.py @@ -12,7 +12,6 @@ from .const import DOMAIN from .mocks.ikea_motion_sensor_mock import ikea_motion_sensor_mock from .mocks.ikea_open_close_mock import ikea_open_close_mock -from .base_classes import battery_percentage_sensor from .base_classes import ikea_base_device, ikea_base_device_sensor logger = logging.getLogger("custom_components.dirigera_platform") @@ -30,8 +29,6 @@ async def async_setup_entry( # hub = dirigera.Hub(config[CONF_TOKEN], config[CONF_IP_ADDRESS]) hub = Hub(config[CONF_TOKEN], config[CONF_IP_ADDRESS]) - lights = [] - # If mock then start with mocks if config[CONF_IP_ADDRESS] == "mock": logger.warning("Setting up mock motion sensors") @@ -50,7 +47,6 @@ async def async_setup_entry( motion_sensors = [] for device in motion_sensor_devices: motion_sensors.append(ikea_motion_sensor(device)) - motion_sensors.append(battery_percentage_sensor(device)) logger.debug("Found {} motion_sensor entities to setup...".format(len(motion_sensors)/2)) async_add_entities(motion_sensors) @@ -64,7 +60,6 @@ async def async_setup_entry( open_close_sensors = [] for device in open_close_devices: open_close_sensors.append(ikea_open_close(device)) - open_close_sensors.append(battery_percentage_sensor(device)) logger.debug("Found {} open close entities to setup...".format(len(open_close_sensors)/2)) async_add_entities(open_close_sensors) @@ -76,7 +71,6 @@ async def async_setup_entry( water_sensors = [] for device in water_sensor_devices: - water_sensors.append(battery_percentage_sensor(device)) water_sensors.append(ikea_water_sensor(device)) logger.debug(f"Found {len(hub_water_sensors)/2} water sensors to setup....") diff --git a/custom_components/dirigera_platform/cover.py b/custom_components/dirigera_platform/cover.py index b938872..e69a42c 100644 --- a/custom_components/dirigera_platform/cover.py +++ b/custom_components/dirigera_platform/cover.py @@ -13,7 +13,7 @@ from .const import DOMAIN from .mocks.ikea_blinds_mock import ikea_blinds_mock -from .base_classes import ikea_base_device, ikea_base_device_sensor, battery_percentage_sensor +from .base_classes import ikea_base_device, ikea_base_device_sensor logger = logging.getLogger("custom_components.dirigera_platform") @@ -29,7 +29,6 @@ async def async_setup_entry( hub = Hub(config[CONF_TOKEN], config[CONF_IP_ADDRESS]) blinds = [] - battery_sensors = [] # If mock then start with mocks if config[CONF_IP_ADDRESS] == "mock": @@ -41,12 +40,9 @@ async def async_setup_entry( devices = [IkeaBlindsDevice(hass, hub, b) for b in hub_blinds] for device in devices: blinds.append(IkeaBlinds(device)) - if getattr(device,"battery_percentage",None) is not None: - battery_sensors.append(battery_percentage_sensor(device)) - + logger.debug("Found {} blinds entities to setup...".format(len(blinds))) async_add_entities(blinds) - async_add_entities(battery_sensors) logger.debug("BLINDS Complete async_setup_entry") class IkeaBlindsDevice(ikea_base_device): diff --git a/custom_components/dirigera_platform/sensor.py b/custom_components/dirigera_platform/sensor.py index efe6e78..b2b6a61 100644 --- a/custom_components/dirigera_platform/sensor.py +++ b/custom_components/dirigera_platform/sensor.py @@ -7,6 +7,15 @@ from dirigera.devices.environment_sensor import EnvironmentSensor from dirigera.devices.controller import Controller from dirigera.devices.scene import Info, Icon +from dirigera.devices.outlet import Outlet +from .base_classes import ikea_base_device, battery_percentage_sensor,ikea_base_device_sensor, current_amps_sensor , current_active_power_sensor, current_voltage_sensor, total_energy_consumed_sensor, energy_consumed_at_last_reset_sensor , total_energy_consumed_last_updated_sensor, total_energy_consumed_sensor, time_of_last_energy_reset_sensor +from .switch import ikea_outlet, ikea_outlet_switch_sensor +from dirigera.devices.motion_sensor import MotionSensor +from dirigera.devices.open_close_sensor import OpenCloseSensor +from dirigera.devices.water_sensor import WaterSensor +from .binary_sensor import ikea_motion_sensor, ikea_motion_sensor_device, ikea_open_close_device, ikea_open_close, ikea_water_sensor_device, ikea_water_sensor +from dirigera.devices.blinds import Blind +from .cover import IkeaBlindsDevice, IkeaBlinds from homeassistant.helpers.entity import Entity from homeassistant import config_entries, core @@ -14,7 +23,7 @@ from homeassistant.const import CONF_IP_ADDRESS, CONF_TOKEN from homeassistant.core import HomeAssistantError from homeassistant.helpers.entity import EntityCategory - + from .const import DOMAIN from .base_classes import ikea_base_device, ikea_base_device_sensor logger = logging.getLogger("custom_components.dirigera_platform") @@ -24,11 +33,11 @@ async def async_setup_entry( config_entry: config_entries.ConfigEntry, async_add_entities, ): - logger.debug("EnvSensor & Controllers Starting async_setup_entry") + logger.debug("sensor Starting async_setup_entry") """Setup sensors from a config entry created in the integrations UI.""" - logger.error("Staring async_setup_entry in SENSOR...") - logger.error(dict(config_entry.data)) - logger.error(f"async_setup_entry SENSOR {config_entry.unique_id} {config_entry.state} {config_entry.entry_id} {config_entry.title} {config_entry.domain}") + logger.debug("Staring async_setup_entry in SENSOR...") + logger.debug(dict(config_entry.data)) + logger.debug(f"async_setup_entry SENSOR {config_entry.unique_id} {config_entry.state} {config_entry.entry_id} {config_entry.title} {config_entry.domain}") config = hass.data[DOMAIN][config_entry.entry_id] logger.debug(config) @@ -62,7 +71,7 @@ async def async_setup_entry( ] hub_controllers = await hass.async_add_executor_job(hub.get_controllers) - logger.error(f"Got {len(hub_controllers)} controllers...") + logger.debug(f"Got {len(hub_controllers)} controllers...") # Controllers with more one button are returned as spearate controllers # their uniqueid has _1, _2 suffixes. Only the primary controller has @@ -101,7 +110,57 @@ async def async_setup_entry( async_add_entities(env_sensors) async_add_entities(controller_devices) - logger.debug("EnvSensor & Controllers Complete async_setup_entry") + # Add sensors for the outlets + hub_outlets : list[Outlet] = await hass.async_add_executor_job(hub.get_outlets) + extra_entities = [] + + extra_attrs=["current_amps","current_active_power","current_voltage","total_energy_consumed","energy_consumed_at_last_reset","time_of_last_energy_reset","total_energy_consumed_last_updated"] + # Some outlets like INSPELNING Smart plug have ability to report power, so add those as well + logger.debug("Looking for extra attributes of power/current/voltage in outlet....") + for hub_outlet in hub_outlets: + outlet = ikea_outlet(hass, hub, hub_outlet) + for attr in extra_attrs: + if getattr(hub_outlet.attributes,attr) is not None: + extra_entities.append(eval(f"{attr}_sensor(outlet)")) + + logger.debug(f"Found {len(extra_entities)}, power attribute sensors for outlets") + async_add_entities(extra_entities) + + # Add battery sensors + battery_sensors = [] + + hub_motion_sensors : list[MotionSensor] = await hass.async_add_executor_job(hub.get_motion_sensors) + motion_sensor_devices : list[ikea_motion_sensor_device] = [ikea_motion_sensor_device(hass, hub, m) for m in hub_motion_sensors] + + for device in motion_sensor_devices: + battery_sensors.append(battery_percentage_sensor(device)) + + hub_open_close_sensors : list[OpenCloseSensor] = await hass.async_add_executor_job(hub.get_open_close_sensors) + open_close_devices : list[ikea_open_close_device] = [ + ikea_open_close_device(hass, hub, open_close_sensor) + for open_close_sensor in hub_open_close_sensors + ] + + for device in open_close_devices: + battery_sensors.append(battery_percentage_sensor(device)) + + hub_water_sensors : list[WaterSensor] = await hass.async_add_executor_job(hub.get_water_sensors) + water_sensor_devices = [ ikea_water_sensor_device(hass, hub, hub_water_sensor) + for hub_water_sensor in hub_water_sensors + ] + + for device in water_sensor_devices: + battery_sensors.append(battery_percentage_sensor(device)) + + hub_blinds = await hass.async_add_executor_job(hub.get_blinds) + devices = [IkeaBlindsDevice(hass, hub, b) for b in hub_blinds] + for device in devices: + if getattr(device,"battery_percentage",None) is not None: + battery_sensors.append(battery_percentage_sensor(device)) + + logger.debug(f"Found {len(battery_sensors)} battery sensors...") + async_add_entities(battery_sensors) + logger.debug("sensor Complete async_setup_entry") class ikea_vindstyrka_device(ikea_base_device): def __init__(self, hass:core.HomeAssistant, hub:Hub , json_data:EnvironmentSensor) -> None: @@ -123,7 +182,7 @@ async def async_update(self): class ikea_vindstyrka_temperature(ikea_base_device_sensor, SensorEntity): def __init__(self, device: ikea_vindstyrka_device) -> None: - super().__init__(device, id_suffix="TEMP", name_suffix="Temperature") + super().__init__(device, id_suffix="TEMP", name="Temperature") logger.debug("ikea_vindstyrka_temperature ctor...") @property @@ -145,7 +204,7 @@ def state_class(self) -> str: class ikea_vindstyrka_humidity(ikea_base_device_sensor, SensorEntity): def __init__(self, device: ikea_vindstyrka_device) -> None: logger.debug("ikea_vindstyrka_humidity ctor...") - super().__init__(device, id_suffix="HUM", name_suffix="Humidity") + super().__init__(device, id_suffix="HUM", name="Humidity") @property def device_class(self): @@ -174,15 +233,15 @@ def __init__( name_suffix = " " if self._pm25_type == WhichPM25.CURRENT: id_suffix = "CURPM25" - name_suffix = "Current PM2.5" + name = "Current PM2.5" if self._pm25_type == WhichPM25.MAX: id_suffix = "MAXPM25" - name_suffix = "Max Measured PM2.5" + name = "Max Measured PM2.5" if self._pm25_type == WhichPM25.MIN: id_suffix = "MINPM25" - name_suffix = "Min Measured PM2.5" + name = "Min Measured PM2.5" - super().__init__(device, id_suffix=id_suffix, name_suffix=name_suffix) + super().__init__(device, id_suffix=id_suffix, name=name_suffix) @property def device_class(self): @@ -206,7 +265,7 @@ def native_unit_of_measurement(self) -> str: class ikea_vindstyrka_voc_index(ikea_base_device_sensor, SensorEntity): def __init__(self, device: ikea_vindstyrka_device) -> None: logger.debug("ikea_vindstyrka_voc_index ctor...") - super().__init__(device, id_suffix="VOC", name_suffix="VOC Index") + super().__init__(device, id_suffix="VOC", name="VOC Index") @property def device_class(self): diff --git a/custom_components/dirigera_platform/switch.py b/custom_components/dirigera_platform/switch.py index ba996f0..fd32187 100644 --- a/custom_components/dirigera_platform/switch.py +++ b/custom_components/dirigera_platform/switch.py @@ -11,7 +11,7 @@ from .const import DOMAIN from .mocks.ikea_outlet_mock import ikea_outlet_mock -from .base_classes import ikea_base_device, current_amps_sensor , current_active_power_sensor, current_voltage_sensor, total_energy_consumed_sensor, energy_consumed_at_last_reset_sensor , total_energy_consumed_last_updated_sensor, total_energy_consumed_sensor, time_of_last_energy_reset_sensor +from .base_classes import ikea_base_device, ikea_base_device_sensor, current_amps_sensor , current_active_power_sensor, current_voltage_sensor, total_energy_consumed_sensor, energy_consumed_at_last_reset_sensor , total_energy_consumed_last_updated_sensor, total_energy_consumed_sensor, time_of_last_energy_reset_sensor logger = logging.getLogger("custom_components.dirigera_platform") @@ -42,10 +42,11 @@ async def async_setup_entry( logger.debug("Looking for extra attributes of power/current/voltage in outlet....") for hub_outlet in hub_outlets: outlet = ikea_outlet(hass, hub, hub_outlet) - outlets.append(outlet) - for attr in extra_attrs: - if getattr(hub_outlet.attributes,attr) is not None: - extra_entities.append(eval(f"{attr}_sensor(outlet)")) + switch_sensor = ikea_outlet_switch_sensor(outlet) + outlets.append(switch_sensor) + #for attr in extra_attrs: + #if getattr(hub_outlet.attributes,attr) is not None: + #extra_entities.append(eval(f"{attr}_sensor(outlet)")) logger.debug("Found {} outlet entities to setup...".format(len(outlets))) async_add_entities(outlets) @@ -55,14 +56,14 @@ async def async_setup_entry( logger.debug("SWITCH Complete async_setup_entry") -class ikea_outlet(ikea_base_device, SwitchEntity): +class ikea_outlet(ikea_base_device): def __init__(self, hass, hub, json_data): super().__init__(hass, hub, json_data, hub.get_outlet_by_id) async def async_turn_on(self): logger.debug("outlet turn_on") try: - await self.hass.async_add_executor_job(self._json_data.set_on, True) + await self._hass.async_add_executor_job(self._json_data.set_on, True) except Exception as ex: logger.error("error encountered turning on : {}".format(self.name)) logger.error(ex) @@ -71,8 +72,20 @@ async def async_turn_on(self): async def async_turn_off(self): logger.debug("outlet turn_off") try: - await self.hass.async_add_executor_job(self._json_data.set_on, False) + await self._hass.async_add_executor_job(self._json_data.set_on, False) except Exception as ex: logger.error("error encountered turning off : {}".format(self.name)) logger.error(ex) - raise HomeAssistantError(ex, DOMAIN, "hub_exception") \ No newline at end of file + raise HomeAssistantError(ex, DOMAIN, "hub_exception") + +class ikea_outlet_switch_sensor(ikea_base_device_sensor, SwitchEntity): + def __init__(self, device): + super().__init__(device = device, name = device.name ) + + async def async_turn_on(self): + logger.debug("sensor: outlet turn_on") + await self._device.async_turn_on() + + async def async_turn_off(self): + logger.debug("sensor: outlet turn_off") + await self._device.async_turn_off() \ No newline at end of file