Compare commits

..

110 Commits

Author SHA1 Message Date
7be10411f5 Bump version to 1.1.2 2026-02-05 21:45:59 +01:00
97b54c9de8 Merge pull request #16 from ptimatth/15-update-ha-202620---missing-all-entities
feat: Update entity_id format and add GeoRide model support
2026-02-05 21:31:21 +01:00
ab64760872 Fix entity_id wronformat since Home assitant 2026.02 2026-02-05 21:14:36 +01:00
djodjo02130
38ceb3a2a1 Add Georide 3S 2025-04-19 22:35:52 +00:00
691b5c2475 Shipping v1.1.1 2024-11-12 20:13:47 +01:00
eea4e1bb6b update to vesion 1.1.1 2024-11-12 20:11:16 +01:00
43b578cd98 Merge pull request #9 from Sigri44/master
Add manual installation
2024-11-12 20:10:57 +01:00
f7d801e1f4 Update deprecated methods and types 2024-11-12 19:49:30 +01:00
6257b65687 Merge pull request #9 from Sigri44/master
Add manual installation
2023-10-29 13:26:04 +01:00
Sigri
cbf81d7438 Add manual installation 2023-10-25 21:19:05 +02:00
Sigri
33d8942e31 Fix syntax 2023-10-25 20:39:23 +02:00
7d08d8f9a7 Shipping v1.1.0 2023-05-24 13:03:59 +02:00
ad77edd103 Update badge and GeoRidelib version 2023-05-24 12:46:26 +02:00
be29b2d9bf Update to georideapilib 0.9.6 2023-03-24 23:17:13 +01:00
f30176cb65 Merge pull request #8 from ptimatth/develop
Shipping v1.0.0
2023-03-18 10:58:58 +01:00
aa6cf7b515 Add beacon verification from the parametter has_beacon unsted of hw version 2023-02-28 19:34:56 +01:00
fa0f67e6ec Update to latest georidelib 2023-02-28 19:23:10 +01:00
bdaccea1e1 Add devices class to better categorisation in HA 2023-02-27 21:49:14 +01:00
Matthieu DUVAL
7ea6c96331 Update to to latest georideapilib 2023-02-27 18:02:27 +01:00
e6817e174b Shipping v0.9.1 2022-04-06 17:49:26 +02:00
22047ff6be Shipping v0.9.0 2022-04-06 17:37:16 +02:00
b8c8bcba14 Update pyjwt to match with the latest version 2022-04-06 17:24:18 +02:00
d270517025 Update to georideapilib 0.8.4 2022-03-20 14:05:24 +01:00
6eebbebdfa Update to georideapilib 0.8.3 2022-03-19 21:45:17 +01:00
cb5f069aff Fix tracker asign to tracker_device 2022-03-07 12:12:03 +01:00
4384526fae Fix call on position_event 2022-03-06 19:38:51 +01:00
2bc41f43c5 Fix sensor declaration 2022-03-06 13:44:19 +01:00
118911c361 Fix tracker position, add mesuremnt for longtherme statistics 2022-03-06 13:16:44 +01:00
9719f26631 Fix update beacon, Add speedometer, Add update on tracker properties change 2022-03-06 12:46:50 +01:00
0ee5a9ad12 Fix tracker device not exti 2022-03-04 19:46:09 +01:00
e9da051fc1 Fix attribute missing _tracker_device_beacon 2022-03-04 19:43:05 +01:00
1b2776f646 Fix update method on coordinator coma 2022-03-04 19:37:06 +01:00
112cf68542 Fix update method on coordinator 2022-03-04 19:34:00 +01:00
9f70dc220f Fix 'Device' object has no attribute 'beacon' 2022-03-04 19:24:47 +01:00
7a8702d57c Fix 'DeviceBeacon' object has no attribute 'tracker_beacon' 2022-03-04 19:21:59 +01:00
7f3d5d7ec7 Fix tracker device not exist 2022-03-04 19:17:20 +01:00
bf1e138c05 Fix wrong sensor setup 2022-03-04 19:12:55 +01:00
ce06f109fe Beacon use wrong coordinator 2022-03-04 19:07:54 +01:00
e9262ff3d4 Fix coordinator device name, await on add entity 2022-03-04 19:01:22 +01:00
f36a3093bf Fix missing import beacon 2022-03-04 18:55:12 +01:00
73d15f8c9a Fix mist spelling of get_tracker_beacons 2022-03-04 18:49:55 +01:00
d326726138 Fix invalid sintax 2022-03-04 18:45:58 +01:00
3000d5588b Fix support multiple tracker beacon 2022-03-04 18:42:13 +01:00
a3c84c63fd Fix missing tracker in list 2022-03-04 18:18:46 +01:00
a37cca7902 Fix missing get_tracker_beacon_by_tracker_id 2022-03-04 18:11:28 +01:00
32db0ec42e Fix "NameError: name 'new_georide_tracker' is not defined" 2022-03-04 18:07:37 +01:00
1250dda047 Fix "list' object has no attribute 'tracker_id' " 2022-03-04 18:04:10 +01:00
759fe2d257 Fix "local variable 'tracker' referenced before assignment" 2022-03-04 18:00:03 +01:00
9e2e6e8d53 remove await on force_refresh_trackers_beacon 2022-03-04 17:56:29 +01:00
4dc091525e Fix force_refresh_trackers_beacon 2022-03-04 17:53:51 +01:00
e92ac1825d Fix siren and device unique id 2022-03-04 17:28:10 +01:00
fd10326489 Revert change on device id format 2022-03-04 17:18:08 +01:00
4dcf642260 Fix unique id format 2022-03-04 17:08:24 +01:00
80de671649 Small import fixes 2022-03-04 16:59:28 +01:00
9f5b09dc4c Add entity category on All entities 2022-03-04 16:50:34 +01:00
09231c4fc0 First steps of entity category 2022-03-04 16:15:42 +01:00
0555c63b7d Bump version to 0.9.0 2022-03-04 15:52:47 +01:00
adb6cef3c7 Add suport for new device beacon, siren control, eco_mode control, better device event 2022-03-04 15:47:19 +01:00
7d809c2105 Shipping v0.8.2 2022-03-03 20:40:02 +01:00
af12cd70e4 Add cloud and issue info 2022-03-03 20:30:24 +01:00
e59c8cb195 Shipping v0.8.1 2021-10-10 13:09:57 +02:00
b17f38ee66 Fix #4 jwt decode afater update of hA to pyjwt 2.1.0 2021-10-10 13:06:26 +02:00
5af8e7dc5d Shipping v0.8.0, thx to @Inervo 2021-08-30 20:14:56 +02:00
fe073cff55 Merge PR #3 2021-08-30 20:09:10 +02:00
9053f95bc1 Shipping v0.7.2 2021-07-13 20:37:32 +02:00
a3c5d2db6e Fix data.name unsted of data['name'] 2021-07-13 20:36:19 +02:00
fc383ff373 Shipping v0.7.1 2021-07-05 18:42:57 +02:00
c295c8848c Change version 2021-07-05 18:41:22 +02:00
29f42a75c9 Fix alarm event trigger 2021-07-05 18:39:07 +02:00
57abb51b13 Shipping 0.7.0 2021-07-01 11:54:45 +02:00
54b7e9b5d6 Setup ne device event on georide Ha 2021-07-01 11:54:03 +02:00
24180c9345 Shipping v0.6.2 2021-05-05 22:56:38 +02:00
af0a4a37aa Bump georideapilib min version to v0.6.1 2021-05-05 22:53:52 +02:00
a83bae6d3e Shipping v0.6.1 2021-04-11 18:01:27 +02:00
c229ca4b6e Fix lock change 2021-04-11 18:00:54 +02:00
b84a4cfd7f Shipping v0.6.0 2021-04-11 17:27:23 +02:00
5c77acbdb7 Fire more home assistant event 2021-04-11 17:22:42 +02:00
5e83ed1c68 Fix small mistakes 2021-04-11 17:00:33 +02:00
8a44d22d41 Migreate to 0.6.0 and new device architecture 2021-04-11 13:38:15 +02:00
56a8a35479 Fix old Swirhc entity to odometer 2021-04-11 01:31:00 +02:00
7d25405c2f Use coordinator to imporoo reactivituy 2021-04-11 01:07:31 +02:00
dbbe1f33b9 Add space betteween useless refresh 2021-04-11 00:23:25 +02:00
660aaa0180 Fix switch response 2021-04-11 00:05:52 +02:00
0113102a7f Centralize update loop 2021-04-10 23:51:51 +02:00
093bebc391 Trying to refactor the tracker entity_status 2021-04-10 23:08:52 +02:00
ec63a36c81 Change tracker update mangment 2021-04-10 20:13:49 +02:00
283a1074e1 Move connect_socket into the context 2021-04-10 16:01:32 +02:00
5cab811c85 Fix binnary sensors 2021-04-10 13:57:23 +02:00
e09f8eb7e7 Add new sensor 2021-04-10 13:10:30 +02:00
be4e68e152 Add version in manifet, and add_executor_job on config_flow 2021-04-10 12:54:42 +02:00
bcaa4bf957 Add hass object on entity items 2021-04-10 12:43:43 +02:00
af1df1cd84 Fix tracker retrivial 2021-04-10 12:25:47 +02:00
b4871b9374 Fix param gived to async_add_executor_job 2021-04-10 12:09:49 +02:00
f72481236d Add get_tracker to await 2021-04-10 12:05:38 +02:00
1e21de655f Move all external call to asyn/await 2021-04-10 11:58:58 +02:00
3c6f39f7ba Add async for refresh_tken 2021-04-10 11:34:43 +02:00
02e1656aab Merge branch 'develop' 2020-12-01 19:24:26 +01:00
4593c694d5 GeoRide wording update 2020-05-26 22:39:09 +02:00
9b651f933e Shipping v0.5.0 2020-05-23 15:07:27 +02:00
a8274f7fd1 Update to prepare new home assisntant version (0.111) 2020-05-23 15:01:52 +02:00
ef783db79b HotFix deveice_tracker.py du to HA Update 2020-03-28 23:59:38 +01:00
18de851e4d Merge pull request #2 from limeryls/fix_login
Fix setting login credentials using config_flow and fix mistakes in spelling
2020-03-28 17:04:19 +01:00
Kévin SIMON-QUESNEL
cbed2cebb3 Fix setting login credentials using config_flow and fix mistakes in spelling 2020-03-28 04:38:52 +01:00
e5bd90ae6f Shipping v0.4.1 2019-11-11 13:37:28 +01:00
a2e65694fc Increase interval to refresh each 10 minutes 2019-11-11 13:33:17 +01:00
66bd335a8e Add component to default HACS 2019-11-09 10:04:47 +01:00
14300c5278 Add polling off all tracker each hours 2019-11-09 09:11:56 +01:00
bc3da8f0e5 Shipping v0.4.0 2019-11-05 22:01:46 +01:00
ecf2a23c70 Fix socket thread launch after first time 2019-11-05 21:52:07 +01:00
991370fcec index on master: 7a80670 Add better polling 2019-11-03 19:15:58 +01:00
15 changed files with 1597 additions and 332 deletions

