Skip to content

Commit

Permalink
Merge pull request #237 from marcelvriend/feature/battery-support
Browse files Browse the repository at this point in the history
Add battery support
  • Loading branch information
isabellaalstrom authored Jul 29, 2022
2 parents 7d24b68 + f2d1ae3 commit 3e33d9f
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 2 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ Future integration updates will appear automatically within Home Assistant via H
# Entities

**All entities are disabled from the start. You have to manually enable the entities you want to use in Home Assistant.**
You get a sensor each for chores, meal plan, shopping list, stock and tasks.
You get a binary sensor each for overdue, expired, expiring and missing products and overdue tasks and chores.
You get a sensor each for chores, meal plan, shopping list, stock, tasks and batteries.
You get a binary sensor each for overdue, expired, expiring and missing products and for overdue tasks, overdue chores and overdue batteries.


# Services
Expand All @@ -51,6 +51,10 @@ Adds a single object of the given entity type.

Adds a given amount of a product to the stock.

- **Grocy: Track Battery** (_grocy.track_battery_)

Tracks the given battery.

- **Grocy: Complete Task** (_grocy.complete_task_)

Completes the given task.
Expand Down
6 changes: 6 additions & 0 deletions custom_components/grocy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
from homeassistant.core import HomeAssistant

from .const import (
ATTR_BATTERIES,
ATTR_CHORES,
ATTR_EXPIRED_PRODUCTS,
ATTR_EXPIRING_PRODUCTS,
ATTR_MEAL_PLAN,
ATTR_MISSING_PRODUCTS,
ATTR_OVERDUE_BATTERIES,
ATTR_OVERDUE_CHORES,
ATTR_OVERDUE_PRODUCTS,
ATTR_OVERDUE_TASKS,
Expand Down Expand Up @@ -91,6 +93,10 @@ async def _async_get_available_entities(grocy_data: GrocyData) -> List[str]:
if _is_enabled_grocy_feature(grocy_config, "FEATURE_FLAG_RECIPES"):
available_entities.append(ATTR_MEAL_PLAN)

if _is_enabled_grocy_feature(grocy_config, "FEATURE_FLAG_BATTERIES"):
available_entities.append(ATTR_BATTERIES)
available_entities.append(ATTR_OVERDUE_BATTERIES)

_LOGGER.debug("Available entities: %s", available_entities)

return available_entities
Expand Down
11 changes: 11 additions & 0 deletions custom_components/grocy/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
ATTR_EXPIRED_PRODUCTS,
ATTR_EXPIRING_PRODUCTS,
ATTR_MISSING_PRODUCTS,
ATTR_OVERDUE_BATTERIES,
ATTR_OVERDUE_CHORES,
ATTR_OVERDUE_PRODUCTS,
ATTR_OVERDUE_TASKS,
Expand Down Expand Up @@ -121,6 +122,16 @@ class GrocyBinarySensorEntityDescription(BinarySensorEntityDescription):
"count": len(data),
},
),
GrocyBinarySensorEntityDescription(
key=ATTR_OVERDUE_BATTERIES,
name="Grocy overdue batteries",
icon="mdi:battery-charging-10",
exists_fn=lambda entities: ATTR_OVERDUE_BATTERIES in entities,
attributes_fn=lambda data: {
"overdue_batteries": [x.as_dict() for x in data],
"count": len(data),
},
),
)


Expand Down
3 changes: 3 additions & 0 deletions custom_components/grocy/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,15 @@
MEAL_PLANS: Final = "Meal(s)"
PRODUCTS: Final = "Product(s)"
TASKS: Final = "Task(s)"
ITEMS: Final = "Item(s)"

ATTR_BATTERIES: Final = "batteries"
ATTR_CHORES: Final = "chores"
ATTR_EXPIRED_PRODUCTS: Final = "expired_products"
ATTR_EXPIRING_PRODUCTS: Final = "expiring_products"
ATTR_MEAL_PLAN: Final = "meal_plan"
ATTR_MISSING_PRODUCTS: Final = "missing_products"
ATTR_OVERDUE_BATTERIES: Final = "overdue_batteries"
ATTR_OVERDUE_CHORES: Final = "overdue_chores"
ATTR_OVERDUE_PRODUCTS: Final = "overdue_products"
ATTR_OVERDUE_TASKS: Final = "overdue_tasks"
Expand Down
25 changes: 25 additions & 0 deletions custom_components/grocy/grocy_data.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
"""Communication with Grocy API."""
from __future__ import annotations

import logging
from datetime import datetime
from typing import List

from aiohttp import hdrs, web
from homeassistant.components.http import HomeAssistantView
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from pygrocy.data_models.battery import Battery

from .const import (
ATTR_BATTERIES,
ATTR_CHORES,
ATTR_EXPIRED_PRODUCTS,
ATTR_EXPIRING_PRODUCTS,
ATTR_MEAL_PLAN,
ATTR_MISSING_PRODUCTS,
ATTR_OVERDUE_BATTERIES,
ATTR_OVERDUE_CHORES,
ATTR_OVERDUE_PRODUCTS,
ATTR_OVERDUE_TASKS,
Expand Down Expand Up @@ -48,6 +54,8 @@ def __init__(self, hass, api):
ATTR_MEAL_PLAN: self.async_update_meal_plan,
ATTR_OVERDUE_CHORES: self.async_update_overdue_chores,
ATTR_OVERDUE_TASKS: self.async_update_overdue_tasks,
ATTR_BATTERIES: self.async_update_batteries,
ATTR_OVERDUE_BATTERIES: self.async_update_overdue_batteries,
}

