Skip to content

Commit

Permalink
0.90.2 Add support for configuration_url, entity_category, some extra…
Browse files Browse the repository at this point in the history
… sensors (cpu, ram, net, live)
  • Loading branch information
quazzie committed Oct 29, 2021
1 parent 7d0e817 commit dc275ea
Show file tree
Hide file tree
Showing 4 changed files with 467 additions and 300 deletions.
190 changes: 129 additions & 61 deletions hass_client/Client.py
Original file line number Diff line number Diff line change
@@ -1,74 +1,102 @@
# -*- coding: utf-8 -*-
import json
import logging
from time import gmtime, strftime

from base import \
Application, \
Plugin, \
configuration, \
ConfigurationNumber, \
ConfigurationString, \
ConfigurationBool, \
ConfigurationList, \
implements, \
ISignalObserver, \
slot

from telldus import DeviceManager
#from time import gmtime, strftime

from base import Application, Plugin, configuration, ConfigurationNumber, ConfigurationString, ConfigurationBool, ConfigurationSelect, ConfigurationList, implements, ISignalObserver, slot # type: ignore
from hass_client.utils import getIpAddr

from telldus import DeviceManager # type: ignore
from tellduslive.base import TelldusLive # type: ignore

import Devices as devs
import logging
import paho.mqtt.client as mqtt
import paho.mqtt.client as mqtt # type: ignore

__name__ = 'HASSMQTT'