101
README.md
View File

@@ -1,16 +1,96 @@
# Georide Home assistant # GeoRide Home assistant
![Logo Georide](./georide-logo.png) ![Logo GeoRide](https://brands.home-assistant.io/georide/logo.png)
⚠️ This is not an official implementation ⚠️ This is not an official implementation
[![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg?style=for-the-badge)](https://github.com/custom-components/hacs)
[![hacs_badge](https://img.shields.io/badge/HACS-Custom-orange.svg?style=for-the-badge)](https://github.com/custom-components/hacs)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg?style=for-the-badge)](https://www.gnu.org/licenses/gpl-3.0) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg?style=for-the-badge)](https://www.gnu.org/licenses/gpl-3.0)
[![install_badge](https://img.shields.io/badge/dynamic/json?color=41BDF5&logo=home-assistant&label=integration%20usage&suffix=%20installs&cacheSeconds=15600&url=https://analytics.home-assistant.io/custom_integrations.json&query=$.georide.total)](https://analytics.home-assistant.io/)
Official GeoRide website: https://georide.fr/ Official GeoRide website: https://georide.fr/
## Description ## Description
This component add some sensor for GeoRide Tracker This component add some sensor for GeoRide Tracker
### What's entity is available :
Get GeoRide position
Get GeoRide lock status
Change GeoRide lock status
Get stollen status
Get crashed status
Get is owner status
Get subsription status
Get odomoter to km an m (2 entities)
Internal battery (of georide 3) (not work on GR1)
External battery (of the bike) (not work on GR1)
Fixtime (last registered positition of the georide)
### What's events are available:
you can filter by data.device_id == XX (XX is your tracker id)
you can display your tracker name by by data.device_name
event;
georide_position_event
georide_lock_event
georide_device_event
georide_alarm_event
you can filter with data.type == 'alarm_vibration' to filter by vibration
here is the alarm type available: (listen the georide_alarm_event)
alarm_vibration
alarm_exitZone
alarm_crash
alarm_crashParking
alarm_deviceOffline
alarm_deviceOnline
alarm_powerCut
alarm_powerUncut
alarm_batteryWarning
alarm_temperatureWarning
alarm_magnetOn
alarm_magnetOff
alarm_sonorAlarmOn
## Question:
### How to have the odometer in Km ? (Deprecated, now you have an entity - thx @Inervo)
Simply add a sensor like this in configuration.yaml
(Replace XX by your tracker id)
```yaml
sensor:
- platform: template # Conversion georide de m en km
sensors:
odometer_XX_km:
friendly_name: "Odometter - Km"
value_template: "{{ states.sensor.odometer_XX.state | multiply(0.001) | round(3, 'flour') }}"
unit_of_measurement: 'Km'
```
### How to use the event:
Simply made a automation like this:
```yaml
alias: '[TEST] Send notification'
description: ''
trigger:
- platform: event
event_type: georide_lock_event
condition: []
action:
- service: notify.mobile_app_oneplus_a3003
data:
message: 'The device {{ data.device_name }} have recieved a lock event'
mode: single
```
## Options ## Options
@@ -22,12 +102,21 @@ This component add some sensor for GeoRide Tracker
## Installation ## Installation
### Option 1 ### Option 1
- Just folow the integration config steps. - Just follow the integration config steps.
### Option 2 ### Option 2
- Add the folowing line in your configuration.yml - Add the following line in your configuration.yml
```yaml ```yaml
georide: georide:
email: <your-email>@exmple.com email: <your-email>@exmple.com
password: <your-password> password: <your-password>
``` ```
### Option 3 (manual)
- Download .zip git repo
- Go to /config (with File Editor or SSH)
- Unzip GeorideHA.zip
- Restart Home Assistant
- Go to "Integrations"
- If GeoRide not display, choose "add integration"
- Configure GeoRide add-on

View File

@@ -1,7 +1,9 @@
""" georide custom conpennt """ """ georide custom conpennt """
from collections import defaultdict from collections import defaultdict
import asyncio
import logging import logging
from typing import Any, Mapping
from datetime import timedelta from datetime import timedelta
import math import math
import time import time
@@ -11,11 +13,10 @@ import voluptuous as vol
import jwt import jwt
from aiohttp.web import json_response from aiohttp.web import json_response
from georideapilib.objects import GeorideAccount from georideapilib.objects import GeoRideAccount
import georideapilib.api as GeorideApi import georideapilib.api as GeoRideApi
from georideapilib.socket import GeorideSocket
from georideapilib.socket import GeoRideSocket
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.const import CONF_WEBHOOK_ID from homeassistant.const import CONF_WEBHOOK_ID
@@ -24,20 +25,26 @@ import homeassistant.helpers.config_validation as cv
import homeassistant.helpers.event as ha_event import homeassistant.helpers.event as ha_event
from homeassistant.setup import async_when_setup from homeassistant.setup import async_when_setup
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from .device import Device, DeviceBeacon
from .const import ( from .const import (
CONF_EMAIL, CONF_EMAIL,
CONF_PASSWORD, CONF_PASSWORD,
CONF_TOKEN, CONF_TOKEN,
TRACKER_ID, TRACKER_ID,
TOKEN_SAFE_DAY, TOKEN_SAFE_DAY,
DOMAIN MIN_UNTIL_REFRESH,
DOMAIN,
SIREN_ACTIVATION_DELAY
) )
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = vol.Schema(
{ {
vol.Required(DOMAIN, default={}): { vol.Required(DOMAIN, default={}): {
@@ -48,9 +55,8 @@ CONFIG_SCHEMA = vol.Schema(
extra=vol.ALLOW_EXTRA, extra=vol.ALLOW_EXTRA,
) )
async def async_setup(hass, config): async def async_setup(hass, config):
"""Setup Georide component.""" """Setup GeoRide component."""
hass.data[DOMAIN] = {"config": config[DOMAIN], "devices": {}, "unsub": None} hass.data[DOMAIN] = {"config": config[DOMAIN], "devices": {}, "unsub": None}
hass.async_create_task( hass.async_create_task(
hass.config_entries.flow.async_init( hass.config_entries.flow.async_init(
@@ -65,82 +71,67 @@ async def async_setup(hass, config):
# Return boolean to indicate that initialization was successful. # Return boolean to indicate that initialization was successful.
return True return True
def connect_socket(hass, component):
"""subscribe to georide socket"""
context = hass.data[DOMAIN]["context"]
socket = GeorideSocket()
socket.subscribe_locked(context.on_lock_callback)
socket.subscribe_device(context.on_device_callback)
socket.subscribe_position(context.on_position_callback)
context.socket = socket
socket.init()
socket.connect(context.async_get_token())
async def async_setup_entry(hass, entry): async def async_setup_entry(hass, entry):
"""Set up Georide entry.""" """Set up GeoRide entry."""
config = hass.data[DOMAIN]["config"] config = hass.data[DOMAIN]["config"]
email = config.get(CONF_EMAIL) or entry.data[CONF_EMAIL] email = config.get(CONF_EMAIL) or entry.data[CONF_EMAIL]
password = config.get(CONF_PASSWORD) or entry.data[CONF_PASSWORD] password = config.get(CONF_PASSWORD) or entry.data[CONF_PASSWORD]
token = config.get(CONF_TOKEN) or entry.data[CONF_TOKEN] token = config.get(CONF_TOKEN) or entry.data[CONF_TOKEN]
context = GeorideContext( context = GeoRideContext(
hass, hass,
email, email,
password, password,
token token
) )
_LOGGER.info("Context-setup and start the thread")
_LOGGER.info("Thread started")
hass.data[DOMAIN]["context"] = context hass.data[DOMAIN]["context"] = context
# We add trackers to the context # We add trackers to the context
trackers = GeorideApi.get_trackers(token) await context.init_context(hass)
context.georide_trackers = trackers
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, "device_tracker"))
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, "switch"))
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, "sensor"))
thread = Thread(target=connect_socket, args=(hass, entry))
thread.start()
await hass.config_entries.async_forward_entry_setups(entry, ["device_tracker", "switch", "sensor", "binary_sensor", "siren"])
return True return True
async def async_unload_entry(hass, entry): async def async_unload_entry(hass, entry):
"""Unload an Georide config entry.""" """Unload an GeoRide config entry."""
await hass.config_entries.async_forward_entry_unload(entry, "device_tracker") await hass.config_entries.async_forward_entry_unload(entry, "device_tracker")
await hass.config_entries.async_forward_entry_unload(entry, "switch") await hass.config_entries.async_forward_entry_unload(entry, "switch")
await hass.config_entries.async_forward_entry_unload(entry, "sensor") await hass.config_entries.async_forward_entry_unload(entry, "sensor")
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 = hass.data[DOMAIN]["context"]
context.socket.disconnect() context.socket.disconnect() # Disconnect only if all devices is disabled
hass.data[DOMAIN]["unsub"]()
return True return True
async def async_remove_config_entry_device(hass, config_entry, device_entry) -> bool:
"""Remove an GeoRide device entry."""
return True
class GeorideContext:
"""Hold the current Georide context."""
class GeoRideContext:
"""Hold the current GeoRide context."""
def __init__(self, hass, email, password, token): def __init__(self, hass, email, password, token):
"""Initialize an Georide context.""" """Initialize an GeoRide context."""
self._hass = hass self._hass = hass
self._email = email self._email = email
self._password = password self._password = password
self._georide_trackers = defaultdict(set) self._georide_trackers_coordoned = []
self._georide_trackers_beacon_coordoned = []
self._georide_trackers_beacon = []
self._georide_trackers = []
self._token = token self._token = token
self._socket = None self._socket = None
self._thread_started = False
self._previous_refresh = math.floor(time.time()/60)
@property @property
def hass(self): def hass(self):
""" hass """ """ hass """
@@ -163,24 +154,38 @@ class GeorideContext:
@property @property
def georide_trackers(self): def georide_trackers(self):
""" georide tracker list """ """ GeoRide tracker list """
return self._georide_trackers return self._georide_trackers
@georide_trackers.setter @georide_trackers.setter
def georide_trackers(self, trackers): def georide_trackers(self, trackers):
""" georide tracker list """ """ GeoRide tracker list """
self._georide_trackers = trackers self._georide_trackers = trackers
def get_token(self): async def connect_socket(self):
"""subscribe to GeoRide socket"""
_LOGGER.info("GeoRide socket connexion")
socket = GeoRideSocket()
socket.subscribe_locked(self.on_lock_callback)
socket.subscribe_device(self.on_device_callback)
socket.subscribe_position(self.on_position_callback)
socket.subscribe_alarm(self.on_alarm_callback)
socket.subscribe_refresh_tracker(self.on_refresh_tracker_callback)
self._socket = socket
socket.init()
self._hass.async_add_executor_job(socket.connect, await self.get_token())
async def get_token(self):
""" here we return the current valid tocken """ """ here we return the current valid tocken """
jwt_data = jwt.decode(self._token, verify=False) jwt_data = jwt.decode(self._token, options={"verify_signature": False})
exp_timestamp = jwt_data['exp'] exp_timestamp = jwt_data['exp']
epoch = math.ceil(time.time()) epoch = math.ceil(time.time())
if (exp_timestamp - TOKEN_SAFE_DAY) < epoch: if (exp_timestamp - TOKEN_SAFE_DAY) < epoch:
_LOGGER.info("Time reached, renew token") _LOGGER.info("Time reached, renew token")
account = GeorideApi.get_authorisation_token(self._email, self._password) account = await self._hass.async_add_executor_job(GeoRideApi.get_authorisation_token,
self._email, self._password)
config = self._hass.data[DOMAIN]["config"] config = self._hass.data[DOMAIN]["config"]
config[CONF_TOKEN] = account.auth_token config[CONF_TOKEN] = account.auth_token
self._token = account.auth_token self._token = account.auth_token
@@ -189,56 +194,284 @@ class GeorideContext:
_LOGGER.info("Token exp data: %s", exp_timestamp) _LOGGER.info("Token exp data: %s", exp_timestamp)
return self._token return self._token
def get_tracker(self, tracker_id): async def get_tracker(self, tracker_id):
""" here we return last tracker by id""" """ here we return last tracker by id"""
await self.refresh_trackers()
for tracker in self._georide_trackers: for tracker in self._georide_trackers:
if tracker.tracker_id == tracker_id: if tracker.tracker_id == tracker_id:
return tracker return tracker
return None return {}
async def get_tracker_beacon(self, beacon_id):
""" here we return last tracker_beacon by id"""
for tracker_beacon in self._georide_trackers_beacon:
if tracker_beacon.beacon_id == beacon_id:
return tracker_beacon
return {}
async def get_tracker_beacons_by_tracker_id(self, tracker_id):
""" here we return last tracker_beacon by id"""
filtered_beacon = []
for tracker_beacon in self._georide_trackers_beacon:
if tracker_beacon.linked_tracker_id == tracker_id:
filtered_beacon.append(tracker_beacon)
return filtered_beacon
async def refresh_trackers(self):
""" here we return last tracker by id"""
_LOGGER.debug("Call refresh tracker")
epoch_min = math.floor(time.time()/60)
if epoch_min != self._previous_refresh:
self._previous_refresh = epoch_min
await self.force_refresh_trackers()
for tracker in self._georide_trackers:
if tracker.is_siren_on:
if time.time() - SIREN_ACTIVATION_DELAY > tracker.siren_last_on_date:
tracker.is_siren_on = False
async def refresh_trackers_beacon(self):
""" here we return last tracker by id"""
_LOGGER.debug("Do nothing, updated by another way")
async def force_refresh_trackers(self):
"""Used to refresh the tracker list"""
_LOGGER.info("Tracker list refresh")
new_georide_trackers = await self._hass.async_add_executor_job(GeoRideApi.get_trackers,
await self.get_token())
for refreshed_tracker in new_georide_trackers:
found = False
for tracker in self._georide_trackers:
if tracker.tracker_id == refreshed_tracker.tracker_id:
tracker.update_all_data(refreshed_tracker)
if tracker.version > 2:
await self.force_refresh_trackers_beacon(tracker.tracker_id)
found = True
if not found:
self._georide_trackers.append(refreshed_tracker)
if refreshed_tracker.version > 2:
await self.force_refresh_trackers_beacon(refreshed_tracker.tracker_id)
if not self._thread_started:
_LOGGER.info("Start the thread")
# We refresh the tracker list each hours
self._thread_started = True
await self.connect_socket()
async def force_refresh_trackers_beacon(self, tracker_id):
"""Used to refresh the tracker list"""
_LOGGER.info("Tracker beacon refresh")
new_georide_tracker_beacons = await self._hass.async_add_executor_job(GeoRideApi.get_tracker_beacons,
await self.get_token(), tracker_id)
for new_georide_tracker_beacon in new_georide_tracker_beacons:
found = False
for tracker_beacon in self._georide_trackers_beacon:
if tracker_beacon.beacon_id == new_georide_tracker_beacon.beacon_id:
tracker_beacon.update_all_data(new_georide_tracker_beacon)
found = True
if not found:
self._georide_trackers_beacon.append(new_georide_tracker_beacon)
if not self._thread_started:
_LOGGER.info("Start the thread")
# We refresh the tracker list each hours
self._thread_started = True
await self.connect_socket()
async def init_context(self, hass):
"""Used to refresh the tracker list"""
_LOGGER.info("Init_context")
await self.force_refresh_trackers()
update_interval = timedelta(minutes=MIN_UNTIL_REFRESH)
for tracker in self._georide_trackers:
coordinator = DataUpdateCoordinator[Mapping[str, Any]](
hass,
_LOGGER,
name=tracker.tracker_name,
update_method=self.refresh_trackers,
update_interval=update_interval
)
coordoned_tracker = {
"tracker_device": Device(tracker),
"coordinator": coordinator
}
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]](
hass,
_LOGGER,
name=tracker_beacon.name,
update_method=self.refresh_trackers_beacon,
update_interval=update_interval
)
coordoned_beacon = {
"tracker_beacon": DeviceBeacon(tracker_beacon),
"coordinator": beacon_coordinator
}
self._georide_trackers_beacon_coordoned.append(coordoned_beacon)
self._georide_trackers_coordoned.append(coordoned_tracker)
@property
def georide_trackers_coordoned(self):
"""Return coordoned trackers"""
return self._georide_trackers_coordoned
@property
def georide_trackers_beacon_coordoned(self):
"""Return coordoned trackers"""
return self._georide_trackers_beacon_coordoned
@property @property
def socket(self): def socket(self):
""" hold the georide socket """ """ hold the GeoRide socket """
return self._socket return self._socket
@socket.setter @socket.setter
def socket(self, socket): def socket(self, socket):
"""set the georide socket""" """set the GeoRide socket"""
self._socket = socket self._socket = socket
@callback @callback
def on_lock_callback(self, data): def on_lock_callback(self, data):
"""on lock callback""" """on lock callback"""
_LOGGER.info("On lock received") _LOGGER.info("On lock received")
for tracker in self._georide_trackers: for coordoned_tracker in self._georide_trackers_coordoned:
tracker_device = coordoned_tracker['tracker_device']
tracker = tracker_device.tracker
coordinator = coordoned_tracker['coordinator']
if tracker.tracker_id == data['trackerId']: if tracker.tracker_id == data['trackerId']:
tracker.locked_latitude = data['lockedLatitude'] tracker.locked_latitude = data['lockedLatitude']
tracker.locked_longitude = data['lockedLongitude'] tracker.locked_longitude = data['lockedLongitude']
tracker.is_locked = data['isLocked'] tracker.is_locked = data['isLocked']
return
event_data = {
"device_id": tracker_device.unique_id,
"device_name": tracker_device.name,
}
self._hass.bus.async_fire(f"{DOMAIN}_lock_event", event_data)
asyncio.run_coroutine_threadsafe(
coordinator.async_request_refresh(), self._hass.loop
).result()
break
@callback @callback
def on_device_callback(self, data): def on_device_callback(self, data):
"""on device callback""" """on device callback"""
_LOGGER.info("On device received") _LOGGER.info("On device received")
for tracker in self._georide_trackers: for coordoned_tracker in self._georide_trackers_coordoned:
tracker_device = coordoned_tracker['tracker_device']
tracker = tracker_device.tracker
coordinator = coordoned_tracker['coordinator']
if tracker.tracker_id == data['trackerId']: if tracker.tracker_id == data['trackerId']:
tracker.status = data['status'] tracker.status = data['status']
return event_data = {
"device_id": tracker_device.unique_id,
"device_name": tracker_device.name,
}
self._hass.bus.async_fire(f"{DOMAIN}_device_event", event_data)
asyncio.run_coroutine_threadsafe(
coordinator.async_request_refresh(), self._hass.loop
).result()
break
@callback
def on_refresh_tracker_callback(self):
"""on device callback"""
_LOGGER.info("On refresh tracker received")
self._previous_refresh = math.floor(time.time()/60)
self.force_refresh_trackers()
for coordoned_tracker in self._georide_trackers_coordoned:
tracker_device = coordoned_tracker['tracker_device']
tracker = tracker_device.tracker
coordinator = coordoned_tracker['coordinator']
event_data = {
"device_id": tracker_device.unique_id,
"device_name": tracker_device.name,
}
self._hass.bus.async_fire(f"{DOMAIN}_refresh_tracker_event", event_data)
asyncio.run_coroutine_threadsafe(
coordinator.async_request_refresh(), self._hass.loop
).result()
@callback
def on_alarm_callback(self, data):
"""on device callback"""
_LOGGER.info("On alarm received")
for coordoned_tracker in self._georide_trackers_coordoned:
tracker_device = coordoned_tracker['tracker_device']
tracker = tracker_device.tracker
coordinator = coordoned_tracker['coordinator']
if tracker.tracker_id == data['trackerId']:
if data['name'] == 'vibration':
_LOGGER.info("Vibration detected")
elif data['name'] == 'exitZone':
_LOGGER.info("Exit zone detected")
elif data['name'] == 'crash':
_LOGGER.info("Crash detected")
elif data['name'] == 'crashParking':
_LOGGER.info("Crash parking detected")
elif data['name'] == 'deviceOffline':
_LOGGER.info("Device offline detected")
elif data['name'] == 'deviceOnline':
_LOGGER.info("Device online detected")
elif data['name'] == 'powerCut':
_LOGGER.info("powerCut detected")
elif data['name'] == 'powerUncut':
_LOGGER.info("powerUncut detected")
elif data['name'] == 'batteryWarning':
_LOGGER.info("batteryWarning detected")
elif data['name'] == 'temperatureWarning':
_LOGGER.info("temperatureWarning detected")
elif data['name'] == 'magnetOn':
_LOGGER.info("magnetOn detected")
elif data['name'] == 'magnetOff':
_LOGGER.info("magnetOff detected")
elif data['name'] == 'sonorAlarmOn':
_LOGGER.info("sonorAlarmOn detected")
tracker.is_siren_on = True
else:
_LOGGER.warning("Unmanaged alarm: %s", data["name"])
event_data = {
"device_id": tracker_device.unique_id,
"device_name": tracker_device.name,
"type": f"alarm_{data['name']}"
}
self._hass.bus.async_fire(f"{DOMAIN}_alarm_event", event_data)
asyncio.run_coroutine_threadsafe(
coordinator.async_request_refresh(), self._hass.loop
).result()
break
@callback @callback
def on_position_callback(self, data): def on_position_callback(self, data):
"""on position callback""" """on position callback"""
_LOGGER.info("On position received") _LOGGER.info("On position received")
for tracker in self._georide_trackers: for coordoned_tracker in self._georide_trackers_coordoned:
tracker_device = coordoned_tracker['tracker_device']
tracker = tracker_device.tracker
coordinator = coordoned_tracker['coordinator']
if tracker.tracker_id == data['trackerId']: if tracker.tracker_id == data['trackerId']:
tracker.latitude = data['latitude'] tracker.latitude = data['latitude']
tracker.longitude = data['longitude'] tracker.longitude = data['longitude']
tracker.moving = data['moving'] tracker.moving = data['moving']
tracker.speed = data['speed'] tracker.speed = data['speed']
tracker.fixtime = data['fixtime'] tracker.fixtime = data['fixtime']
return
event_data = {
"device_id": tracker_device.unique_id,
"device_name": tracker_device.name,
}
self._hass.bus.async_fire(f"{DOMAIN}_position_event", event_data)
asyncio.run_coroutine_threadsafe(
coordinator.async_request_refresh(), self._hass.loop
).result()
break

View File

@@ -0,0 +1,349 @@
""" binary_sensor for GeoRide object """
import logging
from typing import Any, Mapping
from homeassistant.core import callback
from homeassistant.helpers.entity import DeviceInfo, EntityCategory
from homeassistant.components.binary_sensor import BinarySensorEntity, BinarySensorDeviceClass, ENTITY_ID_FORMAT
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator
)
from .const import DOMAIN as GEORIDE_DOMAIN
from .device import Device, DeviceBeacon
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass, config_entry, async_add_entities): # pylint: disable=W0613
"""Set up GeoRide tracker based off an entry."""
georide_context = hass.data[GEORIDE_DOMAIN]["context"]
entities = []
coordoned_trackers = georide_context.georide_trackers_coordoned
for coordoned_tracker in coordoned_trackers:
tracker_device = coordoned_tracker['tracker_device']
coordinator = coordoned_tracker['coordinator']
entities.append(GeoRideStolenBinarySensorEntity(coordinator, tracker_device))
entities.append(GeoRideCrashedBinarySensorEntity(coordinator, tracker_device))
entities.append(GeoRideOwnerBinarySensorEntity(coordinator, tracker_device))
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
coordoned_beacons = georide_context.georide_trackers_beacon_coordoned
for coordoned_beacon in coordoned_beacons:
tracker_beacon = coordoned_beacon['tracker_beacon']
coordinator = coordoned_beacon['coordinator']
entities.append(GeoRideBeaconUpdatedBinarySensorEntity(coordinator, tracker_beacon))
hass.data[GEORIDE_DOMAIN]["devices"][tracker_beacon.beacon.beacon_id] = coordinator
async_add_entities(entities, True)
return True
class GeoRideBinarySensorEntity(CoordinatorEntity, BinarySensorEntity):
"""Represent a tracked device."""
def __init__(self, coordinator: DataUpdateCoordinator[Mapping[str, Any]],
tracker_device: Device):
"""Set up Georide entity."""
super().__init__(coordinator)
self._tracker_device = tracker_device
self._name = tracker_device.tracker.tracker_name
self.entity_id = f"{ENTITY_ID_FORMAT.format('binary_sensor')}_{tracker_device.tracker.tracker_id}"# pylint: disable=C0301
self._is_on = False
@property
def device_info(self):
"""Return the device info."""
return self._tracker_device.device_info
class GeoRideBeaconBinarySensorEntity(CoordinatorEntity, BinarySensorEntity):
"""Represent a tracked device."""
def __init__(self, coordinator: DataUpdateCoordinator[Mapping[str, Any]],
tracker_device_beacon: DeviceBeacon):
"""Set up Georide entity."""
super().__init__(coordinator)
self._tracker_device_beacon = tracker_device_beacon
self._name = tracker_device_beacon.beacon.name
self.entity_id = f"{ENTITY_ID_FORMAT.format('binary_sensor')}_{tracker_device_beacon.beacon.beacon_id}"# pylint: disable=C0301
self._is_on = False
@property
def entity_category(self):
return None
@property
def device_info(self):
"""Return the device info."""
return self._tracker_device_beacon.device_info
class GeoRideStolenBinarySensorEntity(GeoRideBinarySensorEntity):
"""Represent a tracked device."""
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('is_stolen')}_{tracker_device.tracker.tracker_id}"# pylint: disable=C0301
@property
def unique_id(self):
"""Return the unique ID."""
return f"is_stolen_{self._tracker_device.tracker.tracker_id}"
@property
def device_class(self):
"""Return the device class."""
return BinarySensorDeviceClass.PROBLEM
@property
def is_on(self):
"""state value property"""
return self._tracker_device.tracker.is_stolen
@property
def name(self):
""" GeoRide odometer name """
return f"{self._name} is not stolen"
class GeoRideCrashedBinarySensorEntity(GeoRideBinarySensorEntity):
"""Represent a tracked device."""
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('is_crashed')}_{tracker_device.tracker.tracker_id}"# pylint: disable=C0301
@property
def unique_id(self):
"""Return the unique ID."""
return f"is_crashed_{self._tracker_device.tracker.tracker_id}"
@property
def device_class(self):
"""Return the device class."""
return BinarySensorDeviceClass.PROBLEM
@property
def is_on(self):
"""state value property"""
return self._tracker_device.tracker.is_crashed
@property
def name(self):
""" GeoRide odometer name """
return f"{self._name} is not crashed"
class GeoRideActiveSubscriptionBinarySensorEntity(GeoRideBinarySensorEntity):
"""Represent a tracked device."""
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('is_active_subscription')}_{tracker_device.tracker.tracker_id}"# pylint: disable=C0301
@property
def entity_category(self):
return EntityCategory.DIAGNOSTIC
@property
def unique_id(self):
"""Return the unique ID."""
return f"is_active_subscription_{self._tracker_device.tracker.tracker_id}"
@property
def is_on(self):
"""state value property"""
tracker = self._tracker_device.tracker
if tracker.is_oldsubscription:
if tracker.subscription_id is not None:
return True
return False
else:
if tracker.subscription is not None and tracker.subscription.subscription_id is not None:
return True
return False
@property
def name(self):
""" GeoRide odometer name """
return f"{self._name} has an active subscription"
class GeoRideOwnerBinarySensorEntity(GeoRideBinarySensorEntity):
"""Represent a tracked device."""
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('is_owner')}_{tracker_device.tracker.tracker_id}"# pylint: disable=C0301
@property
def unique_id(self):
"""Return the unique ID."""
return f"is_owner_{self._tracker_device.tracker.tracker_id}"
@property
def is_on(self):
"""state value property"""
if self._tracker_device.tracker.role == "owner":
return True
return False
@property
def name(self):
""" GeoRide odometer name """
return f"{self._name} is own tracker"
class GeoRideNetworkBinarySensorEntity(GeoRideBinarySensorEntity):
"""Represent a tracked device."""
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('have_network')}_{tracker_device.tracker.tracker_id}"# pylint: disable=C0301
@property
def entity_category(self):
return EntityCategory.DIAGNOSTIC
@property
def unique_id(self):
"""Return the unique ID."""
return f"have_network_{self._tracker_device.tracker.tracker_id}"
@property
def device_class(self):
"""Return the device class."""
return "connectivity"
@property
def is_on(self):
"""state value property"""
if self._tracker_device.tracker.status == "online":
return True
return False
@property
def device_class(self) -> str:
"""device class"""
return BinarySensorDeviceClass.CONNECTIVITY
@property
def name(self):
""" GeoRide name """
return f"{self._name} have network"
class GeoRideMovingBinarySensorEntity(GeoRideBinarySensorEntity):
"""Represent a tracked device."""
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('moving')}_{tracker_device.tracker.tracker_id}"# pylint: disable=C0301
@property
def entity_category(self):
return EntityCategory.DIAGNOSTIC
@property
def unique_id(self):
"""Return the unique ID."""
return f"moving_{self._tracker_device.tracker.tracker_id}"
@property
def device_class(self):
"""Return the device class."""
return "moving"
@property
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
def entity_category(self):
return EntityCategory.DIAGNOSTIC
def __init__(self, coordinator: DataUpdateCoordinator[Mapping[str, Any]],
tracker_beacon_device: DeviceBeacon):
"""Set up Georide entity."""
super().__init__(coordinator, tracker_beacon_device)
self.entity_id = f"{ENTITY_ID_FORMAT.format('update')}_{tracker_beacon_device.beacon.beacon_id}"# pylint: disable=C0301
@property
def unique_id(self):
"""Return the unique ID."""
return f"update_{self._tracker_device_beacon.beacon.beacon_id}"
@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):
""" GeoRide name """
return f"{self._name} have an update"

