Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Iota wallet #11398

Merged
merged 18 commits into from
Jan 25, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ omit =
homeassistant/components/feedreader.py
homeassistant/components/foursquare.py
homeassistant/components/ifttt.py
homeassistant/components/iota.py
homeassistant/components/image_processing/dlib_face_detect.py
homeassistant/components/image_processing/dlib_face_identify.py
homeassistant/components/image_processing/seven_segments.py
Expand Down Expand Up @@ -538,6 +539,7 @@ omit =
homeassistant/components/sensor/imap.py
homeassistant/components/sensor/imap_email_content.py
homeassistant/components/sensor/influxdb.py
homeassistant/components/sensor/iota.py
homeassistant/components/sensor/irish_rail_transport.py
homeassistant/components/sensor/kwb.py
homeassistant/components/sensor/lacrosse.py
Expand Down
98 changes: 98 additions & 0 deletions homeassistant/components/iota.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is only a sensor. This doesn't require to include a component.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, not yet. I am going to add more features to this component. Is it necessary to be a sensor only PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's the right way to create a component if there is coming a binary sensor or switch platform which can share the common stuff. Otherwise everything can be handled in the platform itself as no sharing is needed.

Support for IOTA wallets.

For more details about this component, please refer to the documentation at
https://home-assistant.io/components/iota/
"""
import logging
from datetime import timedelta

import voluptuous as vol

import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.discovery import load_platform
from homeassistant.helpers.entity import Entity

DOMAIN = 'iota'

REQUIREMENTS = ['pyota==2.0.3']

IOTA_PLATFORMS = ['sensor']

SCAN_INTERVAL = timedelta(minutes=10)

CONF_IRI = 'iri'
CONF_TESTNET = 'testnet'
CONF_WALLETS = 'wallets'
CONF_WALLET_NAME = 'name'
CONF_WALLET_SEED = 'seed'

WALLET_CONFIG = vol.Schema({
vol.Required(CONF_WALLET_NAME): cv.string,
vol.Required(CONF_WALLET_SEED): cv.string,
})

CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_IRI): vol.All(str),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use cv.string.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove vol.All.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

vol.Optional(CONF_TESTNET, default=False): cv.boolean,
vol.Required(CONF_WALLETS): vol.All(cv.ensure_list, [WALLET_CONFIG])
})
}, extra=vol.ALLOW_EXTRA)

_LOGGER = logging.getLogger(__name__)


def setup(hass, config):
"""Setup IOTA component"""

# Set domain sepecific data
iota_config = config[DOMAIN]
hass.data[DOMAIN] = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Configuration is supposed to be past in discovery info dict, ie fourth parameter of load_platform. Only non serializable objects need to go in hass.data.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thx. Changed it.

Copy link
Member

@MartinHjelmare MartinHjelmare Jan 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So now you don't need to store the iota_config in hass.data. You also need to change setup_platform to access the discovery_info instead of hass.data.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

'iri': iota_config[CONF_IRI],
'is_testnet': iota_config[CONF_TESTNET],
'wallets': iota_config[CONF_WALLETS]
}

# Set states of IRI config
hass.states.set('iota.iri', hass.data[DOMAIN]['iri'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you want to set these states?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the URL of the IOTA node to connect to, which may change. It is a separate state in order to see current URL of the node in the UI.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Report it as a state attribute on the node sensor entity instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

hass.states.set('iota.is_testnet', hass.data[DOMAIN]['is_testnet'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

Copy link
Contributor Author

@jinnerbichler jinnerbichler Jan 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done ;)


# Load platforms
for platform in IOTA_PLATFORMS:
load_platform(hass, platform, DOMAIN, {}, iota_config)

return True


class IotaDevice(Entity):
"""Representation of a IOTA device."""

def __init__(self, name, seed, iri, is_testnet=False):
"""Initialisation of the IOTA device."""
self._name = name
self._seed = seed
self.iri = iri
self.is_testnet = is_testnet

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

@property
def should_poll(self):
"""Get polling requirement from IOTA device."""
return True
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the default.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thx. removed it


@property
def device_state_attributes(self):
"""Return the state attributes of the device."""
attr = {CONF_WALLET_NAME: self._name}
return attr

@property
def api(self):
""" Constructs API object for interaction with the IRI node"""
from iota import Iota
return Iota(adapter=self.iri, seed=self._seed)
87 changes: 87 additions & 0 deletions homeassistant/components/sensor/iota.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import logging

from homeassistant.components.iota import DOMAIN as IOTA_DOMAIN, IotaDevice

_LOGGER = logging.getLogger(__name__)

DEPENDENCIES = ['iota']


def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the IOTA sensor."""