@configuration(
username=ConfigurationString(
defaultValue='',
title='Mqtt username',
description='Username for mqtt'
),
password=ConfigurationString(
defaultValue='',
title='Mqtt password',
description='Password for mqtt'
device_name=ConfigurationString(
defaultValue='ztest',
title='Device name',
description='Name of this device',
sortOrder=1
),
hostname=ConfigurationString(
defaultValue='',
title='Mqtt hostname',
description='Hostname for mqtt'
description='Hostname for mqtt',
sortOrder=2
),
port=ConfigurationNumber(
defaultValue=1883,
title='Mqtt port',
description='Port for mqtt'
description='Port for mqtt',
sortOrder=3
),
username=ConfigurationString(
defaultValue='',
title='Mqtt username',
description='Username for mqtt',
sortOrder=4
),
password=ConfigurationString(
defaultValue='',
title='Mqtt password',
description='Password for mqtt',
sortOrder=5
),
discovery_topic=ConfigurationString(
defaultValue='hatest',
title='Autodiscovery topic',
description='Homeassistants autodiscovery topic'
),
device_name=ConfigurationString(
defaultValue='ztest',
title='Device name',
description='Name of this device'
description='Homeassistants autodiscovery topic',
sortOrder=6
),
base_topic=ConfigurationString(
defaultValue='telldus',
title='Base topic',
description='Base topic for this device'
description='Base topic for this device, for ex. debug',
sortOrder=7
),
state_retain=ConfigurationBool(
defaultValue=True,
title='Retain state changes',
description='Post state changes with retain'
description='Post state changes with retain',
sortOrder=8
),
use_via=ConfigurationBool(
defaultValue=False,
title='Use via_device',
description='Use via_device to create device hierarchy. (Does not seem to work in home-assistant yet)'
title='Create sub devices/via_device (Missing in HA)',
description='Create sub devices and setup via_device to create device hierarchy. (Does not seem to work in home-assistant yet)',
sortOrder=9
),
useConfigUrl=ConfigurationBool(
defaultValue=False,
title='Support device configuration url (HA >= 2021.11)',
description='Requires HA >= 2021.11.0',
sortOrder=10
),
configUrl=ConfigurationSelect(
defaultValue='live',
title='Configuration url',
options={
'live': 'Use Telldus live as url',
'local': 'Use local IP as url'
},
sortOrder=11
),
useEntityCategories=ConfigurationBool(
defaultValue=False,
title='Support entity categories (HA >= 2021.11)',
description='Requires HA >= 2021.11.0',
sortOrder=12
),
device_topics=ConfigurationList(
defaultValue=[],
hidden=True
Expand All @@ -83,6 +111,7 @@ class Client(Plugin):

def __init__(self):
Application().registerShutdown(self.onShutdown)
self.live = TelldusLive(self.context) # pylint: disable=too-many-function-args

self.discovered_flag = False

Expand All @@ -98,14 +127,25 @@ def __init__(self):
if username != '':
self.client.username_pw_set(username, password)

self.hub = devs.HaHub(self.config('device_name'), self._buildTopic)
self._debug('Hub: %s' % json.dumps(self.hub.getConfig(None, False)))
useConfigUrl = self.config('useConfigUrl')
configUrl = 'https://live.telldus.se' if self.config('configUrl') == 'live' else ('http://%s' % getIpAddr())

self.devices = [self.hub]
self.hub = devs.HaHub(self.config('device_name'), self._buildTopic, configUrl if useConfigUrl else None)
self._debug('Hub: %s' % json.dumps(self._getDeviceConfig(self.hub)))

self.staticDevices = [
self.hub,
devs.HaLiveConnection(self.hub, self.live, self._buildTopic),
devs.HaIpAddr(self.hub, self._buildTopic),
devs.HaCpu(self.hub, self._buildTopic),
devs.HaRamFree(self.hub, self._buildTopic)
]
self.devices = self.staticDevices + []
Application().queue(self.discoverAndConnect)
Application().registerScheduledTask(self._updateTimedSensors, seconds=30)

def configWasUpdated(self, key, value):
if key in ['use_via', 'discovery_topic', 'device_name']:
if key in ['use_via', 'useConfigUrl', 'configUrl', 'useEntityCategories', 'discovery_topic', 'device_name']:
self.devices = []
self.cleanupDevices()
self.hub.deviceName = self.config('device_name')
Expand All @@ -122,6 +162,10 @@ def discoverAndConnect(self):
if self.config('hostname'):
self.connect()

def _updateTimedSensors(self):
for haDev in [x for x in self.devices if isinstance(x, devs.HaTimedSensor)]:
self.publishState(haDev)

def _debug(self, msg):
logging.info("HaClient: %s", msg)
if self.mqtt_connected_flag:
Expand All @@ -132,6 +176,12 @@ def _debug(self, msg):
def _buildTopic(self, type, id):
return '%s/%s/%s/%s' % (self.config('discovery_topic'), type, self.config('device_name'), id)

def _getDeviceConfig(self, haDev):
conf = haDev.getConfig()
if not self.config('useEntityCategories'):
conf.pop('entity_category', None)
return conf

def tearDown(self):
# remove plugin
self.devices = []
Expand All @@ -155,13 +205,16 @@ def connect(self):
def cleanupDevices(self):
if self.mqtt_connected_flag and self.discovered_flag:
# clean up published devices not found
devicesConfigured = self.config('devices_configured')
if devicesConfigured and devicesConfigured != '':
oldDevs = [tuple(x) for x in json.loads(devicesConfigured)]
for type, _, fullId in oldDevs:
oldTopic = self._buildTopic(type, fullId)
self.removeDeviceTopics(oldTopic)
self.setConfig('devices_configured', None)
try:
devicesConfigured = self.config('devices_configured')
if devicesConfigured and devicesConfigured != 'None' and devicesConfigured != '':
oldDevs = [tuple(x) for x in json.loads(devicesConfigured)]
for type, _, fullId in oldDevs:
oldTopic = self._buildTopic(type, fullId)
self.removeDeviceTopics(oldTopic)
self.setConfig('devices_configured', None)
except:
pass

savedTopics = self.config('device_topics')
devTopics = [x.getDeviceTopic() for x in self.devices]
Expand Down Expand Up @@ -197,11 +250,11 @@ def discover(self):
self.discovered_flag = False
self._debug('Discovering devices ...')

self.devices = [self.hub]
self.devices = self.staticDevices + []
devMgr = DeviceManager(self.context)
for device in devMgr.retrieveDevices():
haDevs = devs.createDevices(device, self._buildTopic)
self._debug('Discovered %s' % json.dumps(self._debugDevice(device)))
haDevs = devs.createDevices(device, self.hub, self._buildTopic, self.config('use_via'))
self._debug('Discovered %s' % json.dumps(self._debugDevice(device, haDevs)))
for haDev in haDevs:
self.devices.append(haDev)

Expand All @@ -215,15 +268,19 @@ def publishState(self, haDev):
self._debug('publish state for (%s) %s : %s' % (haDev.getID(), topic, states))
if self.mqtt_connected_flag:
for state in (states if isinstance(states, list) else [states]):
self.client.publish(topic, state, 0, self.config('state_retain'))
self.client.publish(topic, str(state), 0, self.config('state_retain'))

def publishDevices(self):
for device in self.devices:
self.publishDevice(device)
self.publishState(device)

def publishDevice(self, haDev):
config = haDev.getConfig(self.hub, self.config('use_via'))
config = self._getDeviceConfig(haDev)
if self.config('useEntityCategories'):
config.update({
'entity_category': haDev.getCategory()
})
topic = '%s/config' % haDev.getDeviceTopic()
self._debug('publish config for (%s) %s : %s' % (haDev.getID(), topic, json.dumps(config)))
if self.mqtt_connected_flag:
Expand All @@ -240,8 +297,7 @@ def removeDeviceTopics(self, devTopic):
self.client.publish('%s/config' % devTopic, None, 0, True)
self.client.publish('%s/state' % devTopic, None, 0, True)

def _debugDevice(self, device):
haDevs = devs.createDevices(device, self._buildTopic)
def _debugDevice(self, device, haDevs):
return {
'deviceId': device.id(),
'name': device.name(),
Expand All @@ -253,16 +309,16 @@ def _debugDevice(self, device):
'typeStr': device.typeString(),
'sensors': device.sensorValues(),
'state': device.state(),
'devices': [x.getConfig(self.hub, self.config('use_via')) for x in haDevs]
'devices': [self._getDeviceConfig(x) for x in haDevs]
}

@slot('deviceAdded')
def onDeviceAdded(self, device):
self._debug('Device added %s %s' % (device.id(), device.name()))

if device not in (x.device for x in self.devices):
haDevs = devs.createDevices(device, self._buildTopic)
self._debug('New discovery %s' % json.dumps(self._debugDevice(device)))
if device not in (x.device for x in self.devices if hasattr(x, 'device')):
haDevs = devs.createDevices(device, self.hub, self._buildTopic, self.config('use_via'))
self._debug('New discovery %s' % json.dumps(self._debugDevice(device, haDevs)))
for haDev in haDevs:
self.devices.append(haDev)
self.publishDevice(haDev)
Expand All @@ -288,7 +344,7 @@ def onDeviceUpdate(self, device):
def onDeviceStateChanged(self, device, state, stateValue, origin=None):
self._debug('Device state changed (%s) state: %s value: %s origin: %s' %
(device.id(), state, stateValue, origin))
haDev = next((x for x in self.devices if x.device == device), None)
haDev = next((x for x in self.devices if x.deviceId == device.id()), None)
if haDev:
self.publishState(haDev)
else:
Expand All @@ -298,9 +354,21 @@ def onDeviceStateChanged(self, device, state, stateValue, origin=None):
def onSensorValueUpdated(self, device, valueType, value, scale):
self._debug('Sensor value changed (%s) type: %s scale: %s value: %s' %
(device.id(), valueType, scale, value))
haDev = next((x for x in self.devices if x.device == device and x.sensorType ==
haDev = next((x for x in self.devices if hasattr(x, 'device') and x.device == device and x.sensorType ==
valueType and x.sensorScale == scale), None)
if haDev:
self.publishState(haDev)
else:
self._debug('failed to find device for sensor change %s %s %s' % (device.id(), valueType, scale))

@slot('liveRegistered')
def liveRegistered(self, _msg, _refReq):
liveSensor = next(x for x in self.devices if isinstance(x, devs.HaLiveConnection))
if liveSensor:
self.publishState(liveSensor)

@slot('liveDisconnected')
def liveDisconnected(self):
liveSensor = next(x for x in self.devices if isinstance(x, devs.HaLiveConnection))
if liveSensor:
self.publishState(liveSensor)
Loading

0 comments on commit dc275ea

Please sign in to comment.