View File

@@ -3,21 +3,21 @@
import logging import logging
from homeassistant import config_entries from homeassistant import config_entries
import voluptuous as vol import voluptuous as vol
import georideapilib.api as GeorideApi import georideapilib.api as GeoRideApi
import georideapilib.exception as GeorideException import georideapilib.exception as GeoRideException
from .const import CONF_EMAIL, CONF_PASSWORD, CONF_TOKEN from .const import CONF_EMAIL, CONF_PASSWORD, CONF_TOKEN, DOMAIN
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@config_entries.HANDLERS.register("georide") @config_entries.HANDLERS.register(DOMAIN)
class GeorideConfigFlow(config_entries.ConfigFlow): class GeoRideConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Georide config flow """ """GeoRide config flow """
async def async_step_user(self, user_input=None): #pylint: disable=W0613 async def async_step_user(self, user_input=None): #pylint: disable=W0613
""" handle info to help to configure georide """ """ handle info to help to configure GeoRide """
if self._async_current_entries(): if self._async_current_entries():
return self.async_abort(reason="one_instance_allowed") return self.async_abort(reason="one_instance_allowed")
@@ -37,31 +37,34 @@ class GeorideConfigFlow(config_entries.ConfigFlow):
vol.Required(CONF_PASSWORD): vol.All(str) vol.Required(CONF_PASSWORD): vol.All(str)
})) }))
async def async_step_georide_login(self, user_input): async def async_step_georide_login(self, user_input):
""" try to seupt GeoRide Account """ """ try to seupt GeoRide Account """
errors = {}
schema = vol.Schema({
vol.Required(CONF_EMAIL): vol.All(str, vol.Length(min=3)),
vol.Required(CONF_PASSWORD): vol.All(str)
})
if user_input is None:
return self.async_show_form(step_id='georide_login', data_schema=schema)
email = user_input[CONF_EMAIL]
password = user_input[CONF_PASSWORD]
try: try:
account = GeorideApi.get_authorisation_token( account = await self.hass.async_add_executor_job(GeoRideApi.get_authorisation_token, email, password)
user_input[CONF_EMAIL], data = {
user_input[CONF_PASSWORD]) CONF_EMAIL: email,
return self.async_create_entry( CONF_PASSWORD: password,
title=user_input[CONF_EMAIL], CONF_TOKEN: account.auth_token
data={ }
CONF_EMAIL: user_input[CONF_EMAIL], return self.async_create_entry(title=email, data=data)
CONF_PASSWORD: user_input[CONF_PASSWORD], except (GeoRideException.SeverException, GeoRideException.LoginException):
CONF_TOKEN: account.auth_token
}
)
except (GeorideException.SeverException, GeorideException.LoginException):
_LOGGER.error("Invalid credentials provided, config not created") _LOGGER.error("Invalid credentials provided, config not created")
errors["base"] = "faulty_credentials" errors = {"base": "faulty_credentials"}
return self.async_show_form(step_id="georide_login", errors=errors) return self.async_show_form(step_id="georide_login", data_schema=schema, errors=errors)
except: except:
_LOGGER.error("Unknown error") _LOGGER.error("Unknown error")
errors["base"] = "faulty_credentials" errors = {"base": "unkonwn"}
return self.async_show_form(step_id="georide_login", errors=errors) return self.async_show_form(step_id="georide_login", data_schema=schema, errors=errors)

View File

@@ -1,4 +1,4 @@
""" Georide const file """ """ GeoRide const file """
DOMAIN = "georide" DOMAIN = "georide"
CONF_EMAIL = "email" CONF_EMAIL = "email"
@@ -8,4 +8,8 @@ CONF_TOKEN = "token"
TRACKER_ID = "trackerId" TRACKER_ID = "trackerId"
MIN_UNTIL_REFRESH = 10
SIREN_ACTIVATION_DELAY = 30
TOKEN_SAFE_DAY = 432000 # five days TOKEN_SAFE_DAY = 432000 # five days

View File

@@ -0,0 +1,162 @@
"""Home Assistant representation of an GeoRide Tracker device."""
from georideapilib.objects import GeoRideTracker, GeoRideTrackerBeacon
from .const import DOMAIN as GEORIDE_DOMAIN
class Device:
"""Home Assistant representation of a GeoRide Tracker device."""
def __init__(self, tracker):
"""Initialize GeoRideTracker device."""
self._tracker: GeoRideTracker = tracker
@property
def tracker(self):
"""return the tracker"""
return self._tracker
@property
def name(self) -> str:
"""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."""
name = None
if self._tracker.version == 1:
name = "GeoRide 1"
elif self._tracker.version == 2:
name = "GeoRide 2"
elif self._tracker.version == 3:
if self._tracker.model == 'georide-3':
name = "GeoRide 3"
elif self._tracker.model == 'georide-3s':
name = "GeoRide 3S"
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):
"""Return the device info."""
return {
"name": self.name,
"identifiers": self.unique_id,
"manufacturer": self.manufacturer,
"model": self.model_name,
"suggested_area": self.suggested_area,
"sw_version" : self.sw_version,
"hw_version": self.hw_version
}
def __str__(self) -> str:
"""Get string representation."""
return f"GeoRide Device: {self.name}::{self.model_name}::{self.unique_id}"
class DeviceBeacon:
"""Home Assistant representation of a GeoRide Tracker device."""
def __init__(self, beacon):
"""Initialize GeoRideTracker device."""
self._beacon: GeoRideTrackerBeacon = beacon
@property
def beacon(self):
"""return the tracker beacon"""
return self._beacon
@property
def name(self) -> str:
"""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:
"""Get the model name."""
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.linked_tracker_id )
@property
def device_info(self):
"""Return the device info."""
return {
"name": self.name,
"identifiers": self.unique_id,
"manufacturer": self.manufacturer,
"model": self.model_name,
"suggested_area": self.suggested_area,
"via_device": self.via_device
}
def __str__(self) -> str:
"""Get string representation."""
return f"GeoRide Device: {self.name}::{self.model_name}::{self.unique_id}"

