Skip to content

Commit

Permalink
Split sensor and add binary sensor (#14)
Browse files Browse the repository at this point in the history
* Split sensor : chores and products
* Add binary sensor (expiring products)
* update readme in regards of binary sensor
* bump pygrocy to 0.11.0
  • Loading branch information
BlueBlueBlob authored and SebRut committed Sep 22, 2019
1 parent c9a7e1c commit 6a6d195
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 26 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ grocy:
verify_ssl: true
sensor:
- enabled: true
binary_sensor:
- enabled : true
```
7. Restart Home Assistant
Expand Down
66 changes: 53 additions & 13 deletions custom_components/grocy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,18 @@
from homeassistant.util import Throttle
from homeassistant.core import callback

from .const import (CONF_ENABLED, CONF_NAME, CONF_SENSOR, DEFAULT_NAME, DOMAIN,
DOMAIN_DATA, ISSUE_URL, PLATFORMS, REQUIRED_FILES, STARTUP,
VERSION)
from .const import (
CONF_ENABLED,
CONF_NAME,
CONF_SENSOR,
CONF_BINARY_SENSOR,
DEFAULT_NAME, DOMAIN,
DOMAIN_DATA, ISSUE_URL,
PLATFORMS,
REQUIRED_FILES,
STARTUP,
VERSION,
)

MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)

Expand All @@ -28,14 +37,26 @@
}
)

BINARY_SENSOR_SCHEMA = vol.Schema(
{
vol.Optional(CONF_ENABLED, default=True): cv.boolean,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
}
)

CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_URL): cv.string,
vol.Required(CONF_API_KEY): cv.string,
vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean,
vol.Optional(CONF_SENSOR): vol.All(cv.ensure_list, [SENSOR_SCHEMA]),
vol.Optional(CONF_SENSOR): vol.All(
cv.ensure_list, [SENSOR_SCHEMA]
),
vol.Optional(CONF_BINARY_SENSOR): vol.All(
cv.ensure_list, [BINARY_SENSOR_SCHEMA]
),
}
)
},
Expand All @@ -46,7 +67,7 @@
async def async_setup(hass, config):
"""Set up this component."""
# Import client from a external python package hosted on PyPi
from pygrocy import Grocy,TransactionType
from pygrocy import Grocy, TransactionType
from datetime import datetime
import iso8601

Expand Down Expand Up @@ -110,12 +131,17 @@ def handle_consume_product(call):

transaction_type_raw = call.data.get('transaction_type', None)
transaction_type = TransactionType.CONSUME

if transaction_type_raw is not None:
transaction_type = TransactionType[transaction_type_raw]
grocy.consume_product(product_id, amount, spoiled=spoiled, transaction_type=transaction_type)
grocy.consume_product(
product_id, amount,
spoiled=spoiled,
transaction_type=transaction_type)

hass.services.async_register(DOMAIN, "consume_product", handle_consume_product)
hass.services.async_register(
DOMAIN, "consume_product",
handle_consume_product)

@callback
def handle_execute_chore(call):
Expand All @@ -129,7 +155,7 @@ def handle_execute_chore(call):
grocy.execute_chore(chore_id, done_by, tracked_time)

hass.services.async_register(DOMAIN, "execute_chore", handle_execute_chore)

return True


Expand All @@ -142,12 +168,26 @@ def __init__(self, hass, client):
self.client = client

@Throttle(MIN_TIME_BETWEEN_UPDATES)
async def async_update_data(self):
async def async_update_stock(self):
"""Update data."""
# This is where the main logic to update platform data goes.
self.hass.data[DOMAIN_DATA]["stock"] = (
await self.hass.async_add_executor_job(self.client.stock, [True]))

@Throttle(MIN_TIME_BETWEEN_UPDATES)
async def async_update_chores(self):
"""Update data."""
# This is where the main logic to update platform data goes.
self.hass.data[DOMAIN_DATA]["chores"] = (
await self.hass.async_add_executor_job(self.client.chores, [True]))

@Throttle(MIN_TIME_BETWEEN_UPDATES)
async def async_update_expiring_products(self):
"""Update data."""
# This is where the main logic to update platform data goes.
self.hass.data[DOMAIN_DATA]["stock"] = await self.hass.async_add_executor_job(self.client.stock,[True])
self.hass.data[DOMAIN_DATA]["chores"] = await self.hass.async_add_executor_job(self.client.chores,[True])

self.hass.data[DOMAIN_DATA]["expiring_products"] = (
await self.hass.async_add_executor_job(
self.client.expiring_products))


async def check_files(hass):
Expand Down
69 changes: 69 additions & 0 deletions custom_components/grocy/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""Binary sensor platform for grocy."""
from homeassistant.components.binary_sensor import BinarySensorDevice
from .const import (
ATTRIBUTION,
DEFAULT_NAME,
DOMAIN_DATA,
DOMAIN,
)


async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None
): # pylint: disable=unused-argument
"""Setup binary_sensor platform."""
async_add_entities(
[GrocyExpiringProductsBinarySensor(hass, discovery_info)], True)


class GrocyExpiringProductsBinarySensor(BinarySensorDevice):
"""grocy binary_sensor class."""

def __init__(self, hass, config):
self.hass = hass
self.attr = {}
self._status = False
self._name = config.get("name", DEFAULT_NAME) + ".expiring_products"
self._client = self.hass.data[DOMAIN_DATA]["client"]

async def async_update(self):
import jsonpickle
"""Update the binary_sensor."""
# Send update "signal" to the component
await self._client.async_update_expiring_products()

# Get new data (if any)
expiring_products = (
self.hass.data[DOMAIN_DATA].get("expiring_products"))

# Check the data and update the value.
if not expiring_products:
self._status = self._status
else:
self._status = True

# Set/update attributes
self.attr["attribution"] = ATTRIBUTION
self.attr["items"] = jsonpickle.encode(
expiring_products,
unpicklable=False)

@property
def name(self):
"""Return the name of the binary_sensor."""
return self._name

@property
def device_class(self):
"""Return the class of this binary_sensor."""
return None

@property
def is_on(self):
"""Return true if the binary_sensor is on."""
return self._status

@property
def device_state_attributes(self):
"""Return the state attributes."""
return self.attr
7 changes: 5 additions & 2 deletions custom_components/grocy/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
DOMAIN = "grocy"
DOMAIN_DATA = "{}_data".format(DOMAIN)
VERSION = "0.1.3"
PLATFORMS = ["sensor"]
PLATFORMS = ["sensor", "binary_sensor"]
REQUIRED_FILES = [
"const.py",
"manifest.json",
"sensor.py",
"binary_sensor.py",
]
ISSUE_URL = "https://github.com/custom-components/grocy/issues"
ATTRIBUTION = "Data from this is provided by grocy."
Expand All @@ -25,10 +26,12 @@
ICON = "mdi:format-quote-close"

# Device classes
SENSOR_UNIT_OF_MEASUREMENT = "Product(s)"
SENSOR_PRODUCTS_UNIT_OF_MEASUREMENT = "Product(s)"
SENSOR_CHORES_UNIT_OF_MEASUREMENT = "Chore(s)"

# Configuration
CONF_SENSOR = "sensor"
CONF_BINARY_SENSOR = "binary_sensor"
CONF_ENABLED = "enabled"
CONF_NAME = "name"

Expand Down
2 changes: 1 addition & 1 deletion custom_components/grocy/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"@SebRut"
],
"requirements": [
"pygrocy==0.10.0",
"pygrocy==0.11.0",
"jsonpickle==1.1",
"iso8601==0.1.12"
]
Expand Down
78 changes: 68 additions & 10 deletions custom_components/grocy/sensor.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,41 @@
"""Sensor platform for grocy."""
from homeassistant.helpers.entity import Entity

from .const import (ATTRIBUTION, DEFAULT_NAME, DOMAIN_DATA, ICON,
SENSOR_UNIT_OF_MEASUREMENT)
from .const import (
ATTRIBUTION,
DEFAULT_NAME,
DOMAIN_DATA,
ICON,
SENSOR_PRODUCTS_UNIT_OF_MEASUREMENT,
SENSOR_CHORES_UNIT_OF_MEASUREMENT,
)


async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None
): # pylint: disable=unused-argument
"""Setup sensor platform."""
async_add_entities([GrocySensor(hass, discovery_info)], True)
async_add_entities([GrocyProductsSensor(hass, discovery_info)], True)
async_add_entities([GrocyChoresSensor(hass, discovery_info)], True)


class GrocySensor(Entity):
class GrocyProductsSensor(Entity):
"""grocy Sensor class."""

def __init__(self, hass, config):
self.hass = hass
self.attr = {}
self._state = None
self._name = config.get("name", DEFAULT_NAME)
self._name = '{}.products'.format(config.get("name", DEFAULT_NAME))

async def async_update(self):
import jsonpickle
"""Update the sensor."""
# Send update "signal" to the component
await self.hass.data[DOMAIN_DATA]["client"].async_update_data()
await self.hass.data[DOMAIN_DATA]["client"].async_update_stock()

# Get new data (if any)
stock = self.hass.data[DOMAIN_DATA].get("stock")
chores = self.hass.data[DOMAIN_DATA].get("chores")

# Check the data and update the value.
if stock is None:
Expand All @@ -39,8 +45,60 @@ async def async_update(self):

# Set/update attributes
self.attr["attribution"] = ATTRIBUTION
self.attr["items"] = jsonpickle.encode(stock,unpicklable=False)
self.attr["chores"] = jsonpickle.encode(chores,unpicklable=False)
self.attr["items"] = jsonpickle.encode(stock, unpicklable=False)

@property
def name(self):
"""Return the name of the sensor."""
return self._name

@property
def state(self):
"""Return the state of the sensor."""
return self._state

@property
def icon(self):
"""Return the icon of the sensor."""
return ICON

@property
def unit_of_measurement(self):
return SENSOR_PRODUCTS_UNIT_OF_MEASUREMENT

@property
def device_state_attributes(self):
"""Return the state attributes."""
return self.attr


class GrocyChoresSensor(Entity):
"""grocy Sensor class."""

def __init__(self, hass, config):
self.hass = hass
self.attr = {}
self._state = None
self._name = '{}.chores'.format(config.get("name", DEFAULT_NAME))

async def async_update(self):
import jsonpickle
"""Update the sensor."""
# Send update "signal" to the component
await self.hass.data[DOMAIN_DATA]["client"].async_update_chores()

# Get new data (if any)
chores = self.hass.data[DOMAIN_DATA].get("chores")

# Check the data and update the value.
if chores is None:
self._state = self._state
else:
self._state = len(chores)

# Set/update attributes
self.attr["attribution"] = ATTRIBUTION
self.attr["items"] = jsonpickle.encode(chores, unpicklable=False)

@property
def name(self):
Expand All @@ -59,7 +117,7 @@ def icon(self):

@property
def unit_of_measurement(self):
return SENSOR_UNIT_OF_MEASUREMENT
return SENSOR_CHORES_UNIT_OF_MEASUREMENT

@property
def device_state_attributes(self):
Expand Down

0 comments on commit 6a6d195

Please sign in to comment.