164 lines
4.7 KiB
Python
164 lines
4.7 KiB
Python
# -*- coding: utf8 -*-
|
|
import asyncio
|
|
import logging
|
|
import uuid
|
|
|
|
import aiojobs.aiohttp
|
|
from homehub_backend.lib.services import BaseService, ServiceData
|
|
from homehub_tradfri.defs import kServiceTradfri
|
|
import pytradfri
|
|
import pytradfri.api.aiocoap_api
|
|
|
|
LOGGER = logging.getLogger('homehub.tradfri.services')
|
|
|
|
|
|
class TradfriService(BaseService):
|
|
KIND = kServiceTradfri
|
|
|
|
def __init__(self, app, instance, characteristics):
|
|
super(TradfriService, self).__init__(app, instance, characteristics)
|
|
self.api_factory = None
|
|
self.gateway = None
|
|
self.job = None
|
|
self.started = False
|
|
|
|
def state_key(self):
|
|
state_key = super(TradfriService, self).state_key()
|
|
return '{state_key}.{host}.{key}'.format(
|
|
state_key=state_key,
|
|
host=self.characteristics['host'],
|
|
key=self.characteristics['key'],
|
|
)
|
|
|
|
async def current_data(self):
|
|
result = ServiceData()
|
|
|
|
try:
|
|
result.data = {
|
|
'groups': await self.get_groups(),
|
|
'lights': await self.get_lights(),
|
|
}
|
|
except Exception as exc:
|
|
LOGGER.error('Unhandled exception!', exc_info=exc)
|
|
result.exception = exc
|
|
|
|
return result
|
|
|
|
async def call_api(self, commands):
|
|
api = self.api_factory.request
|
|
result = await api(commands)
|
|
|
|
return result
|
|
|
|
async def get_lights(self):
|
|
lights_command = self.gateway.get_devices()
|
|
lights_commands = await self.call_api(lights_command)
|
|
|
|
result = await self.call_api(lights_commands)
|
|
return [
|
|
{
|
|
'id': light.id,
|
|
'name': light.name,
|
|
'state': light.light_control.lights[0].state,
|
|
'reachable': light.reachable,
|
|
'dimmer': light.light_control.lights[0].dimmer,
|
|
}
|
|
for light in filter(lambda x: x.has_light_control, result)
|
|
]
|
|
|
|
async def get_light(self, light_id):
|
|
light_command = self.gateway.get_device(light_id)
|
|
light = await self.call_api(light_command)
|
|
|
|
return light
|
|
|
|
async def set_light_state(self, light_id, next_state):
|
|
light = await self.get_light(light_id)
|
|
|
|
set_state_command = light.light_control.set_state(next_state, index=0)
|
|
await self.call_api(set_state_command)
|
|
|
|
async def get_groups(self):
|
|
groups_command = self.gateway.get_groups()
|
|
groups_commands = await self.call_api(groups_command)
|
|
|
|
result = await self.call_api(groups_commands)
|
|
return [
|
|
{
|
|
'id': group.id,
|
|
'name': group.name,
|
|
'member_ids': group.member_ids,
|
|
}
|
|
for group in result
|
|
]
|
|
|
|
async def set_lights_state(self, light_ids, next_state):
|
|
_ = await asyncio.gather(*[
|
|
self.set_light_state(light_id, next_state)
|
|
for light_id in light_ids
|
|
])
|
|
|
|
lights = await self.get_lights()
|
|
return list(filter(lambda light: light['id'] in light_ids, lights))
|
|
|
|
async def worker(self):
|
|
while True:
|
|
await asyncio.sleep(60)
|
|
await self.notify()
|
|
|
|
if self.job.closed:
|
|
break
|
|
|
|
async def start(self):
|
|
if not self.started:
|
|
psk_id = None
|
|
psk = None
|
|
|
|
if self.state is not None:
|
|
psk_id = self.state.get('psk_id')
|
|
psk = self.state.get('psk')
|
|
|
|
if not psk_id:
|
|
psk_id = uuid.uuid4().hex
|
|
|
|
LOGGER.debug('TradfriService.start() {psk_id} {has_psk} {characteristics}'.format(
|
|
psk_id=psk_id,
|
|
has_psk=(psk is not None),
|
|
characteristics=self.characteristics,
|
|
))
|
|
|
|
self.api_factory = pytradfri.api.aiocoap_api.APIFactory(
|
|
host=self.characteristics['host'],
|
|
psk_id=psk_id,
|
|
psk=psk,
|
|
)
|
|
|
|
if not psk:
|
|
psk = await self.api_factory.generate_psk(
|
|
self.characteristics['key'],
|
|
)
|
|
|
|
self.gateway = pytradfri.Gateway()
|
|
|
|
if not self.job:
|
|
scheduler = aiojobs.aiohttp.get_scheduler_from_app(self.app)
|
|
self.job = await scheduler.spawn(self.worker())
|
|
|
|
self.set_state({
|
|
'psk_id': psk_id,
|
|
'psk': psk,
|
|
})
|
|
|
|
self.started = True
|
|
|
|
async def stop(self):
|
|
LOGGER.debug('TradfriService.stop()')
|
|
if self.gateway:
|
|
self.gateway = None
|
|
|
|
if self.api_factory:
|
|
self.api_factory = None
|
|
|
|
if self.job is not None and not self.job.closed:
|
|
await self.job.close()
|