View File

@@ -1,12 +1,17 @@
""" device tracker for Georide object """ """ device tracker for GeoRide object """
import logging import logging
from typing import Any, Mapping
from homeassistant.components.device_tracker.const import ENTITY_ID_FORMAT, SOURCE_TYPE_GPS from homeassistant.components.device_tracker.const import DOMAIN, SourceType
from homeassistant.components.device_tracker.config_entry import TrackerEntity from homeassistant.components.device_tracker.config_entry import TrackerEntity
import georideapilib.api as GeorideApi from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from .device import Device
from .const import DOMAIN as GEORIDE_DOMAIN from .const import DOMAIN as GEORIDE_DOMAIN
@@ -14,72 +19,66 @@ _LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass, config_entry, async_add_entities): # pylint: disable=W0613 async def async_setup_entry(hass, config_entry, async_add_entities): # pylint: disable=W0613
"""Set up Georide tracker based off an entry.""" """Set up Georide tracker based off an entry."""
georide_context = hass.data[GEORIDE_DOMAIN]["context"] georide_context = hass.data[GEORIDE_DOMAIN]["context"]
coordoned_trackers = georide_context.georide_trackers_coordoned
if georide_context.get_token() is None: entities = []
return False for coordoned_tracker in coordoned_trackers:
tracker_device = coordoned_tracker['tracker_device']
coordinator = coordoned_tracker['coordinator']
entity = GeoRideTrackerEntity(coordinator, tracker_device, hass)
hass.data[GEORIDE_DOMAIN]["devices"][tracker_device.tracker.tracker_id] = coordinator
entities.append(entity)
_LOGGER.info('Current georide token: %s', georide_context.get_token()) async_add_entities(entities)
trackers = GeorideApi.get_trackers(georide_context.get_token())
tracker_entities = []
for tracker in trackers:
entity = GeorideTrackerEntity(tracker.tracker_id, georide_context.get_token,
georide_context.get_tracker, tracker)
hass.data[GEORIDE_DOMAIN]["devices"][tracker.tracker_id] = entity
tracker_entities.append(entity)
async_add_entities(tracker_entities)
return True return True
class GeorideTrackerEntity(TrackerEntity): class GeoRideTrackerEntity(CoordinatorEntity, TrackerEntity):
"""Represent a tracked device.""" """Represent a tracked device."""
def __init__(self, tracker_id, get_token_callback, get_tracker_callback, tracker): def __init__(self, coordinator: DataUpdateCoordinator[Mapping[str, Any]],
"""Set up Georide entity.""" tracker_device: Device, hass):
self._tracker_id = tracker_id """Set up GeoRide entity."""
self._get_token_callback = get_token_callback super().__init__(coordinator)
self._get_tracker_callback = get_tracker_callback self._name = tracker_device.tracker.tracker_name
self._name = tracker.tracker_name self._tracker_device = tracker_device
self._data = tracker or {} self.entity_id = DOMAIN + ".{}".format(tracker_device.tracker.tracker_id)
self.entity_id = ENTITY_ID_FORMAT.format(tracker_id) self._hass = hass
@property
def entity_category(self):
return None
@property @property
def unique_id(self): def unique_id(self):
"""Return the unique ID.""" """Return the unique ID."""
return self._tracker_id return f"georide_tracker_{self._tracker_device.tracker.tracker_id}"
@property @property
def name(self): def name(self):
return self._name """ GeoRide odometer name """
return f"{self._name} position"
@property @property
def latitude(self): def latitude(self):
"""Return latitude value of the device.""" """Return latitude value of the device."""
if self._data.latitude: if self._tracker_device.tracker.latitude:
return self._data.latitude return self._tracker_device.tracker.latitude
return None return None
@property @property
def longitude(self): def longitude(self):
"""Return longitude value of the device.""" """Return longitude value of the device."""
if self._data.longitude: if self._tracker_device.tracker.longitude:
return self._data.longitude return self._tracker_device.tracker.longitude
return None return None
@property @property
def source_type(self): def source_type(self):
"""Return the source type, eg gps or router, of the device.""" """Return the source type, eg gps or router, of the device."""
return SOURCE_TYPE_GPS return SourceType.GPS
@property @property
def location_accuracy(self): def location_accuracy(self):
@@ -88,38 +87,10 @@ class GeorideTrackerEntity(TrackerEntity):
@property @property
def icon(self): def icon(self):
"""return the entity icon"""
return "mdi:map-marker" return "mdi:map-marker"
@property @property
def device_info(self): def device_info(self):
"""Return the device info.""" """Return the device info."""
return { return self._tracker_device.device_info
"name": self.name,
"identifiers": {(GEORIDE_DOMAIN, self._tracker_id)},
"manufacturer": "GeoRide",
"odometer": "{} km".format(self._data.odometer)
}
@property
def get_tracker_callback(self):
""" get tracker callaback"""
return self._get_tracker_callback
@property
def get_token_callback(self):
""" get token callaback"""
return self._get_token_callback
@property
def should_poll(self):
"""No polling needed."""
return True
async def async_update(self):
""" update the current tracker"""
self._data = self._get_tracker_callback(self._tracker_id)
self._name = self._data.tracker_name
return