# Add sensors for wallet balance
iota_config = hass.data[IOTA_DOMAIN]
balance_sensors = [IotaBalanceSensor(wallet, iota_config) for wallet in iota_config['wallets']]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line too long (99 > 79 characters)

add_devices(balance_sensors)

# Add sensor for node information
add_devices([IotaNodeSensor(iota_config=iota_config)])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add all devices in one go.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done



class IotaBalanceSensor(IotaDevice):
"""Implement an IOTA sensor for displaying wallets balance."""

def __init__(self, wallet_config, iota_config):
"""Initialize the sensor."""

super().__init__(name=wallet_config['name'], seed=wallet_config['seed'],

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line too long (80 > 79 characters)

iri=iota_config['iri'], is_testnet=iota_config['is_testnet'])

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line too long (86 > 79 characters)

self._state = 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set it to None.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


@property
def name(self):
"""Return the name of the sensor."""
return '{} Balance'.format(self._name)

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

@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
return 'IOTA'

def update(self):
"""Fetch new balance from IRI."""
self._state = self.api.get_inputs()['totalBalance']


class IotaNodeSensor(IotaDevice):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that this should be a binary sensor. A node can be offline or online.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, a node can have multiple attributes, which are changing over time (https://iota.readme.io/v1.2.0/reference#getnodeinfo)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Attributes and state are not the same. A binary sensor can have attributes as well. If a node is always present then it's doesn't make sense.

There are a dozen values available from getNodeInfo. Those details should be exposed as attributes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Information about the node is part of the attributes of the node sensor and should be part of the wallet.

"""Implement an IOTA sensor for displaying attributes of node."""

def __init__(self, iota_config):
"""Initialize the sensor."""

super().__init__(name='Node Info', seed=None,
iri=iota_config['iri'], is_testnet=iota_config['is_testnet'])

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line too long (86 > 79 characters)

self._state = ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unknown state should be initialized with None.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

self._attr = dict()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Init with {}.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


@property
def name(self):
"""Return the name of the sensor."""
return 'IOTA Node'

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

@property
def device_state_attributes(self):
"""Return the state attributes of the device."""
return self._attr
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment this will stay empty.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed that line.


@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
return ''
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a valid unit of measurement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed it


def update(self):
"""Fetch new attribures IRI node."""
node_info = self.api.get_node_info()
self._state = node_info.get('appVersion')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is it useful to know the appVersion of a node as state? Will that change on a regular base? If not then it's just another attribute.

Copy link
Contributor Author

@jinnerbichler jinnerbichler Jan 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value of appVersion tells if the version of the node is up-to-date. Yes, the version of the node changes almost every other week.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would still require an additional part because it only shows the version and not if the node is up-to-date.

self._attr = {k: str(v) for k, v in node_info.items()} # convert values to raw string formats

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line too long (102 > 79 characters)

3 changes: 3 additions & 0 deletions requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1209,3 +1209,6 @@ zeroconf==0.19.1

# homeassistant.components.media_player.ziggo_mediabox_xl
ziggo-mediabox-xl==1.0.0

# homeassistant.components.iota
pyota==2.0.3