From 7ea6c96331c03bf498c071877747ad248a1f82b9 Mon Sep 17 00:00:00 2001 From: Matthieu DUVAL Date: Mon, 27 Feb 2023 18:02:27 +0100 Subject: [PATCH 1/4] Update to to latest georideapilib --- custom_components/georide/__init__.py | 22 +++++-- custom_components/georide/config_flow.py | 4 +- custom_components/georide/device.py | 84 +++++++++++++++++++----- custom_components/georide/manifest.json | 4 +- hacs.json | 2 +- 5 files changed, 86 insertions(+), 30 deletions(-) diff --git a/custom_components/georide/__init__.py b/custom_components/georide/__init__.py index 9c09610..6d24751 100644 --- a/custom_components/georide/__init__.py +++ b/custom_components/georide/__init__.py @@ -44,11 +44,8 @@ from .const import ( SIREN_ACTIVATION_DELAY ) - - _LOGGER = logging.getLogger(__name__) - CONFIG_SCHEMA = vol.Schema( { vol.Required(DOMAIN, default={}): { @@ -59,7 +56,6 @@ CONFIG_SCHEMA = vol.Schema( extra=vol.ALLOW_EXTRA, ) - async def async_setup(hass, config): """Setup GeoRide component.""" hass.data[DOMAIN] = {"config": config[DOMAIN], "devices": {}, "unsub": None} @@ -76,7 +72,6 @@ async def async_setup(hass, config): # Return boolean to indicate that initialization was successful. return True - async def async_setup_entry(hass, entry): """Set up GeoRide entry.""" config = hass.data[DOMAIN]["config"] @@ -111,7 +106,7 @@ async def async_setup_entry(hass, entry): return True -async def async_unload_entry(hass, entry): +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): """Unload an GeoRide config entry.""" await hass.config_entries.async_forward_entry_unload(entry, "device_tracker") @@ -120,9 +115,22 @@ async def async_unload_entry(hass, entry): await hass.config_entries.async_forward_entry_unload(entry, "binary_sensor") await hass.config_entries.async_forward_entry_unload(entry, "siren") + context = hass.data[DOMAIN]["context"] + context.socket.disconnect() // Disconnect only if all devices is disabled + + return True + +async def async_remove_config_entry_device(hass: HomeAssistant, config_entry: ConfigEntry, device_entry: DeviceEntry) -> bool: + """Remove an GeoRide device entry.""" + + await hass.config_entries.async_remove_config_entry_device(device_entry, "device_tracker") + await hass.config_entries.async_remove_config_entry_device(device_entry, "switch") + await hass.config_entries.async_remove_config_entry_device(device_entry, "sensor") + await hass.config_entries.async_remove_config_entry_device(device_entry, "binary_sensor") + await hass.config_entries.async_remove_config_entry_device(device_entry, "siren") context = hass.data[DOMAIN]["context"] - context.socket.disconnect() + # context.socket.disconnect() return True diff --git a/custom_components/georide/config_flow.py b/custom_components/georide/config_flow.py index b8d5cc5..5e324ca 100644 --- a/custom_components/georide/config_flow.py +++ b/custom_components/georide/config_flow.py @@ -12,7 +12,7 @@ from .const import CONF_EMAIL, CONF_PASSWORD, CONF_TOKEN, DOMAIN _LOGGER = logging.getLogger(__name__) -@config_entries.HANDLERS.register("georide") +@config_entries.HANDLERS.register(DOMAIN) class GeoRideConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """GeoRide config flow """ @@ -37,8 +37,6 @@ class GeoRideConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): vol.Required(CONF_PASSWORD): vol.All(str) })) - - async def async_step_georide_login(self, user_input): """ try to seupt GeoRide Account """ diff --git a/custom_components/georide/device.py b/custom_components/georide/device.py index a7276c4..0053076 100644 --- a/custom_components/georide/device.py +++ b/custom_components/georide/device.py @@ -9,6 +9,7 @@ class Device: def __init__(self, tracker): """Initialize GeoRideTracker device.""" self._tracker: GeoRideTracker = tracker + @property def tracker(self): @@ -20,11 +21,26 @@ class Device: """Get the name.""" return self._tracker.tracker_name + @property + def default_manufacturer(self) -> str: + """Get the default_manufacturer.""" + return "GeoRide" + @property def manufacturer(self) -> str: """Get the manufacturer.""" return "GeoRide" + + @property + def default_model(self) -> str: + """Get the default model.""" + return "GeoRide 1" + @property + def suggested_area(self) -> str: + """Get the suggested_area.""" + return "Garage" + @property def model_name(self) -> str: """Get the model name.""" @@ -34,10 +50,28 @@ class Device: elif self._tracker.version == 2: name = "GeoRide 2" elif self._tracker.version == 3: - name = "GeoRide 3" + if self._tracker.model == 'georide-3': + name = "GeoRide 3" + else: + name = "GeoRide Mini" else: name = "Prototype / Unknown" return name + + @property + def sw_version(self) -> str: + """Get the software version.""" + return str(self._tracker.software_version) + + @property + def hw_version(self) -> str: + """Get the hardware version.""" + return str(self._tracker.version) + + @property + def unique_id(self) -> str: + """Get the unique id.""" + return {(GEORIDE_DOMAIN, self._tracker.tracker_id)} @property def device_info(self): @@ -45,17 +79,13 @@ class Device: return { "name": self.name, "identifiers": self.unique_id, - "manufacturer": "GeoRide", + "manufacturer": self.manufacturer, "model": self.model_name, - "suggested_area": "Garage" + "suggested_area": self.suggested_area, + "sw_version" : self.sw_version, + "hw_version": self.hw_version } - - @property - def unique_id(self) -> str: - """Get the unique id.""" - return {(GEORIDE_DOMAIN, self._tracker.tracker_id)} - def __str__(self) -> str: """Get string representation.""" return f"GeoRide Device: {self.name}::{self.model_name}::{self.unique_id}" @@ -77,10 +107,25 @@ class DeviceBeacon: """Get the name.""" return self._beacon.name + @property + def default_manufacturer(self) -> str: + """Get the default_manufacturer.""" + return "GeoRide" + @property def manufacturer(self) -> str: """Get the manufacturer.""" return "GeoRide" + + @property + def default_model(self) -> str: + """Get the default model.""" + return "GeoRide Beacon" + + @property + def suggested_area(self) -> str: + """Get the suggested_area.""" + return "Garage" @property def model_name(self) -> str: @@ -88,23 +133,28 @@ class DeviceBeacon: name = "GeoRide Beacon" return name + @property + def unique_id(self) -> str: + """Get the unique id.""" + return {(GEORIDE_DOMAIN, self._beacon.beacon_id)} + + @property + def via_device(self) -> str: + """Get the unique id.""" + return {(GEORIDE_DOMAIN, self._beacon.tracker_id)} + @property def device_info(self): """Return the device info.""" return { "name": self.name, "identifiers": self.unique_id, - "manufacturer": "GeoRide", + "manufacturer": self.manufacturer, "model": self.model_name, - "suggested_area": "Garage" + "suggested_area": self.suggested_area, + "via_device": self.via_device } - @property - def unique_id(self) -> str: - """Get the unique id.""" - - return {(GEORIDE_DOMAIN, self._beacon.beacon_id)} - def __str__(self) -> str: """Get string representation.""" return f"GeoRide Device: {self.name}::{self.model_name}::{self.unique_id}" \ No newline at end of file diff --git a/custom_components/georide/manifest.json b/custom_components/georide/manifest.json index f04ebf7..f95928c 100644 --- a/custom_components/georide/manifest.json +++ b/custom_components/georide/manifest.json @@ -6,10 +6,10 @@ "issue_tracker": "https://github.com/ptimatth/GeorideHA/issues", "iot_class": "cloud_polling", "requirements": [ - "georideapilib>=0.8.4", + "georideapilib>=0.9.0", "pyjwt>=2.2.0" ], "dependencies": [], "codeowners": ["ptimatth"], - "version": "0.9.0" + "version": "1.0.0" } \ No newline at end of file diff --git a/hacs.json b/hacs.json index d8e4f24..8c67814 100644 --- a/hacs.json +++ b/hacs.json @@ -4,5 +4,5 @@ "render_readme": true, "domains": ["devices_tracker", "sensor"], "country": ["FR"], - "homeassistant": "2022.2.0" + "homeassistant": "2023.2.0" } \ No newline at end of file From bdaccea1e1b89c0e2cd3a2b108cbb72a76f1f73a Mon Sep 17 00:00:00 2001 From: Matthieu DUVAL Date: Mon, 27 Feb 2023 21:31:37 +0100 Subject: [PATCH 2/4] Add devices class to better categorisation in HA --- custom_components/georide/__init__.py | 18 ++----- custom_components/georide/binary_sensor.py | 60 ++++++++++++++++++---- custom_components/georide/device.py | 2 +- custom_components/georide/manifest.json | 2 +- custom_components/georide/sensor.py | 18 ++++++- 5 files changed, 73 insertions(+), 27 deletions(-) diff --git a/custom_components/georide/__init__.py b/custom_components/georide/__init__.py index 6d24751..3bac8c4 100644 --- a/custom_components/georide/__init__.py +++ b/custom_components/georide/__init__.py @@ -106,7 +106,7 @@ async def async_setup_entry(hass, entry): return True -async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): +async def async_unload_entry(hass, entry): """Unload an GeoRide config entry.""" await hass.config_entries.async_forward_entry_unload(entry, "device_tracker") @@ -116,22 +116,12 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): await hass.config_entries.async_forward_entry_unload(entry, "siren") context = hass.data[DOMAIN]["context"] - context.socket.disconnect() // Disconnect only if all devices is disabled + context.socket.disconnect() # Disconnect only if all devices is disabled return True -async def async_remove_config_entry_device(hass: HomeAssistant, config_entry: ConfigEntry, device_entry: DeviceEntry) -> bool: - """Remove an GeoRide device entry.""" - - await hass.config_entries.async_remove_config_entry_device(device_entry, "device_tracker") - await hass.config_entries.async_remove_config_entry_device(device_entry, "switch") - await hass.config_entries.async_remove_config_entry_device(device_entry, "sensor") - await hass.config_entries.async_remove_config_entry_device(device_entry, "binary_sensor") - await hass.config_entries.async_remove_config_entry_device(device_entry, "siren") - - context = hass.data[DOMAIN]["context"] - # context.socket.disconnect() - +async def async_remove_config_entry_device(hass, config_entry, device_entry) -> bool: + """Remove an GeoRide device entry.""" return True diff --git a/custom_components/georide/binary_sensor.py b/custom_components/georide/binary_sensor.py index da6660d..911d1e5 100644 --- a/custom_components/georide/binary_sensor.py +++ b/custom_components/georide/binary_sensor.py @@ -6,8 +6,7 @@ from typing import Any, Mapping from homeassistant.core import callback from homeassistant.helpers.entity import DeviceInfo, EntityCategory -from homeassistant.components.binary_sensor import BinarySensorEntity -from homeassistant.components.binary_sensor import ENTITY_ID_FORMAT +from homeassistant.components.binary_sensor import BinarySensorEntity, BinarySensorDeviceClass, ENTITY_ID_FORMAT from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, DataUpdateCoordinator @@ -33,6 +32,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): # pylint: d entities.append(GeoRideActiveSubscriptionBinarySensorEntity(coordinator, tracker_device)) entities.append(GeoRideNetworkBinarySensorEntity(coordinator, tracker_device)) entities.append(GeoRideMovingBinarySensorEntity(coordinator, tracker_device)) + entities.append(GeoRideUpdatedBinarySensorEntity(coordinator, tracker_device)) hass.data[GEORIDE_DOMAIN]["devices"][tracker_device.tracker.tracker_id] = coordinator @@ -102,7 +102,7 @@ class GeoRideStolenBinarySensorEntity(GeoRideBinarySensorEntity): @property def device_class(self): """Return the device class.""" - return "problem" + return BinarySensorDeviceClass.PROBLEM @property def is_on(self): @@ -132,7 +132,7 @@ class GeoRideCrashedBinarySensorEntity(GeoRideBinarySensorEntity): @property def device_class(self): """Return the device class.""" - return "problem" + return BinarySensorDeviceClass.PROBLEM @property def is_on(self): @@ -236,6 +236,11 @@ class GeoRideNetworkBinarySensorEntity(GeoRideBinarySensorEntity): return True return False + @property + def device_class(self) -> str: + """device class""" + return BinarySensorDeviceClass.CONNECTIVITY + @property def name(self): """ GeoRide name """ @@ -268,12 +273,49 @@ class GeoRideMovingBinarySensorEntity(GeoRideBinarySensorEntity): def is_on(self): """state value property""" return self._tracker_device.tracker.moving + + @property + def device_class(self) -> str: + """device class""" + return BinarySensorDeviceClass.MOVING @property def name(self): """ GeoRide name """ return f"{self._name} is moving" +class GeoRideUpdatedBinarySensorEntity(GeoRideBinarySensorEntity): + """Represent a tracked device.""" + @property + def entity_category(self): + return EntityCategory.DIAGNOSTIC + + def __init__(self, coordinator: DataUpdateCoordinator[Mapping[str, Any]], + tracker_device: Device): + """Set up Georide entity.""" + super().__init__(coordinator, tracker_device) + self.entity_id = f"{ENTITY_ID_FORMAT.format('update')}.{tracker_device.tracker.tracker_id}"# pylint: disable=C0301 + + @property + def unique_id(self): + """Return the unique ID.""" + return f"update_{self._tracker_device.tracker.tracker_id}" + + @property + def is_on(self): + """state value property""" + return not self._tracker_device.tracker.is_up_to_date + + @property + def device_class(self) -> str: + """device class""" + return BinarySensorDeviceClass.UPDATE + + @property + def name(self): + """ GeoRide name """ + return f"{self._name} have an update" + class GeoRideBeaconUpdatedBinarySensorEntity(GeoRideBeaconBinarySensorEntity): """Represent a tracked device.""" @property @@ -291,15 +333,15 @@ class GeoRideBeaconUpdatedBinarySensorEntity(GeoRideBeaconBinarySensorEntity): """Return the unique ID.""" return f"update_{self._tracker_device_beacon.beacon.beacon_id}" - @property - def device_class(self): - """Return the device class.""" - return "update" - @property def is_on(self): """state value property""" return not self._tracker_device_beacon.beacon.is_updated + + @property + def device_class(self) -> str: + """device class""" + return BinarySensorDeviceClass.UPDATE @property def name(self): diff --git a/custom_components/georide/device.py b/custom_components/georide/device.py index 0053076..65fe47a 100644 --- a/custom_components/georide/device.py +++ b/custom_components/georide/device.py @@ -141,7 +141,7 @@ class DeviceBeacon: @property def via_device(self) -> str: """Get the unique id.""" - return {(GEORIDE_DOMAIN, self._beacon.tracker_id)} + return (GEORIDE_DOMAIN, self._beacon.linked_tracker_id ) @property def device_info(self): diff --git a/custom_components/georide/manifest.json b/custom_components/georide/manifest.json index f95928c..0cd1643 100644 --- a/custom_components/georide/manifest.json +++ b/custom_components/georide/manifest.json @@ -6,7 +6,7 @@ "issue_tracker": "https://github.com/ptimatth/GeorideHA/issues", "iot_class": "cloud_polling", "requirements": [ - "georideapilib>=0.9.0", + "georideapilib>=0.9.3", "pyjwt>=2.2.0" ], "dependencies": [], diff --git a/custom_components/georide/sensor.py b/custom_components/georide/sensor.py index 1ad10a8..62423bb 100644 --- a/custom_components/georide/sensor.py +++ b/custom_components/georide/sensor.py @@ -5,8 +5,7 @@ from typing import Any, Mapping from homeassistant.core import callback from homeassistant.helpers.entity import DeviceInfo, EntityCategory -from homeassistant.components.sensor import SensorEntity -from homeassistant.components.sensor import ENTITY_ID_FORMAT +from homeassistant.components.sensor import SensorEntity, SensorDeviceClass, ENTITY_ID_FORMAT from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, DataUpdateCoordinator, @@ -245,6 +244,11 @@ class GeoRideInternalBatterySensorEntity(CoordinatorEntity, SensorEntity): """icon getter""" return "mdi:battery" + @property + def device_class(self) -> str: + """device class""" + return SensorDeviceClass.VOLTAGE + @property def device_info(self) -> DeviceInfo: """Return the device info.""" @@ -302,6 +306,11 @@ class GeoRideExternalBatterySensorEntity(CoordinatorEntity, SensorEntity): """icon getter""" return "mdi:battery" + @property + def device_class(self) -> str: + """device class""" + return SensorDeviceClass.VOLTAGE + @property def device_info(self) -> DeviceInfo: """Return the device info.""" @@ -391,6 +400,11 @@ class GeoRideBeaconBatterySensorEntity(CoordinatorEntity, SensorEntity): """icon getter""" return "mdi:battery" + @property + def device_class(self) -> str: + """device class""" + return SensorDeviceClass.BATTERY + @property def device_info(self) -> DeviceInfo: """Return the device info.""" From fa0f67e6ec32fd8b0ff60a47ebf5166bc85f05b8 Mon Sep 17 00:00:00 2001 From: Matthieu DUVAL Date: Tue, 28 Feb 2023 19:23:10 +0100 Subject: [PATCH 3/4] Update to latest georidelib --- custom_components/georide/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/georide/manifest.json b/custom_components/georide/manifest.json index 0cd1643..67eab13 100644 --- a/custom_components/georide/manifest.json +++ b/custom_components/georide/manifest.json @@ -6,7 +6,7 @@ "issue_tracker": "https://github.com/ptimatth/GeorideHA/issues", "iot_class": "cloud_polling", "requirements": [ - "georideapilib>=0.9.3", + "georideapilib>=0.9.4", "pyjwt>=2.2.0" ], "dependencies": [], From aa6cf7b515ea7ce13fe288610d8c9f236db18179 Mon Sep 17 00:00:00 2001 From: Matthieu DUVAL Date: Tue, 28 Feb 2023 19:34:56 +0100 Subject: [PATCH 4/4] Add beacon verification from the parametter has_beacon unsted of hw version --- custom_components/georide/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/georide/__init__.py b/custom_components/georide/__init__.py index 3bac8c4..77cac20 100644 --- a/custom_components/georide/__init__.py +++ b/custom_components/georide/__init__.py @@ -306,7 +306,7 @@ class GeoRideContext: "tracker_device": Device(tracker), "coordinator": coordinator } - if tracker.version > 2: + if tracker.has_beacon: tracker_beacons = await self.get_tracker_beacons_by_tracker_id(tracker.tracker_id) for tracker_beacon in tracker_beacons: beacon_coordinator = DataUpdateCoordinator[Mapping[str, Any]](