View File

@@ -1,12 +1,15 @@
{ {
"domain": "georide", "domain": "georide",
"name": "Georide", "name": "GeoRide",
"config_flow": true, "config_flow": true,
"documentation": "https://git.tontontux.fr/mduval/GeorideHA", "documentation": "https://github.com/ptimatth/GeorideHA",
"issue_tracker": "https://github.com/ptimatth/GeorideHA/issues",
"iot_class": "cloud_polling",
"requirements": [ "requirements": [
"georideapilib>=0.4.3", "georideapilib>=1.0.0",
"pyjwt>=1.7.1" "pyjwt>=2.2.0"
], ],
"dependencies": [], "dependencies": [],
"codeowners": ["@ptimatth"] "codeowners": ["ptimatth"],
"version": "1.1.2"
} }

View File

@@ -1,102 +1,411 @@
""" odometter sensor for Georide object """ """ odometter sensor for GeoRide object """
import logging import logging
from typing import Any, Mapping
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.components.switch import SwitchDevice from homeassistant.helpers.entity import DeviceInfo, EntityCategory
from homeassistant.components.switch import ENTITY_ID_FORMAT from homeassistant.components.sensor import SensorEntity, SensorDeviceClass, ENTITY_ID_FORMAT
from homeassistant.helpers.update_coordinator import (
import georideapilib.api as GeorideApi CoordinatorEntity,
DataUpdateCoordinator,
)
from .const import DOMAIN as GEORIDE_DOMAIN from .const import DOMAIN as GEORIDE_DOMAIN
from .device import Device, DeviceBeacon
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass, config_entry, async_add_entities): # pylint: disable=W0613 async def async_setup_entry(hass, config_entry, async_add_entities): # pylint: disable=W0613
"""Set up Georide tracker based off an entry.""" """Set up GeoRide tracker based off an entry."""
georide_context = hass.data[GEORIDE_DOMAIN]["context"] georide_context = hass.data[GEORIDE_DOMAIN]["context"]
coordoned_trackers = georide_context.georide_trackers_coordoned
if georide_context.get_token() is None: entities = []
return False for coordoned_tracker in coordoned_trackers:
tracker_device = coordoned_tracker['tracker_device']
coordinator = coordoned_tracker['coordinator']
hass.data[GEORIDE_DOMAIN]["devices"][tracker_device.tracker.tracker_id] = coordinator
entities.append(GeoRideOdometerSensorEntity(coordinator, tracker_device, hass))
entities.append(GeoRideOdometerKmSensorEntity(coordinator, tracker_device, hass))
entities.append(GeoRideSpeedSensorEntity(coordinator, tracker_device,hass))
entities.append(GeoRideFixtimeSensorEntity(coordinator, tracker_device))
if tracker_device.tracker.version > 2:
entities.append(GeoRideInternalBatterySensorEntity(coordinator, tracker_device))
entities.append(GeoRideExternalBatterySensorEntity(coordinator, tracker_device))
trackers = GeorideApi.get_trackers(georide_context.get_token()) coordoned_beacons = georide_context.georide_trackers_beacon_coordoned
for coordoned_beacon in coordoned_beacons:
tracker_beacon = coordoned_beacon['tracker_beacon']
coordinator = coordoned_beacon['coordinator']
entities.append(GeoRideBeaconBatterySensorEntity(coordinator, tracker_beacon))
hass.data[GEORIDE_DOMAIN]["devices"][tracker_beacon.beacon.beacon_id] = coordinator
odometer_switch_entities = [] async_add_entities(entities)
for tracker in trackers:
entity = GeorideOdometerSensorEntity(tracker.tracker_id, georide_context.get_token,
georide_context.get_tracker, data=tracker)
hass.data[GEORIDE_DOMAIN]["devices"][tracker.tracker_id] = entity
odometer_switch_entities.append(entity)
async_add_entities(odometer_switch_entities)
return True return True
class GeorideOdometerSensorEntity(SwitchDevice): class GeoRideOdometerSensorEntity(CoordinatorEntity, SensorEntity):
"""Represent a tracked device.""" """Represent a tracked device."""
def __init__(self, tracker_id, get_token_callback, get_tracker_callback, data): def __init__(self, coordinator: DataUpdateCoordinator[Mapping[str, Any]],
"""Set up Georide entity.""" tracker_device:Device, hass):
self._tracker_id = tracker_id """Set up GeoRide entity."""
self._data = data or {} super().__init__(coordinator)
self._get_token_callback = get_token_callback self._tracker_device = tracker_device
self._get_tracker_callback = get_tracker_callback self._name = tracker_device.tracker.tracker_name
self._name = data.tracker_name
self._unit_of_measurement = "m" self._unit_of_measurement = "m"
self.entity_id = f"{ENTITY_ID_FORMAT.format('odometer')}_{tracker_device.tracker.tracker_id}"# pylint: disable=C0301
self.entity_id = ENTITY_ID_FORMAT.format("odometer") + "." + str(tracker_id)
self._state = 0 self._state = 0
self._hass = hass
@property
def update(self): def entity_category(self):
""" update the current tracker""" return None
_LOGGER.info('async_update ')
self._data = self._get_tracker_callback(self._tracker_id)
self._name = self._data.tracker_name
self._state = self._data.odometer
@property @property
def unique_id(self): def unique_id(self):
"""Return the unique ID.""" """Return the unique ID."""
return self._tracker_id return f"odometer_{self._tracker_device.tracker.tracker_id}".lower()
@property
def name(self):
""" Georide switch name """
return self._name
@property @property
def state(self): def state(self):
return self._state """state property"""
odometer = self._tracker_device.tracker.odometer
return odometer
@property @property
def unit_of_measurement(self): def unit_of_measurement(self):
"""unit of mesurment property"""
return self._unit_of_measurement return self._unit_of_measurement
@property @property
def get_token_callback(self): def name(self):
""" Georide switch token callback method """ """ GeoRide odometer name """
return self._get_token_callback return f"{self._name} odometer"
@property
def get_tracker_callback(self):
""" Georide switch token callback method """
return self._get_tracker_callback
@property @property
def icon(self): def icon(self):
"""icon getter"""
return "mdi:counter" return "mdi:counter"
@property
def device_info(self) -> DeviceInfo:
"""Return the device info."""
return self._tracker_device.device_info
class GeoRideOdometerKmSensorEntity(CoordinatorEntity, SensorEntity):
"""Represent a tracked device."""
def __init__(self, coordinator: DataUpdateCoordinator[Mapping[str, Any]],
tracker_device:Device, hass):
"""Set up GeoRide entity."""
super().__init__(coordinator)
self._tracker_device = tracker_device
self._name = tracker_device.tracker.tracker_name
self._unit_of_measurement = "km"
self.entity_id = f"{ENTITY_ID_FORMAT.format('odometer_km')}_{tracker_device.tracker.tracker_id}"# pylint: disable=C0301
self._state = 0
self._hass = hass
@property @property
def device_info(self): def unique_id(self):
"""Return the unique ID."""
return f"odometer_km_{self._tracker_device.tracker.tracker_id}".lower()
@property
def state(self):
"""state property"""
odometer = self._tracker_device.tracker.odometer // 1000
return odometer
@property
def unit_of_measurement(self):
"""unit of mesurment property"""
return self._unit_of_measurement
@property
def name(self):
""" GeoRide odometer name """
return f"{self._name} odometer km"
@property
def icon(self):
"""icon getter"""
return "mdi:counter"
@property
def device_info(self) -> DeviceInfo:
"""Return the device info.""" """Return the device info."""
return { return self._tracker_device.device_info
"name": self.name,
"identifiers": {(GEORIDE_DOMAIN, self._tracker_id)},
"manufacturer": "GeoRide"
}
class GeoRideSpeedSensorEntity(CoordinatorEntity, SensorEntity):
"""Represent a tracked device."""
def __init__(self, coordinator: DataUpdateCoordinator[Mapping[str, Any]],
tracker_device:Device, hass):
"""Set up GeoRide entity."""
super().__init__(coordinator)
self._tracker_device = tracker_device
self._name = tracker_device.tracker.tracker_name
self._unit_of_measurement = "km/h"
self.entity_id = f"{ENTITY_ID_FORMAT.format('speed')}_{tracker_device.tracker.tracker_id}"# pylint: disable=C0301
self._state = 0
self._hass = hass
self._state_class = "measurement"
@property
def unique_id(self):
"""Return the unique ID."""
return f"speed_{self._tracker_device.tracker.tracker_id}"
@property
def state(self):
"""state property"""
return self._tracker_device.tracker.speed
@property
def state_class(self):
return self._state_class
@property
def unit_of_measurement(self):
"""unit of mesurment property"""
return self._unit_of_measurement
@property
def name(self):
""" GeoRide odometer name """
return f"{self._name} speed"
@property
def icon(self):
"""icon getter"""
return "mdi:speedometer"
@property
def device_info(self) -> DeviceInfo:
"""Return the device info."""
return self._tracker_device.device_info
class GeoRideInternalBatterySensorEntity(CoordinatorEntity, SensorEntity):
"""Represent a tracked device."""
entity_category = EntityCategory.DIAGNOSTIC
def __init__(self, coordinator: DataUpdateCoordinator[Mapping[str, Any]],
tracker_device:Device):
"""Set up GeoRide entity."""
super().__init__(coordinator)
self._tracker_device = tracker_device
self._name = tracker_device.tracker.tracker_name
self._unit_of_measurement = "V"
self.entity_id = f"{ENTITY_ID_FORMAT.format('internal_battery_voltage')}_{tracker_device.tracker.tracker_id}"# pylint: disable=C0301
self._state = 0
self._state_class = "measurement"
self._device_class = "voltage"
@property
def state_class(self):
return self._state_class
@property
def device_class(self):
return self._device_class
@property
def entity_category(self):
return EntityCategory.DIAGNOSTIC
@property
def unique_id(self):
"""Return the unique ID."""
return f"internal_battery_voltage_{self._tracker_device.tracker.tracker_id}"
@property
def state(self):
"""state property"""
return self._tracker_device.tracker.internal_battery_voltage
@property
def unit_of_measurement(self):
"""unit of mesurment property"""
return self._unit_of_measurement
@property
def name(self):
""" GeoRide internal_battery_voltage name """
return f"{self._name} internal_battery_voltage"
@property
def icon(self):
"""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."""
return self._tracker_device.device_info
class GeoRideExternalBatterySensorEntity(CoordinatorEntity, SensorEntity):
"""Represent a tracked device."""
def __init__(self, coordinator: DataUpdateCoordinator[Mapping[str, Any]],
tracker_device:Device):
"""Set up GeoRide entity."""
super().__init__(coordinator)
self._tracker_device = tracker_device
self._name = tracker_device.tracker.tracker_name
self._unit_of_measurement = "V"
self.entity_id = f"{ENTITY_ID_FORMAT.format('external_battery_voltage')}_{tracker_device.tracker.tracker_id}"# pylint: disable=C0301
self._state = 0
self._state_class = "measurement"
self._device_class = "voltage"
@property
def state_class(self):
return self._state_class
@property
def device_class(self):
return self._device_class
@property
def entity_category(self):
return EntityCategory.DIAGNOSTIC
@property
def unique_id(self):
"""Return the unique ID."""
return f"external_battery_voltage_{self._tracker_device.tracker.tracker_id}"
@property
def state(self):
"""state property"""
return self._tracker_device.tracker.external_battery_voltage
@property
def unit_of_measurement(self):
"""unit of mesurment property"""
return self._unit_of_measurement
@property
def name(self):
""" GeoRide internal_battery_voltage name """
return f"{self._name} external_battery_voltage"
@property
def icon(self):
"""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."""
return self._tracker_device.device_info
class GeoRideFixtimeSensorEntity(CoordinatorEntity, SensorEntity):
"""Represent a tracked device."""
def __init__(self, coordinator: DataUpdateCoordinator[Mapping[str, Any]],
tracker_device:Device):
"""Set up GeoRide entity."""
super().__init__(coordinator)
self._tracker_device = tracker_device
self._name = tracker_device.tracker.tracker_name
self.entity_id = f"{ENTITY_ID_FORMAT.format('fixtime')}_{tracker_device.tracker.tracker_id}"# pylint: disable=C0301
self._state = 0
self._device_class = "timestamp"
@property
def entity_category(self):
return EntityCategory.DIAGNOSTIC
@property
def unique_id(self):
"""Return the unique ID."""
return f"fixtime_{self._tracker_device.tracker.tracker_id}"
@property
def state(self):
"""state property"""
return self._tracker_device.tracker.fixtime
@property
def name(self):
""" GeoRide fixtime name """
return f"{self._name} last fixed position"
@property
def icon(self):
"""icon getter"""
return "mdi:map-clock"
@property
def device_info(self) -> DeviceInfo:
"""Return the device info."""
return self._tracker_device.device_info
class GeoRideBeaconBatterySensorEntity(CoordinatorEntity, SensorEntity):
"""Represent a tracked device."""
def __init__(self, coordinator: DataUpdateCoordinator[Mapping[str, Any]],
tracker_beacon: DeviceBeacon):
"""Set up GeoRide entity."""
super().__init__(coordinator)
self._tracker_device = tracker_beacon
self._name = tracker_beacon.beacon.name
self._unit_of_measurement = "%"
self.entity_id = f"{ENTITY_ID_FORMAT.format('battery_percent')}_{tracker_beacon.beacon.beacon_id}"# pylint: disable=C0301
self._state = 0
@property
def entity_category(self):
return EntityCategory.DIAGNOSTIC
@property
def unique_id(self):
"""Return the unique ID."""
return f"battery_percent_{self._tracker_device.beacon.beacon_id}"
@property
def state(self):
"""state property"""
return self._tracker_device.beacon.battery_level
@property
def unit_of_measurement(self):
"""unit of mesurment property"""
return self._unit_of_measurement
@property
def name(self):
""" GeoRide internal_battery_voltage name """
return f"{self._name} battery percent"
@property
def icon(self):
"""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."""
return self._tracker_device.device_info

