Skip to content

Commit

Permalink
Added all the missing sensors and a camera that pulls image from api
Browse files Browse the repository at this point in the history
  • Loading branch information
mitch-dc committed Jan 18, 2022
1 parent bb3f796 commit 271deaa
Show file tree
Hide file tree
Showing 6 changed files with 344 additions and 94 deletions.
7 changes: 5 additions & 2 deletions custom_components/volkswagen_we_connect_id/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
from weconnect.elements.vehicle import Vehicle

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.helpers.entity import DeviceInfo, Entity
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN

PLATFORMS: list[str] = ["sensor", "button"]
PLATFORMS = [Platform.BINARY_SENSOR, Platform.BUTTON, Platform.SENSOR, Platform.CAMERA]

_LOGGER = logging.getLogger(__name__)


Expand Down Expand Up @@ -240,7 +242,7 @@ class VolkswagenIDBaseEntity(Entity):

def __init__(
self,
vehicle,
vehicle: weconnect.Vehicle,
data,
we_connect: weconnect.WeConnect,
coordinator: CoordinatorEntity,
Expand All @@ -257,6 +259,7 @@ def __init__(
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, f"vw{vehicle.vin}")},
manufacturer="Volkswagen",
model=f"{vehicle.model}", # format because of the ID.3/ID.4 names.
name=f"Volkswagen {vehicle.nickname}",
)

Expand Down
162 changes: 162 additions & 0 deletions custom_components/volkswagen_we_connect_id/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
"""Platform for sensor integration."""
from __future__ import annotations

from dataclasses import dataclass
from datetime import timedelta
import logging
from typing import cast

from weconnect import weconnect
from weconnect.elements.plug_status import PlugStatus
from weconnect.elements.window_heating_status import WindowHeatingStatus

from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator

from . import VolkswagenIDBaseEntity
from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)


@dataclass
class VolkswagenIdBinaryEntityDescription(BinarySensorEntityDescription):
"""Describes Volkswagen ID binary sensor entity."""

local_address: str | None = None
on_value: None = None


SENSORS: tuple[VolkswagenIdBinaryEntityDescription, ...] = (
VolkswagenIdBinaryEntityDescription(
key="climatisationWithoutExternalPower",
name="Climatisation Without External Power",
local_address="/climatisation/climatisationSettings/climatisationWithoutExternalPower",
on_value=True,
),
VolkswagenIdBinaryEntityDescription(
key="climatizationAtUnlock",
name="Climatisation At Unlock",
local_address="/climatisation/climatisationSettings/climatizationAtUnlock",
on_value=True,
),
VolkswagenIdBinaryEntityDescription(
key="zoneFrontLeftEnabled",
name="Zone Front Left Enabled",
local_address="/climatisation/climatisationSettings/zoneFrontLeftEnabled",
on_value=True,
),
VolkswagenIdBinaryEntityDescription(
key="zoneFrontRightEnabled",
name="Zone Front Right Enabled",
local_address="/climatisation/climatisationSettings/zoneFrontRightEnabled",
on_value=True,
),
VolkswagenIdBinaryEntityDescription(
key="windowHeatingEnabled",
name="Window Heating Enabled",
local_address="/climatisation/climatisationSettings/windowHeatingEnabled",
on_value=True,
),
VolkswagenIdBinaryEntityDescription(
key="frontWindowHeatingState",
name="Front Window Heating State",
local_address="/climatisation/windowHeatingStatus/windows/front/windowHeatingState",
on_value=WindowHeatingStatus.Window.WindowHeatingState.ON,
),
VolkswagenIdBinaryEntityDescription(
key="rearWindowHeatingState",
name="Rear Window Heating State",
local_address="/climatisation/windowHeatingStatus/windows/rear/windowHeatingState",
on_value=WindowHeatingStatus.Window.WindowHeatingState.ON,
),
VolkswagenIdBinaryEntityDescription(
key="autoUnlockPlugWhenCharged",
name="Auto Unlock Plug When Charged",
local_address="/charging/chargingSettings/autoUnlockPlugWhenCharged",
on_value=True,
),
VolkswagenIdBinaryEntityDescription(
key="plugConnectionState",
name="Plug Connection State",
local_address="/charging/plugStatus/plugConnectionState",
device_class=BinarySensorDeviceClass.PLUG,
on_value=PlugStatus.PlugConnectionState.CONNECTED,
),
VolkswagenIdBinaryEntityDescription(
key="plugLockState",
name="Plug Lock State",
local_address="/charging/plugStatus/plugLockState",
device_class=BinarySensorDeviceClass.LOCK,
on_value=PlugStatus.PlugLockState.LOCKED,
),
)


async def async_setup_entry(hass, config_entry, async_add_entities):
"""Add sensors for passed config_entry in HA."""
we_connect: weconnect.WeConnect
we_connect = hass.data[DOMAIN][config_entry.entry_id]
vehicles = hass.data[DOMAIN][config_entry.entry_id + "_vehicles"]

async def async_update_data():
await hass.async_add_executor_job(we_connect.update)

coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
# Name of the data. For logging purposes.
name="volkswagen_we_connect_id_sensors",
update_method=async_update_data,
# Polling interval. Will only be polled if there are subscribers.
update_interval=timedelta(seconds=30),
)

entities: list[VolkswagenIDSensor] = []
for vin, vehicle in vehicles.items():
for sensor in SENSORS:
entities.append(
VolkswagenIDSensor(
vehicle,
sensor,
we_connect,
coordinator,
)
)
if entities:
async_add_entities(entities)


class VolkswagenIDSensor(VolkswagenIDBaseEntity, BinarySensorEntity):
"""Representation of a VolkswagenID vehicle sensor."""

entity_description: VolkswagenIdBinaryEntityDescription

def __init__(
self,
vehicle: weconnect.Vehicle,
sensor: VolkswagenIdBinaryEntityDescription,
we_connect: weconnect.WeConnect,
coordinator: DataUpdateCoordinator,
) -> None:
"""Initialize VolkswagenID vehicle sensor."""
super().__init__(vehicle, sensor, we_connect, coordinator)

self.entity_description = sensor
self._coordinator = coordinator
self._attr_name = f"Volkswagen ID {vehicle.nickname} {sensor.name}"
self._attr_unique_id = f"{vehicle.vin}-{sensor.key}"
self._data = f"/vehicles/{vehicle.vin}{sensor.local_address}"

@property
def is_on(self) -> bool:
"""Return true if sensor is on."""

state = self._we_connect.getByAddressString(self._data)
return state == self.entity_description.on_value
7 changes: 4 additions & 3 deletions custom_components/volkswagen_we_connect_id/button.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
"""TOLO Sauna Button controls."""

from weconnect import weconnect
from weconnect.elements.control_operation import ControlOperation
from weconnect.elements.vehicle import Vehicle

from homeassistant.components.button import ButtonEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import VolkswagenIDBaseEntity, set_climatisation
from weconnect import weconnect
from weconnect.elements.control_operation import ControlOperation
from weconnect.elements.vehicle import Vehicle
from .const import DOMAIN


Expand Down
93 changes: 93 additions & 0 deletions custom_components/volkswagen_we_connect_id/camera.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""Platform for sensor integration."""
from __future__ import annotations

from datetime import timedelta
import io
import logging

from PIL import Image
from weconnect import weconnect

from homeassistant.components.camera import Camera
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator

from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(hass, config_entry, async_add_entities):
"""Add sensors for passed config_entry in HA."""
we_connect: weconnect.WeConnect
we_connect = hass.data[DOMAIN][config_entry.entry_id]
vehicles = hass.data[DOMAIN][config_entry.entry_id + "_vehicles"]

async def async_update_data():
await hass.async_add_executor_job(we_connect.update)

coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
# Name of the data. For logging purposes.
name="volkswagen_we_connect_id_sensors",
update_method=async_update_data,
# Polling interval. Will only be polled if there are subscribers.
update_interval=timedelta(minutes=60),
)

cameras: list[VolkswagenIDCamera] = []
for vin, vehicle in vehicles.items():
cameras.append(
VolkswagenIDCamera(
vehicle,
we_connect,
coordinator,
)
)

if cameras:
async_add_entities(cameras)


class VolkswagenIDCamera(Camera):
# Implement one of these methods.

def __init__(
self,
vehicle: weconnect.Vehicle,
we_connect: weconnect.WeConnect,
coordinator: DataUpdateCoordinator,
) -> None:
"""Initialize VolkswagenID camera."""

Camera.__init__(self)

self._attr_name = f"Volkswagen ID {vehicle.nickname} Image"
self._attr_unique_id = f"{vehicle.vin}-Image"

self._coordinator = coordinator
self._we_connect = we_connect
self._data = f"/vehicles/{vehicle.vin}/pictures/car"

self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, f"vw{vehicle.vin}")},
manufacturer="Volkswagen",
model=f"{vehicle.model}", # format because of the ID.3/ID.4 names.
name=f"Volkswagen {vehicle.nickname}",
)

def camera_image(
self, width: int | None = None, height: int | None = None
) -> bytes | None:
"""Return image response."""
try:
image = self._we_connect.getByAddressString(self._data).value
imgByteArr = io.BytesIO()
image.save(imgByteArr, format=image.format)
imgByteArr = imgByteArr.getvalue()
return imgByteArr

except FileNotFoundError:
_LOGGER.warning("Could not read camera %s image from file: %s")
return None
4 changes: 2 additions & 2 deletions custom_components/volkswagen_we_connect_id/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
"name": "Volkswagen We Connect ID",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/volkswagen_we_connect_id",
"requirements": ["weconnect==0.32.1", "ascii_magic==1.6"],
"requirements": ["weconnect==0.33.0", "ascii_magic==1.6"],
"ssdp": [],
"zeroconf": [],
"homekit": {},
"dependencies": [],
"codeowners": ["@mitch-dc"],
"iot_class": "cloud_polling",
"version": "0.1"
"version": "0.3"
}
Loading

0 comments on commit 271deaa

Please sign in to comment.