Add Google pubsub component (#20049)

* Add google pubsub component

* Add tests and requirements

* Make python3.5 compatible

* Fix linting

* Fix pubsub test

* Code review comments

* Add missing docstrings

* Update requirements_all

* Code review comment

- Remove pylint ignores
- Don't modify global environment
This commit is contained in:
Tim van Cann
2019-02-10 21:45:46 +01:00
committed by Anders Melchiorsen
parent 1ebdc2e2c2
commit d049b521b2
3 changed files with 124 additions and 0 deletions

View File

@ -0,0 +1,99 @@
"""
Support for Google Cloud Pub/Sub.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/google_pubsub/
"""
import datetime
import json
import logging
import os
from typing import Any, Dict
import voluptuous as vol
from homeassistant.const import (
EVENT_STATE_CHANGED, STATE_UNAVAILABLE, STATE_UNKNOWN)
from homeassistant.core import Event, HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entityfilter import FILTER_SCHEMA
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['google-cloud-pubsub==0.39.1']
DOMAIN = 'google_pubsub'
CONF_PROJECT_ID = 'project_id'
CONF_TOPIC_NAME = 'topic_name'
CONF_SERVICE_PRINCIPAL = 'credentials_json'
CONF_FILTER = 'filter'
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_PROJECT_ID): cv.string,
vol.Required(CONF_TOPIC_NAME): cv.string,
vol.Required(CONF_SERVICE_PRINCIPAL): cv.string,
vol.Required(CONF_FILTER): FILTER_SCHEMA
}),
}, extra=vol.ALLOW_EXTRA)
def setup(hass: HomeAssistant, yaml_config: Dict[str, Any]):
"""Activate Google Pub/Sub component."""
from google.cloud import pubsub_v1
config = yaml_config[DOMAIN]
project_id = config[CONF_PROJECT_ID]
topic_name = config[CONF_TOPIC_NAME]
service_principal_path = os.path.join(hass.config.config_dir,
config[CONF_SERVICE_PRINCIPAL])
if not os.path.isfile(service_principal_path):
_LOGGER.error("Path to credentials file cannot be found")
return False
entities_filter = config[CONF_FILTER]
publisher = (pubsub_v1
.PublisherClient
.from_service_account_json(service_principal_path)
)
topic_path = publisher.topic_path(project_id, # pylint: disable=E1101
topic_name)
encoder = DateTimeJSONEncoder()
def send_to_pubsub(event: Event):
"""Send states to Pub/Sub."""
state = event.data.get('new_state')
if (state is None
or state.state in (STATE_UNKNOWN, '', STATE_UNAVAILABLE)
or not entities_filter(state.entity_id)):
return
as_dict = state.as_dict()
data = json.dumps(
obj=as_dict,
default=encoder.encode
).encode('utf-8')
publisher.publish(topic_path, data=data)
hass.bus.listen(EVENT_STATE_CHANGED, send_to_pubsub)
return True
class DateTimeJSONEncoder(json.JSONEncoder):
"""Encode python objects.
Additionally add encoding for datetime objects as isoformat.
"""
def default(self, o): # pylint: disable=E0202
"""Implement encoding logic."""
if isinstance(o, datetime.datetime):
return o.isoformat()
return super().default(o)

View File

@ -471,6 +471,9 @@ gntp==1.0.3
# homeassistant.components.google
google-api-python-client==1.6.4
# homeassistant.components.google_pubsub
google-cloud-pubsub==0.39.1
# homeassistant.components.googlehome
googledevices==1.0.2

View File

@ -0,0 +1,22 @@
"""The tests for the Google Pub/Sub component."""
from datetime import datetime
from homeassistant.components.google_pubsub import (
DateTimeJSONEncoder as victim)
class TestDateTimeJSONEncoder(object):
"""Bundle for DateTimeJSONEncoder tests."""
def test_datetime(self):
"""Test datetime encoding."""
time = datetime(2019, 1, 13, 12, 30, 5)
assert victim().encode(time) == '"2019-01-13T12:30:05"'
def test_no_datetime(self):
"""Test integer encoding."""
assert victim().encode(42) == '42'
def test_nested(self):
"""Test dictionary encoding."""
assert victim().encode({'foo': 'bar'}) == '{"foo": "bar"}'