View File

@@ -0,0 +1,96 @@
""" device tracker for GeoRide object """
import logging
from typing import Any, Mapping
from homeassistant.components.siren import SirenEntity
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
import georideapilib.api as GeoRideApi
from .const import DOMAIN as GEORIDE_DOMAIN
from .device import Device
ENTITY_ID_FORMAT = GEORIDE_DOMAIN + ".{}"
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass, config_entry, async_add_entities): # pylint: disable=W0613
"""Set up GeoRide tracker based off an entry."""
georide_context = hass.data[GEORIDE_DOMAIN]["context"]
coordoned_trackers = georide_context.georide_trackers_coordoned
entities = []
for coordoned_tracker in coordoned_trackers:
tracker_device = coordoned_tracker['tracker_device']
coordinator = coordoned_tracker['coordinator']
hass.data[GEORIDE_DOMAIN]["devices"][tracker_device.tracker.tracker_id] = coordinator
if tracker_device.tracker.version > 2:
entities.append(GeoRideSirenEntity(coordinator, tracker_device, hass))
async_add_entities(entities)
return True
class GeoRideSirenEntity(CoordinatorEntity, SirenEntity):
"""Represent a tracked device."""
def __init__(self, coordinator: DataUpdateCoordinator[Mapping[str, Any]],
tracker_device:Device, hass):
"""Set up GeoRide entity."""
super().__init__(coordinator)
self._tracker_device = tracker_device
self._name = tracker_device.tracker.tracker_name
self.entity_id = f"{ENTITY_ID_FORMAT.format('eco_mode')}_{tracker_device.tracker.tracker_id}"# pylint: disable=C0301
self._hass = hass
@property
def entity_category(self):
return None
async def async_turn_on(self, **kwargs):
""" lock the GeoRide tracker """
_LOGGER.info('async_turn_on eco %s', kwargs)
georide_context = self._hass.data[GEORIDE_DOMAIN]["context"]
token = await georide_context.get_token()
success = await self._hass.async_add_executor_job(GeoRideApi.change_tracker_siren_state,
token, self._tracker_device.tracker.tracker_id, True)
if success:
self._tracker_device.tracker.is_siren_on = True
async def async_turn_off(self, **kwargs):
""" unlock the GeoRide tracker """
_LOGGER.info('async_turn_off eco %s', kwargs)
georide_context = self._hass.data[GEORIDE_DOMAIN]["context"]
token = await georide_context.get_token()
success = await self._hass.async_add_executor_job(GeoRideApi.change_tracker_siren_state,
token, self._tracker_device.tracker.tracker_id, False)
if success:
self._tracker_device.tracker.is_siren_on = False
@property
def unique_id(self):
"""Return the unique ID."""
return f"siren_{self._tracker_device.tracker.tracker_id}"
@property
def name(self):
""" GeoRide odometer name """
return f"{self._name} siren"
@property
def is_on(self):
""" GeoRide switch status """
return self._tracker_device.tracker.is_siren_on
@property
def device_info(self):
"""Return the device info."""
return self._tracker_device.device_info

