Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
SebRut committed Dec 8, 2019
2 parents 4fca333 + 3300371 commit 45891ad
Show file tree
Hide file tree
Showing 9 changed files with 358 additions and 155 deletions.
32 changes: 26 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,48 @@
[![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg)](https://github.com/custom-components/hacs)

## Installation instructions:
## Installation instructions (general):
>>>>>>> develop
1. Install HACS for Home Assistant
2. Go to Community-Store-Grocy
3. Install Grocy
4. Restart Home Assistant
5. Go to Grocy-Wrench icon-Manage API keys-Add
6. Copy resulting API key and Grocy URL and input this in configuration.yaml:
7. Choose:
- Add `grocy:` to your HA configuration.
- In the HA UI go to "Configuration" -> "Integrations" click "+" and search for "Grocy"

8. Look for the new Grocy sensor in States and use its info


## Additional installation instructions for Hass.io users

The configuration is slightly different for users that use Hass.io and the [official Grocy addon](https://github.com/hassio-addons/addon-grocy) from the Hass.io Add-on store.

1. If you haven't already done so, install Grocy from the Add-on store
2. In the 'Network' section of the add-on config, input 9192 in the host field [screenshot](https://github.com/custom-components/grocy/raw/master/grocy-addon-config.png). Save your changes and restart the add-on.
3. Install HACS for Home Assistant
4. Go to Community > Store > Grocy
5. Install the Grocy integration component
6. Restart Home Assistant
7. Go to Grocy > Wrench icon > Manage API keys > Add
8. Copy resulting API key and input this in configuration.yaml:

```yaml
grocy:
url: "https://{{YOUR_GROCY_URL}}"
api_key: "{{YOUR_GROCY_API_KEY}}"
verify_ssl: true
url: "https://IP_ADDRESS_OFF_THE_DEVICE_RUNNING_HASS.IO" eg. "https://192.168.1.4"
api_key: "YOUR_GROCY_API_KEY"
verify_ssl: false
port: 9192
sensor:
- enabled: true
binary_sensor:
- enabled : true
```
7. Restart Home Assistant
8. Look for the new Grocy sensor in States and use its info
9. Restart Home Assistant
10. Look for the new Grocy sensor in States and use its info
---
[![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/X8X1LYUK)
23 changes: 23 additions & 0 deletions custom_components/grocy/.translations/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"config": {
"title": "Grocy",
"step": {
"user": {
"title": "Grocy",
"description": "If you need help with the configuration have a look here: https://github.com/custom-components/grocy",
"data": {
"url": "Grocy API URL (e.g. \"http://yourgrocyurl.com\")",
"api_key": "Grocy API Key",
"port": "Port Number (9192)",
"verify_ssl": "Verify SSL Certificate"
}
}
},
"error": {
"auth": "Something went wrong."
},
"abort": {
"single_instance_allowed": "Only a single configuration of Grocy is allowed."
}
}
}
164 changes: 108 additions & 56 deletions custom_components/grocy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,27 @@
"""
The integration for grocy.
"""
import hashlib
import logging
import os
from datetime import timedelta

import voluptuous as vol

import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
CONF_API_KEY,
CONF_URL,
CONF_VERIFY_SSL,
CONF_PORT,
)
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import (CONF_API_KEY, CONF_PORT, CONF_URL,
CONF_VERIFY_SSL)
from homeassistant.core import callback
from homeassistant.helpers import discovery
from homeassistant.util import Throttle
from homeassistant.core import callback
from integrationhelper.const import CC_STARTUP_VERSION

from .const import (
CONF_ENABLED,
CONF_NAME,
CONF_SENSOR,
CONF_BINARY_SENSOR,
DEFAULT_NAME,
DOMAIN,
DEFAULT_PORT_NUMBER,
DOMAIN_DATA, ISSUE_URL,
PLATFORMS,
REQUIRED_FILES,
STARTUP,
VERSION,
)
from .const import (CHORES_NAME, CONF_BINARY_SENSOR, CONF_ENABLED, CONF_NAME,
CONF_SENSOR, DEFAULT_NAME, DEFAULT_PORT_NUMBER, DOMAIN,
DOMAIN_DATA, EXPIRED_PRODUCTS_NAME, EXPIRING_PRODUCTS_NAME,
ISSUE_URL, MISSING_PRODUCTS_NAME, PLATFORMS,
REQUIRED_FILES, SHOPPING_LIST_NAME, STARTUP, STOCK_NAME,
VERSION)

MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)

Expand Down Expand Up @@ -74,14 +64,26 @@

async def async_setup(hass, config):
"""Set up this component."""
# Import client from a external python package hosted on PyPi
return True

async def async_setup_entry(hass, config_entry):
"""Set up this integration using UI."""
from pygrocy import Grocy, TransactionType
from datetime import datetime
import iso8601

conf = hass.data.get(DOMAIN_DATA)
if config_entry.source == config_entries.SOURCE_IMPORT:
if conf is None:
hass.async_create_task(
hass.config_entries.async_remove(config_entry.entry_id)
)
return False

# Print startup message
startup = STARTUP.format(name=DOMAIN, version=VERSION, issueurl=ISSUE_URL)
_LOGGER.info(startup)
_LOGGER.info(
CC_STARTUP_VERSION.format(name=DOMAIN, version=VERSION, issue_link=ISSUE_URL)
)

# Check that all required files are present
file_check = await check_files(hass)
Expand All @@ -92,36 +94,25 @@ async def async_setup(hass, config):
hass.data[DOMAIN_DATA] = {}

# Get "global" configuration.
url = config[DOMAIN].get(CONF_URL)
api_key = config[DOMAIN].get(CONF_API_KEY)
verify_ssl = config[DOMAIN].get(CONF_VERIFY_SSL)
port_number = config[DOMAIN].get(CONF_PORT)
url = config_entry.data.get(CONF_URL)
api_key = config_entry.data.get(CONF_API_KEY)
verify_ssl = config_entry.data.get(CONF_VERIFY_SSL)
port_number = config_entry.data.get(CONF_PORT)
hash_key = hashlib.md5(api_key.encode('utf-8') + url.encode('utf-8')).hexdigest()

# Configure the client.
grocy = Grocy(url, api_key, port_number, verify_ssl)
hass.data[DOMAIN_DATA]["client"] = GrocyData(hass, grocy)
hass.data[DOMAIN_DATA]["hash_key"] = hash_key

# Load platforms
for platform in PLATFORMS:
# Get platform specific configuration
platform_config = config[DOMAIN].get(platform, {})

# If platform is not enabled, skip.
if not platform_config:
continue

for entry in platform_config:
entry_config = entry

# If entry is not enabled, skip.
if not entry_config[CONF_ENABLED]:
continue

hass.async_create_task(
discovery.async_load_platform(
hass, platform, DOMAIN, entry_config, config
)
)
# Add sensor
hass.async_add_job(
hass.config_entries.async_forward_entry_setup(config_entry, "sensor")
)
# Add sensor
hass.async_add_job(
hass.config_entries.async_forward_entry_setup(config_entry, "binary_sensor")
)

@callback
def handle_add_product(call):
Expand Down Expand Up @@ -167,36 +158,82 @@ def handle_execute_chore(call):

return True


class GrocyData:
"""This class handle communication and stores the data."""

def __init__(self, hass, client):
"""Initialize the class."""
self.hass = hass
self.client = client
self.sensor_types_dict = { STOCK_NAME : self.async_update_stock,
CHORES_NAME : self.async_update_chores,
SHOPPING_LIST_NAME : self.async_update_shopping_list,
EXPIRING_PRODUCTS_NAME : self.async_update_expiring_products,
EXPIRED_PRODUCTS_NAME : self.async_update_expired_products,
MISSING_PRODUCTS_NAME : self.async_update_missing_products,
}
self.sensor_update_dict = { STOCK_NAME : None,
CHORES_NAME : None,
SHOPPING_LIST_NAME : None,
EXPIRING_PRODUCTS_NAME : None,
EXPIRED_PRODUCTS_NAME : None,
MISSING_PRODUCTS_NAME : None,
}

async def async_update_data(self, sensor_type):
"""Update data."""
sensor_update = self.sensor_update_dict[sensor_type]
db_changed = await self.hass.async_add_executor_job(self.client.get_last_db_changed)
if db_changed != sensor_update:
self.sensor_update_dict[sensor_type] = db_changed
if sensor_type in self.sensor_types_dict:
# This is where the main logic to update platform data goes.
self.hass.async_create_task(self.sensor_types_dict[sensor_type]())

@Throttle(MIN_TIME_BETWEEN_UPDATES)
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"] = (
self.hass.data[DOMAIN_DATA][STOCK_NAME] = (
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"] = (
self.hass.data[DOMAIN_DATA][CHORES_NAME] = (
await self.hass.async_add_executor_job(self.client.chores, [True]))

@Throttle(MIN_TIME_BETWEEN_UPDATES)
async def async_update_shopping_list(self):
"""Update data."""
# This is where the main logic to update platform data goes.
self.hass.data[DOMAIN_DATA][SHOPPING_LIST_NAME] = (
await self.hass.async_add_executor_job(self.client.shopping_list, [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]["expiring_products"] = (
self.hass.data[DOMAIN_DATA][EXPIRING_PRODUCTS_NAME] = (
await self.hass.async_add_executor_job(
self.client.expiring_products, [True]))

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

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


async def check_files(hass):
Expand All @@ -216,3 +253,18 @@ async def check_files(hass):
returnvalue = True

return returnvalue

async def async_remove_entry(hass, config_entry):
"""Handle removal of an entry."""
try:
await hass.config_entries.async_forward_entry_unload(config_entry, "sensor")
_LOGGER.info("Successfully removed sensor from the grocy integration")
except ValueError as error:
_LOGGER.exception(error)
pass
try:
await hass.config_entries.async_forward_entry_unload(config_entry, "binary_sensor")
_LOGGER.info("Successfully removed sensor from the grocy integration")
except ValueError as error:
_LOGGER.exception(error)
pass
Loading

0 comments on commit 45891ad

Please sign in to comment.