async def async_update_data(self, entity_key):
Expand Down Expand Up @@ -163,6 +171,23 @@ def wrapper():

return await self.hass.async_add_executor_job(wrapper)

async def async_update_batteries(self) -> List[Battery]:
"""Update batteries."""

def wrapper():
return self.api.batteries(get_details=True)

return await self.hass.async_add_executor_job(wrapper)

async def async_update_overdue_batteries(self) -> List[Battery]:
"""Update overdue batteries."""

def wrapper():
filter_query = [f"next_estimated_charge_time<{datetime.now()}"]
return self.api.batteries(filter_query, get_details=True)

return await self.hass.async_add_executor_job(wrapper)


async def async_setup_endpoint_for_image_proxy(
hass: HomeAssistant, config_entry: ConfigEntry
Expand Down
14 changes: 14 additions & 0 deletions custom_components/grocy/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
from homeassistant.helpers.typing import StateType

from .const import (
ATTR_BATTERIES,
ATTR_CHORES,
ATTR_MEAL_PLAN,
ATTR_SHOPPING_LIST,
ATTR_STOCK,
ATTR_TASKS,
CHORES,
DOMAIN,
ITEMS,
MEAL_PLANS,
PRODUCTS,
TASKS,
Expand Down Expand Up @@ -126,6 +128,18 @@ class GrocySensorEntityDescription(SensorEntityDescription):
"count": len(data),
},
),
GrocySensorEntityDescription(
key=ATTR_BATTERIES,
name="Grocy batteries",
native_unit_of_measurement=ITEMS,
state_class=SensorStateClass.MEASUREMENT,
icon="mdi:battery",
exists_fn=lambda entities: ATTR_BATTERIES in entities,
attributes_fn=lambda data: {
"batteries": [x.as_dict() for x in data],
"count": len(data),
},
),
)


Expand Down
24 changes: 24 additions & 0 deletions custom_components/grocy/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@
SERVICE_ENTITY_TYPE = "entity_type"
SERVICE_DATA = "data"
SERVICE_RECIPE_ID = "recipe_id"
SERVICE_BATTERY_ID = "battery_id"

SERVICE_ADD_PRODUCT = "add_product_to_stock"
SERVICE_CONSUME_PRODUCT = "consume_product_from_stock"
SERVICE_EXECUTE_CHORE = "execute_chore"
SERVICE_COMPLETE_TASK = "complete_task"
SERVICE_ADD_GENERIC = "add_generic"
SERVICE_CONSUME_RECIPE = "consume_recipe"
SERVICE_TRACK_BATTERY = "track_battery"

SERVICE_ADD_PRODUCT_SCHEMA = vol.All(
vol.Schema(
Expand Down Expand Up @@ -87,13 +89,22 @@
)
)

SERVICE_TRACK_BATTERY_SCHEMA = vol.All(
vol.Schema(
{
vol.Required(SERVICE_BATTERY_ID): vol.Coerce(int),
}
)
)

SERVICES_WITH_ACCOMPANYING_SCHEMA: list[tuple[str, vol.Schema]] = [
(SERVICE_ADD_PRODUCT, SERVICE_ADD_PRODUCT_SCHEMA),
(SERVICE_CONSUME_PRODUCT, SERVICE_CONSUME_PRODUCT_SCHEMA),
(SERVICE_EXECUTE_CHORE, SERVICE_EXECUTE_CHORE_SCHEMA),
(SERVICE_COMPLETE_TASK, SERVICE_COMPLETE_TASK_SCHEMA),
(SERVICE_ADD_GENERIC, SERVICE_ADD_GENERIC_SCHEMA),
(SERVICE_CONSUME_RECIPE, SERVICE_CONSUME_RECIPE_SCHEMA),
(SERVICE_TRACK_BATTERY, SERVICE_TRACK_BATTERY_SCHEMA),
]


Expand Down Expand Up @@ -128,6 +139,9 @@ async def async_call_grocy_service(service_call: ServiceCall) -> None:
elif service == SERVICE_CONSUME_RECIPE:
await async_consume_recipe_service(hass, coordinator, service_data)

elif service == SERVICE_TRACK_BATTERY:
await async_track_battery_service(hass, coordinator, service_data)

for service, schema in SERVICES_WITH_ACCOMPANYING_SCHEMA:
hass.services.async_register(DOMAIN, service, async_call_grocy_service, schema)

Expand Down Expand Up @@ -228,6 +242,16 @@ def wrapper():
await hass.async_add_executor_job(wrapper)


async def async_track_battery_service(hass, coordinator, data):
"""Track a battery in Grocy."""
battery_id = data[SERVICE_BATTERY_ID]

def wrapper():
coordinator.grocy_api.charge_battery(battery_id)

await hass.async_add_executor_job(wrapper)


async def _async_force_update_entity(
coordinator: GrocyDataUpdateCoordinator, entity_key: str
) -> None:
Expand Down
12 changes: 12 additions & 0 deletions custom_components/grocy/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -163,5 +163,17 @@ consume_recipe:
example: '3'
required: true
description: The id of the recipe to consume
selector:
text:

track_battery:
name: Track Battery
description: Tracks the given battery
fields:
battery_id:
name: Battery Id
example: '1'
required: true
description: The id of the battery
selector:
text:

0 comments on commit 3e33d9f

Please sign in to comment.