View File

@@ -4,7 +4,7 @@
"step": { "step": {
"georide_login": { "georide_login": {
"title": "Set up GeoRide", "title": "Set up GeoRide",
"description": "Sign-in to georide", "description": "Sign-in to GeoRide",
"data": { "data": {
"email": "email", "email": "email",
"password": "password" "password": "password"

View File

@@ -1,129 +1,175 @@
""" device tracker for Georide object """ """ device tracker for GeoRide object """
import logging import logging
from homeassistant.core import callback
from homeassistant.components.switch import SwitchDevice from typing import Any, Mapping
from homeassistant.components.switch import SwitchEntity
from homeassistant.components.switch import ENTITY_ID_FORMAT from homeassistant.components.switch import ENTITY_ID_FORMAT
import georideapilib.api as GeorideApi from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
import georideapilib.api as GeoRideApi
from .const import DOMAIN as GEORIDE_DOMAIN from .const import DOMAIN as GEORIDE_DOMAIN
from .device import Device
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass, config_entry, async_add_entities): # pylint: disable=W0613 async def async_setup_entry(hass, config_entry, async_add_entities): # pylint: disable=W0613
"""Set up Georide tracker based off an entry.""" """Set up GeoRide tracker based off an entry."""
georide_context = hass.data[GEORIDE_DOMAIN]["context"] georide_context = hass.data[GEORIDE_DOMAIN]["context"]
coordoned_trackers = georide_context.georide_trackers_coordoned
if georide_context.get_token() is None: entities = []
return False for coordoned_tracker in coordoned_trackers:
tracker_device = coordoned_tracker['tracker_device']
coordinator = coordoned_tracker['coordinator']
hass.data[GEORIDE_DOMAIN]["devices"][tracker_device.tracker.tracker_id] = coordinator
entities.append(GeoRideLockSwitchEntity(coordinator, tracker_device, hass))
if tracker_device.tracker.version > 2:
entities.append(GeoRideEcoModeSwitchEntity(coordinator, tracker_device, hass))
async_add_entities(entities)
_LOGGER.info('Current georide token: %s', georide_context.get_token())
trackers = GeorideApi.get_trackers(georide_context.get_token())
lock_switch_entities = []
for tracker in trackers:
entity = GeorideLockSwitchEntity(tracker.tracker_id, georide_context.get_token,
georide_context.get_tracker, data=tracker)
hass.data[GEORIDE_DOMAIN]["devices"][tracker.tracker_id] = entity
lock_switch_entities.append(entity)
async_add_entities(lock_switch_entities)
return True return True
class GeoRideLockSwitchEntity(CoordinatorEntity, SwitchEntity):
class GeorideLockSwitchEntity(SwitchDevice):
"""Represent a tracked device.""" """Represent a tracked device."""
def __init__(self, tracker_id, get_token_callback, get_tracker_callback, data): def __init__(self, coordinator: DataUpdateCoordinator[Mapping[str, Any]],
"""Set up Georide entity.""" tracker_device:Device, hass):
self._tracker_id = tracker_id """Set up GeoRide entity."""
self._data = data or {} super().__init__(coordinator)
self._get_token_callback = get_token_callback self._tracker_device = tracker_device
self._get_tracker_callback = get_tracker_callback self._name = tracker_device.tracker.tracker_name
self._name = data.tracker_name self.entity_id = f"{ENTITY_ID_FORMAT.format('lock')}_{tracker_device.tracker.tracker_id}"# pylint: disable=C0301
self._is_on = data.is_locked self._hass = hass
self.entity_id = ENTITY_ID_FORMAT.format("lock") +"." + str(tracker_id)
self._state = {}
@property
def entity_category(self):
return None
def turn_on(self, **kwargs): async def async_turn_on(self, **kwargs):
""" lock the georide tracker """ """ lock the GeoRide tracker """
_LOGGER.info('async_turn_on %s', kwargs) _LOGGER.info('async_turn_on %s', kwargs)
success = GeorideApi.lock_tracker(self._get_token_callback(), self._tracker_id) georide_context = self._hass.data[GEORIDE_DOMAIN]["context"]
token = await georide_context.get_token()
success = await self._hass.async_add_executor_job(GeoRideApi.lock_tracker,
token, self._tracker_device.tracker.tracker_id)
if success: if success:
self._data.is_locked = True self._tracker_device.tracker.is_locked = True
self._is_on = True
def turn_off(self, **kwargs): async def async_turn_off(self, **kwargs):
""" unlock the georide tracker """ """ unlock the GeoRide tracker """
_LOGGER.info('async_turn_off %s', kwargs) _LOGGER.info('async_turn_off %s', kwargs)
success = GeorideApi.unlock_tracker(self._get_token_callback(), self._tracker_id) georide_context = self._hass.data[GEORIDE_DOMAIN]["context"]
token = await georide_context.get_token()
success = await self._hass.async_add_executor_job(GeoRideApi.unlock_tracker,
token, self._tracker_device.tracker.tracker_id)
if success: if success:
self._data.is_locked = False self._tracker_device.tracker.is_locked = False
self._is_on = False
async def async_toggle(self, **kwargs): async def async_toggle(self, **kwargs):
""" toggle lock the georide tracker """ """ toggle lock the georide tracker """
_LOGGER.info('async_toggle %s', kwargs) _LOGGER.info('async_toggle %s', kwargs)
result = GeorideApi.toogle_lock_tracker(self._get_token_callback(), georide_context = self._hass.data[GEORIDE_DOMAIN]["context"]
self._tracker_id) token = await georide_context.get_token()
self._data.is_locked = result result = await self._hass.async_add_executor_job(GeoRideApi.toogle_lock_tracker,
self._is_on = result token, self._tracker_device.tracker.tracker_id)
self._tracker_device.tracker.is_locked = result
def update(self):
""" update the current tracker"""
_LOGGER.info('async_update ')
self._data = self._get_tracker_callback(self._tracker_id)
self._name = self._data.tracker_name
self._is_on = self._data.is_locked
@property @property
def unique_id(self): def unique_id(self):
"""Return the unique ID.""" """Return the unique ID."""
return self._tracker_id return f"lock_{self._tracker_device.tracker.tracker_id}"
@property @property
def name(self): def name(self):
""" Georide switch name """ """ GeoRide odometer name """
return self._name return f"{self._name} lock"
@property @property
def is_on(self): def is_on(self):
""" Georide switch status """ """ GeoRide switch status """
return self._is_on return self._tracker_device.tracker.is_locked
@property
def get_token_callback(self):
""" Georide switch token callback method """
return self._get_token_callback
@property
def get_tracker_callback(self):
""" Georide switch token callback method """
return self._get_tracker_callback
@property @property
def icon(self): def icon(self):
if self._is_on: """return the entity icon"""
if self._tracker_device.tracker.is_locked:
return "mdi:lock" return "mdi:lock"
return "mdi:lock-open" return "mdi:lock-open"
@property @property
def device_info(self): def device_info(self):
"""Return the device info.""" """Return the device info."""
return { return self._tracker_device.device_info
"name": self.name,
"identifiers": {(GEORIDE_DOMAIN, self._tracker_id)},
"manufacturer": "GeoRide"
}
class GeoRideEcoModeSwitchEntity(CoordinatorEntity, SwitchEntity):
"""Represent a tracked device."""
def __init__(self, coordinator: DataUpdateCoordinator[Mapping[str, Any]],
tracker_device:Device, hass):
"""Set up GeoRide entity."""
super().__init__(coordinator)
self._tracker_device = tracker_device
self._name = tracker_device.tracker.tracker_name
self.entity_id = f"{ENTITY_ID_FORMAT.format('eco_mode')}_{tracker_device.tracker.tracker_id}"# pylint: disable=C0301
self._hass = hass
@property
def entity_category(self):
return None
async def async_turn_on(self, **kwargs):
""" lock the GeoRide tracker """
_LOGGER.info('async_turn_on eco %s', kwargs)
georide_context = self._hass.data[GEORIDE_DOMAIN]["context"]
token = await georide_context.get_token()
success = await self._hass.async_add_executor_job(GeoRideApi.change_tracker_eco_mode_state,
token, self._tracker_device.tracker.tracker_id, True)
if success:
self._tracker_device.tracker.is_in_eco = True
async def async_turn_off(self, **kwargs):
""" unlock the GeoRide tracker """
_LOGGER.info('async_turn_off eco %s', kwargs)
georide_context = self._hass.data[GEORIDE_DOMAIN]["context"]
token = await georide_context.get_token()
success = await self._hass.async_add_executor_job(GeoRideApi.change_tracker_eco_mode_state,
token, self._tracker_device.tracker.tracker_id, False)
if success:
self._tracker_device.tracker.is_in_eco = False
@property
def unique_id(self):
"""Return the unique ID."""
return f"eco_mode_{self._tracker_device.tracker.tracker_id}"
@property
def name(self):
""" GeoRide odometer name """
return f"{self._name} eco mode"
@property
def is_on(self):
""" GeoRide switch status """
return self._tracker_device.tracker.is_in_eco
@property
def icon(self):
"""return the entity icon"""
if self._tracker_device.tracker.is_in_eco:
return "mdi:battery-heart-variant"
return "mdi:battery"
@property
def device_info(self):
"""Return the device info."""
return self._tracker_device.device_info

View File

@@ -4,7 +4,7 @@
"step": { "step": {
"georide_login": { "georide_login": {
"title": "Set up GeoRide", "title": "Set up GeoRide",
"description": "Sign-in to georide", "description": "Sign-in to GeoRide",
"data": { "data": {
"email": "email", "email": "email",
"password": "password" "password": "password"
@@ -16,10 +16,10 @@
"unknown": "Unknown error" "unknown": "Unknown error"
}, },
"abort": { "abort": {
"one_instance_allowed": "Only a single instance is allowed." "one_instance_allowed": "Only one instance is allowed."
}, },
"create_entry": { "create_entry": {
"default": "\n\nLogin succes" "default": "\n\nLogin success"
} }
} }
} }

View File

@@ -4,7 +4,7 @@
"step": { "step": {
"georide_login": { "georide_login": {
"title": "Configuration de GeoRide", "title": "Configuration de GeoRide",
"description": "T'es un mortard ! V", "description": "T'es un motard ! V",
"data": { "data": {
"email": "email", "email": "email",
"password": "password" "password": "password"
@@ -12,14 +12,14 @@
} }
}, },
"error": { "error": {
"faulty_credentials": "Connexion \u00e9chou\u00e9", "faulty_credentials": "Connexion \u00e9chou\u00e9e",
"unkonwn": "Erreur inconue" "unkonwn": "Erreur inconnue"
}, },
"abort": { "abort": {
"one_instance_allowed": "Seulement un instance est authoris\u00e9e" "one_instance_allowed": "Seulement une instance est autoris\u00e9e"
}, },
"create_entry": { "create_entry": {
"default": "\n\nConnexion r\u00e9ussie!" "default": "\n\nConnexion r\u00e9ussie !"
} }
} }
} }

View File

@@ -1,8 +1,8 @@
{ {
"name": "Georide integration", "name": "GeoRide integration",
"content_in_root": false, "content_in_root": false,
"render_readme": true, "render_readme": true,
"domains": ["devices_tracker", "sensor"], "domains": ["devices_tracker", "sensor"],
"country": ["FR"], "country": ["FR"],
"homeassistant": "0.100.0" "homeassistant": "2024.11.0"
} }