diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..b21ca53 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,17 @@ +FROM python:3.8 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + git \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +RUN python -m pip install --upgrade colorlog black pylint +RUN python -m pip install --upgrade git+git://github.com/home-assistant/home-assistant.git@dev +RUN cd && mkdir -p /config/custom_components + + +WORKDIR /workspace + +# Set the default shell to bash instead of sh +ENV SHELL /bin/bash \ No newline at end of file diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 0000000..f52282a --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,53 @@ +# Devcontainer + +_The easiest way to contribute to and/or test this repository._ + +## Requirements + +- [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +- [docker](https://docs.docker.com/install/) +- [VS Code](https://code.visualstudio.com/) +- [Remote - Containers (VSC Extention)](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) + +[More info about requirements and devcontainer in general](https://code.visualstudio.com/docs/remote/containers#_getting-started) + +## How to use Devcontainer for development/test + +1. Make sure your computer meets the requirements. +1. Fork this repository. +1. Clone the repository to your computer. +1. Open the repository using VS Code. + +When you open this repository with VSCode and your computer meets the requirements you are asked to "Reopen in Container", do that. + +![reopen](images/reopen.png) + +If you don't see this notification, open the command pallet (ctrl+shift+p) and select `Remote-Containers: Reopen Folder in Container`. + +_It will now build the devcontainer._ + +The container have some "tasks" to help you testing your changes. + +## Custom Tasks in this repository + +_Start "tasks" by opening the the command pallet (ctrl+shift+p) and select `Tasks: Run Task`_ + +Running tasks like `Start Home Assistant on port 8124` can be restarted by opening the the command pallet (ctrl+shift+p) and select `Tasks: Restart Running Task`, then select the task you want to restart. + +### Start Home Assistant on port 8124 + +This will copy the configuration and the integration files to the expected location in the container. + +And start up Home Assistant on [port 8124.](http://localhost:8124) + +### Upgrade Home Assistant to latest dev + +This will upgrade Home Assistant to the latest dev version. + +### Set Home Assistant Version + +This allows you to specify a version of Home Assistant to install inside the devcontainer. + +### Home Assistant Config Check + +This runs a config check to make sure your config is valid. diff --git a/.devcontainer/configuration.yaml b/.devcontainer/configuration.yaml new file mode 100644 index 0000000..6fdd43e --- /dev/null +++ b/.devcontainer/configuration.yaml @@ -0,0 +1,5 @@ +default_config: +logger: + default: error + logs: + custom_components.drivvo: debug diff --git a/.devcontainer/custom_component_helper b/.devcontainer/custom_component_helper new file mode 100644 index 0000000..40165d9 --- /dev/null +++ b/.devcontainer/custom_component_helper @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +function StartHomeAssistant { + echo "Copy configuration.yaml" + cp -f .devcontainer/configuration.yaml /config || echo ".devcontainer/configuration.yaml are missing!" exit 1 + + echo "Copy the custom component" + rm -R /config/custom_components/ || echo "" + cp -r custom_components /config/custom_components/ || echo "Could not copy the custom_component" exit 1 + + echo "Start Home Assistant" + hass -c /config +} + +function UpdgradeHomeAssistantDev { + python -m pip install --upgrade git+git://github.com/home-assistant/home-assistant.git@dev +} + +function SetHomeAssistantVersion { + read -p 'Version: ' version + python -m pip install --upgrade homeassistant==$version +} + +function HomeAssistantConfigCheck { + hass -c /config --script check_config +} \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..ee2577a --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,23 @@ +// See https://aka.ms/vscode-remote/devcontainer.json for format details. +{ + "context": "..", + "dockerFile": "Dockerfile", + "appPort": "8124:8123", + "runArgs": [ + "-e", + "GIT_EDTIOR='code --wait'" + ], + "extensions": [ + "ms-python.python" + ], + "settings": { + "python.pythonPath": "/usr/local/bin/python", + "python.linting.pylintEnabled": true, + "python.linting.enabled": true, + "python.formatting.provider": "black", + "editor.formatOnPaste": false, + "editor.formatOnSave": true, + "editor.formatOnType": true, + "files.trimTrailingWhitespace": true + } +} \ No newline at end of file diff --git a/.devcontainer/images/reopen.png b/.devcontainer/images/reopen.png new file mode 100644 index 0000000..cbcec3c Binary files /dev/null and b/.devcontainer/images/reopen.png differ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a04b218 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "*.yaml": "home-assistant" + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..31504d9 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,61 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Start Home Assistant on port 8124", + "type": "shell", + "command": "source .devcontainer/custom_component_helper && StartHomeAssistant", + "group": { + "kind": "test", + "isDefault": true, + }, + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + }, + { + "label": "Upgrade Home Assistant to latest dev", + "type": "shell", + "command": "source .devcontainer/custom_component_helper && UpdgradeHomeAssistantDev", + "group": { + "kind": "test", + "isDefault": true, + }, + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + }, + { + "label": "Set Home Assistant Version", + "type": "shell", + "command": "source .devcontainer/custom_component_helper && SetHomeAssistantVersion", + "group": { + "kind": "test", + "isDefault": true, + }, + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + }, + { + "label": "Home Assistant Config Check", + "type": "shell", + "command": "source .devcontainer/custom_component_helper && HomeAssistantConfigCheck", + "group": { + "kind": "test", + "isDefault": true, + }, + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/custom_components/drivvo/__init__.py b/custom_components/drivvo/__init__.py new file mode 100644 index 0000000..f97cacb --- /dev/null +++ b/custom_components/drivvo/__init__.py @@ -0,0 +1 @@ +"""The Drivvo.com Component.""" diff --git a/custom_components/drivvo/manifest.json b/custom_components/drivvo/manifest.json new file mode 100644 index 0000000..0affe53 --- /dev/null +++ b/custom_components/drivvo/manifest.json @@ -0,0 +1,9 @@ +{ + "domain": "drivvo", + "name": "Drivvo", + "version": "0.0.1", + "documentation": "https://github.com/hudsonbrendon/sensor.drivvo", + "dependencies": [], + "codeowners": ["@hudsonbrendon"], + "requirements": [] +} diff --git a/custom_components/drivvo/sensor.py b/custom_components/drivvo/sensor.py new file mode 100755 index 0000000..d4c5183 --- /dev/null +++ b/custom_components/drivvo/sensor.py @@ -0,0 +1,143 @@ +""" +For more details on this component, refer to the documentation at +https://github.com/hudsonbrendon/sensor.drivvo +""" +import logging +from datetime import timedelta + +import async_timeout +import homeassistant.helpers.config_validation as cv +import voluptuous as vol +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.helpers.aiohttp_client import async_create_clientsession +from homeassistant.helpers.entity import Entity + +CONF_EMAIL = "email" +CONF_PASSWORD = "password" +CONF_MODEL = "model" +CONF_ID_VEHICLE = "id_vehicle" + +SCAN_INTERVAL = timedelta(minutes=60) + +LOGIN_BASE_URL = "https://api.drivvo.com/autenticacao/login" +BASE_URL = "https://api.drivvo.com/veiculo/{}/{}/web" + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( + { + vol.Required(CONF_EMAIL): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Required(CONF_MODEL): cv.string, + vol.Required(CONF_ID_VEHICLE): cv.string, + } +) + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): + """Configuração do sensor.""" + email = config["email"] + password = config["password"] + model = config["model"] + id_vehicle = config["id_vehicle"] + session = async_create_clientsession(hass) + name = "Abastecimento" + async_add_entities( + [DrivvoSupplySensor(email, password, model, id_vehicle, name, session)], True + ) + + +class DrivvoSupplySensor(Entity): + """Sensor de abastecimento""" + + def __init__(self, email, password, model, id_vehicle, name, session): + self._state = model + self._model = model + self._email = email + self._password = password + self._id_vehicle = id_vehicle + self.session = session + self._name = name + self._supplies = [] + + async def async_update(self): + """Atualização do sensor.""" + _LOGGER.debug("%s - Running update", self._name) + try: + url = BASE_URL.format(self._id_vehicle, "abastecimento") + async with async_timeout.timeout(10, loop=self.hass.loop): + response = await self.session.post( + LOGIN_BASE_URL, + data=dict(email=self._email, senha=self._password), + ) + data = await response.json() + x_token = data.get("token") + response = await self.session.get(url, headers={"x-token": x_token}) + self._supplies = await response.json() + except Exception as error: + _LOGGER.debug("%s - Could not update - %s", self._name, error) + + @property + def name(self): + """Nome.""" + return self._name + + @property + def state(self): + """Estado.""" + return self._state + + @property + def supply(self): + """Abastecimento.""" + return self._supplies[0] + + @property + def total_payment(self): + """Soma total de valores pagos em todos os abastecimentos.""" + total = 0 + for supply in self._supplies: + total += supply.get("valor_total") + return total + + @property + def km_travel(self): + """Km percorridos desde o ultimo abastecimento.""" + km = 0 + odometers = [supply.get("odometro") for supply in self._supplies] + if len(odometers) > 1: + km = odometers[0] - odometers[1] + return km + + @property + def cheapest_gasoline_until_today(self): + return min([supply.get("preco") for supply in self._supplies]) + + @property + def total_amount_of_supplies(self): + return sum([1 for supply in self._supplies]) + + @property + def icon(self): + """Icone.""" + return "mdi:gas-station" + + @property + def device_state_attributes(self): + """Atributos.""" + return { + "veiculo": self._model, + "odometro": self.supply.get("odometro"), + "posto_combustivel": self.supply.get("posto_combustivel").get("nome"), + "combustivel": self.supply.get("combustivel"), + "motivo": self.supply.get("tipo_motivo"), + "data": self.supply.get("data"), + "volume": self.supply.get("tanques")[0].get("volume"), + "valor": self.supply.get("valor_total"), + "preco": self.supply.get("preco"), + "soma_total_de_abastecimentos": self.total_amount_of_supplies, + "soma_total_de_valores_pagos_em_todos_os_abastecimentos": self.total_payment, + "tanque_cheio": "Sim" if self.supply.get("tanque_cheio") else "Não", + "km_percorridos_desde_o_ultimo_abastecimento": self.km_travel, + "gasolina_mais_barata_ate_entao": self.cheapest_gasoline_until_today, + } diff --git a/hacs.json b/hacs.json new file mode 100644 index 0000000..cbbb85e --- /dev/null +++ b/hacs.json @@ -0,0 +1,8 @@ +{ + "name": "Drivvo", + "country": "BR", + "render_readme": true, + "domains": [ + "sensor" + ] +} \ No newline at end of file