diff --git a/custom_components/browser_mod/__init__.py b/custom_components/browser_mod/__init__.py index a3440d6..1423fa2 100644 --- a/custom_components/browser_mod/__init__.py +++ b/custom_components/browser_mod/__init__.py @@ -1,5 +1,7 @@ import logging +from homeassistant import config_entries + from .mod_view import setup_view from .connection import setup_connection from .service import setup_service @@ -13,11 +15,32 @@ from .const import ( DATA_SETUP_COMPLETE, ) +COMPONENTS = [ + "media_player", + "sensor", + "binary_sensor", + "light", + "camera", +] + _LOGGER = logging.getLogger(__name__) async def async_setup(hass, config): + _LOGGER.error(hass.config_entries) + + if not hass.config_entries.async_entries(DOMAIN): + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": config_entries.SOURCE_IMPORT + }, + data={} + ) + ) + aliases = {} for d in config[DOMAIN].get(CONFIG_DEVICES, {}): name = config[DOMAIN][CONFIG_DEVICES][d].get("name", None) @@ -35,12 +58,15 @@ async def async_setup(hass, config): await setup_connection(hass, config) setup_view(hass) - async_load_platform = hass.helpers.discovery.async_load_platform - await async_load_platform("media_player", DOMAIN, {}, config) - await async_load_platform("sensor", DOMAIN, {}, config) - await async_load_platform("binary_sensor", DOMAIN, {}, config) - await async_load_platform("light", DOMAIN, {}, config) - await async_load_platform("camera", DOMAIN, {}, config) + for component in COMPONENTS: + hass.async_create_task( + hass.helpers.discovery.async_load_platform( + component, + DOMAIN, + {}, + config + ) + ) await setup_service(hass) @@ -50,3 +76,14 @@ async def async_setup(hass, config): device.trigger_update() return True + + +async def async_setup_entry(hass, config_entry): + for component in COMPONENTS: + hass.async_create_task( + hass.config_entries.async_forward_entry_setup( + config_entry, + component + ) + ) + return True diff --git a/custom_components/browser_mod/binary_sensor.py b/custom_components/browser_mod/binary_sensor.py index 5240356..fb0c919 100644 --- a/custom_components/browser_mod/binary_sensor.py +++ b/custom_components/browser_mod/binary_sensor.py @@ -11,6 +11,11 @@ PLATFORM = 'binary_sensor' async def async_setup_platform(hass, config, async_add_devices, discovery_info=None): return setup_platform(hass, config, async_add_devices, PLATFORM, BrowserModSensor) + +async def async_setup_entry(hass, config_entry, async_add_entities): + await async_setup_platform(hass, {}, async_add_entities) + + class BrowserModSensor(BrowserModEntity): domain = PLATFORM diff --git a/custom_components/browser_mod/camera.py b/custom_components/browser_mod/camera.py index 59baad4..f3da81a 100644 --- a/custom_components/browser_mod/camera.py +++ b/custom_components/browser_mod/camera.py @@ -12,6 +12,11 @@ PLATFORM = 'camera' async def async_setup_platform(hass, config, async_add_devices, discovery_info=None): return setup_platform(hass, config, async_add_devices, PLATFORM, BrowserModCamera) + +async def async_setup_entry(hass, config_entry, async_add_entities): + await async_setup_platform(hass, {}, async_add_entities) + + class BrowserModCamera(Camera, BrowserModEntity): domain = PLATFORM diff --git a/custom_components/browser_mod/config_flow.py b/custom_components/browser_mod/config_flow.py new file mode 100644 index 0000000..1c4e08a --- /dev/null +++ b/custom_components/browser_mod/config_flow.py @@ -0,0 +1,10 @@ +from homeassistant import config_entries + +from .const import DOMAIN + +class BrowserModConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + + VERSION = 1 + + async def async_step_import(self, import_info): + return self.async_create_entry(title="Browser Mod", data={}) diff --git a/custom_components/browser_mod/helpers.py b/custom_components/browser_mod/helpers.py index b65b5fb..15186a3 100644 --- a/custom_components/browser_mod/helpers.py +++ b/custom_components/browser_mod/helpers.py @@ -50,6 +50,9 @@ def create_entity(hass, platform, deviceID, connection): def setup_platform(hass, config, async_add_devices, platform, cls): + if platform in hass.data[DOMAIN][DATA_ADDERS]: + return True + def adder(hass, deviceID, connection, alias=None): entity = cls(hass, connection, deviceID, alias) async_add_devices([entity]) @@ -69,6 +72,7 @@ class BrowserModEntity(Entity): self.connection = connection self.deviceID = deviceID self._data = {} + self._alias = alias prefix = hass.data[DOMAIN][DATA_CONFIG].get(CONFIG_PREFIX, '') self.entity_id = async_generate_entity_id( self.domain+".{}", @@ -79,6 +83,19 @@ class BrowserModEntity(Entity): def updated(self): pass + @property + def device_info(self): + return { + "identifiers": { + (DOMAIN, self.deviceID) + }, + "name": self._alias or self.deviceID + } + + @property + def unique_id(self): + return f"{self.domain}-{self.deviceID}" + @property def data(self): return self._data diff --git a/custom_components/browser_mod/light.py b/custom_components/browser_mod/light.py index ffd1fb8..71c8ec3 100644 --- a/custom_components/browser_mod/light.py +++ b/custom_components/browser_mod/light.py @@ -11,6 +11,11 @@ PLATFORM = 'light' async def async_setup_platform(hass, config, async_add_devices, discovery_info=None): return setup_platform(hass, config, async_add_devices, PLATFORM, BrowserModLight) + +async def async_setup_entry(hass, config_entry, async_add_entities): + await async_setup_platform(hass, {}, async_add_entities) + + class BrowserModLight(LightEntity, BrowserModEntity): domain = PLATFORM diff --git a/custom_components/browser_mod/media_player.py b/custom_components/browser_mod/media_player.py index b9b8d6c..8770f4a 100644 --- a/custom_components/browser_mod/media_player.py +++ b/custom_components/browser_mod/media_player.py @@ -23,6 +23,10 @@ async def async_setup_platform(hass, config, async_add_devices, discovery_info=N return setup_platform(hass, config, async_add_devices, PLATFORM, BrowserModPlayer) +async def async_setup_entry(hass, config_entry, async_add_entities): + await async_setup_platform(hass, {}, async_add_entities) + + class BrowserModPlayer(MediaPlayerEntity, BrowserModEntity): domain = PLATFORM diff --git a/custom_components/browser_mod/sensor.py b/custom_components/browser_mod/sensor.py index 71c9a07..a8ec672 100644 --- a/custom_components/browser_mod/sensor.py +++ b/custom_components/browser_mod/sensor.py @@ -10,6 +10,11 @@ PLATFORM = 'sensor' async def async_setup_platform(hass, config, async_add_devices, discovery_info=None): return setup_platform(hass, config, async_add_devices, PLATFORM, BrowserModSensor) + +async def async_setup_entry(hass, config_entry, async_add_entities): + await async_setup_platform(hass, {}, async_add_entities) + + class BrowserModSensor(BrowserModEntity): domain = PLATFORM diff --git a/custom_components/browser_mod/service.py b/custom_components/browser_mod/service.py index 9ed092e..203ab03 100644 --- a/custom_components/browser_mod/service.py +++ b/custom_components/browser_mod/service.py @@ -1,8 +1,19 @@ import logging -from .const import DOMAIN, DATA_DEVICES, DATA_ALIASES, USER_COMMANDS + +from homeassistant.helpers.entity_registry import ( + async_entries_for_config_entry, + async_entries_for_device +) +from homeassistant.const import STATE_UNAVAILABLE + +from .const import ( + DOMAIN, DATA_DEVICES, DATA_ALIASES, + USER_COMMANDS, DATA_CONFIG, CONFIG_DEVICES +) _LOGGER = logging.getLogger(__name__) + async def setup_service(hass): def handle_command(call): @@ -27,11 +38,72 @@ async def setup_service(hass): devices[t].send(command, **data) def command_wrapper(call): - command = call.service.replace('_','-') + command = call.service.replace('_', '-') call.data = dict(call.data) call.data['command'] = command handle_command(call) hass.services.async_register(DOMAIN, 'command', handle_command) for cmd in USER_COMMANDS: - hass.services.async_register(DOMAIN, cmd.replace('-','_'), command_wrapper) + hass.services.async_register( + DOMAIN, + cmd.replace('-', '_'), + command_wrapper + ) + + async def call_service(service_call): + await async_clean_devices(hass, service_call.data) + + hass.services.async_register(DOMAIN, 'clean_devices', call_service) + + +async def async_clean_devices(hass, data): + config_entry = hass.config_entries.async_entries(DOMAIN)[0] + + entity_registry = await hass.helpers.entity_registry.async_get_registry() + device_registry = await hass.helpers.device_registry.async_get_registry() + entity_entries = async_entries_for_config_entry( + entity_registry, + config_entry.entry_id + ) + + device_entries = [ + entry + for entry + in device_registry.devices.values() + if config_entry.entry_id in entry.config_entries + ] + + user_config = hass.data[DOMAIN][DATA_CONFIG] + + devices_to_keep = [] + if CONFIG_DEVICES in user_config: + for d in device_entries: + for c in user_config[CONFIG_DEVICES]: + if (DOMAIN, c) in d.identifiers: + devices_to_keep.append(d.id) + + entities_to_remove = [] + for e in entity_entries: + entity = hass.states.get(e.entity_id) + if entity.state != STATE_UNAVAILABLE: + continue + if e.device_id in devices_to_keep: + continue + entities_to_remove.append(e) + + for e in entities_to_remove: + entity_registry.async_remove(e.entity_id) + + removed = [] + for d in device_entries: + if len(async_entries_for_device(entity_registry, d.id)) == 0: + removed.append(d.name) + device_registry.async_remove_device(d.id) + + devices = hass.data[DOMAIN][DATA_DEVICES] + for rec in devices: + devices[rec].send( + 'toast', + message=f"Removed devices: {removed}" + )