forked from platypush/platypush
Added native support for switch entities to the smartthings plugin
This commit is contained in:
parent
b9c78ad913
commit
9d9ec1dc59
1 changed files with 94 additions and 51 deletions
|
@ -18,7 +18,7 @@ class SmartthingsPlugin(SwitchPlugin):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_timeout = aiohttp.ClientTimeout(total=20.)
|
_timeout = aiohttp.ClientTimeout(total=20.0)
|
||||||
|
|
||||||
def __init__(self, access_token: str, **kwargs):
|
def __init__(self, access_token: str, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -44,45 +44,30 @@ class SmartthingsPlugin(SwitchPlugin):
|
||||||
async def _refresh_locations(self, api):
|
async def _refresh_locations(self, api):
|
||||||
self._locations = await api.locations()
|
self._locations = await api.locations()
|
||||||
|
|
||||||
self._locations_by_id = {
|
self._locations_by_id = {loc.location_id: loc for loc in self._locations}
|
||||||
loc.location_id: loc
|
|
||||||
for loc in self._locations
|
|
||||||
}
|
|
||||||
|
|
||||||
self._locations_by_name = {
|
self._locations_by_name = {loc.name: loc for loc in self._locations}
|
||||||
loc.name: loc
|
|
||||||
for loc in self._locations
|
|
||||||
}
|
|
||||||
|
|
||||||
async def _refresh_devices(self, api):
|
async def _refresh_devices(self, api):
|
||||||
self._devices = await api.devices()
|
self._devices = await api.devices()
|
||||||
|
|
||||||
self._devices_by_id = {
|
self._devices_by_id = {dev.device_id: dev for dev in self._devices}
|
||||||
dev.device_id: dev
|
|
||||||
for dev in self._devices
|
|
||||||
}
|
|
||||||
|
|
||||||
self._devices_by_name = {
|
self._devices_by_name = {dev.label: dev for dev in self._devices}
|
||||||
dev.label: dev
|
|
||||||
for dev in self._devices
|
|
||||||
}
|
|
||||||
|
|
||||||
async def _refresh_rooms(self, api, location_id: str):
|
async def _refresh_rooms(self, api, location_id: str):
|
||||||
self._rooms_by_location[location_id] = await api.rooms(location_id=location_id)
|
self._rooms_by_location[location_id] = await api.rooms(location_id=location_id)
|
||||||
|
|
||||||
self._rooms_by_id.update(**{
|
self._rooms_by_id.update(
|
||||||
room.room_id: room
|
**{room.room_id: room for room in self._rooms_by_location[location_id]}
|
||||||
for room in self._rooms_by_location[location_id]
|
)
|
||||||
})
|
|
||||||
|
|
||||||
self._rooms_by_location_and_id[location_id] = {
|
self._rooms_by_location_and_id[location_id] = {
|
||||||
room.room_id: room
|
room.room_id: room for room in self._rooms_by_location[location_id]
|
||||||
for room in self._rooms_by_location[location_id]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self._rooms_by_location_and_name[location_id] = {
|
self._rooms_by_location_and_name[location_id] = {
|
||||||
room.name: room
|
room.name: room for room in self._rooms_by_location[location_id]
|
||||||
for room in self._rooms_by_location[location_id]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async def _refresh_info(self):
|
async def _refresh_info(self):
|
||||||
|
@ -127,7 +112,7 @@ class SmartthingsPlugin(SwitchPlugin):
|
||||||
'rooms': {
|
'rooms': {
|
||||||
room.room_id: self._room_to_dict(room)
|
room.room_id: self._room_to_dict(room)
|
||||||
for room in self._rooms_by_location.get(location.location_id, {})
|
for room in self._rooms_by_location.get(location.location_id, {})
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -257,12 +242,18 @@ class SmartthingsPlugin(SwitchPlugin):
|
||||||
"""
|
"""
|
||||||
self.refresh_info()
|
self.refresh_info()
|
||||||
return {
|
return {
|
||||||
'locations': {loc.location_id: self._location_to_dict(loc) for loc in self._locations},
|
'locations': {
|
||||||
'devices': {dev.device_id: self._device_to_dict(dev) for dev in self._devices},
|
loc.location_id: self._location_to_dict(loc) for loc in self._locations
|
||||||
|
},
|
||||||
|
'devices': {
|
||||||
|
dev.device_id: self._device_to_dict(dev) for dev in self._devices
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def get_location(self, location_id: Optional[str] = None, name: Optional[str] = None) -> dict:
|
def get_location(
|
||||||
|
self, location_id: Optional[str] = None, name: Optional[str] = None
|
||||||
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
Get the info of a location by ID or name.
|
Get the info of a location by ID or name.
|
||||||
|
|
||||||
|
@ -296,10 +287,15 @@ class SmartthingsPlugin(SwitchPlugin):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
assert location_id or name, 'Specify either location_id or name'
|
assert location_id or name, 'Specify either location_id or name'
|
||||||
if location_id not in self._locations_by_id or name not in self._locations_by_name:
|
if (
|
||||||
|
location_id not in self._locations_by_id
|
||||||
|
or name not in self._locations_by_name
|
||||||
|
):
|
||||||
self.refresh_info()
|
self.refresh_info()
|
||||||
|
|
||||||
location = self._locations_by_id.get(location_id, self._locations_by_name.get(name))
|
location = self._locations_by_id.get(
|
||||||
|
location_id, self._locations_by_name.get(name)
|
||||||
|
)
|
||||||
assert location, 'Location {} not found'.format(location_id or name)
|
assert location, 'Location {} not found'.format(location_id or name)
|
||||||
return self._location_to_dict(location)
|
return self._location_to_dict(location)
|
||||||
|
|
||||||
|
@ -340,24 +336,41 @@ class SmartthingsPlugin(SwitchPlugin):
|
||||||
device = self._get_device(device)
|
device = self._get_device(device)
|
||||||
return self._device_to_dict(device)
|
return self._device_to_dict(device)
|
||||||
|
|
||||||
async def _execute(self, device_id: str, capability: str, command, component_id: str, args: Optional[list]):
|
async def _execute(
|
||||||
|
self,
|
||||||
|
device_id: str,
|
||||||
|
capability: str,
|
||||||
|
command,
|
||||||
|
component_id: str,
|
||||||
|
args: Optional[list],
|
||||||
|
):
|
||||||
import pysmartthings
|
import pysmartthings
|
||||||
|
|
||||||
async with aiohttp.ClientSession(timeout=self._timeout) as session:
|
async with aiohttp.ClientSession(timeout=self._timeout) as session:
|
||||||
api = pysmartthings.SmartThings(session, self._access_token)
|
api = pysmartthings.SmartThings(session, self._access_token)
|
||||||
device = await api.device(device_id)
|
device = await api.device(device_id)
|
||||||
ret = await device.command(component_id=component_id, capability=capability, command=command, args=args)
|
ret = await device.command(
|
||||||
|
component_id=component_id,
|
||||||
|
capability=capability,
|
||||||
|
command=command,
|
||||||
|
args=args,
|
||||||
|
)
|
||||||
|
|
||||||
assert ret, 'The command {capability}={command} failed on device {device}'.format(
|
assert (
|
||||||
capability=capability, command=command, device=device_id)
|
ret
|
||||||
|
), 'The command {capability}={command} failed on device {device}'.format(
|
||||||
|
capability=capability, command=command, device=device_id
|
||||||
|
)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def execute(self,
|
def execute(
|
||||||
|
self,
|
||||||
device: str,
|
device: str,
|
||||||
capability: str,
|
capability: str,
|
||||||
command,
|
command,
|
||||||
component_id: str = 'main',
|
component_id: str = 'main',
|
||||||
args: Optional[list] = None):
|
args: Optional[list] = None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Execute a command on a device.
|
Execute a command on a device.
|
||||||
|
|
||||||
|
@ -388,17 +401,39 @@ class SmartthingsPlugin(SwitchPlugin):
|
||||||
loop = asyncio.new_event_loop()
|
loop = asyncio.new_event_loop()
|
||||||
try:
|
try:
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
loop.run_until_complete(self._execute(
|
loop.run_until_complete(
|
||||||
device_id=device.device_id, capability=capability, command=command,
|
self._execute(
|
||||||
component_id=component_id, args=args))
|
device_id=device.device_id,
|
||||||
|
capability=capability,
|
||||||
|
command=command,
|
||||||
|
component_id=component_id,
|
||||||
|
args=args,
|
||||||
|
)
|
||||||
|
)
|
||||||
finally:
|
finally:
|
||||||
loop.stop()
|
loop.stop()
|
||||||
|
|
||||||
@staticmethod
|
async def _get_device_status(self, api, device_id: str) -> dict:
|
||||||
async def _get_device_status(api, device_id: str) -> dict:
|
from platypush.entities.switches import Switch
|
||||||
|
|
||||||
device = await api.device(device_id)
|
device = await api.device(device_id)
|
||||||
await device.status.refresh()
|
await device.status.refresh()
|
||||||
|
|
||||||
|
if 'switch' in device.capabilities:
|
||||||
|
self.publish_entities(
|
||||||
|
[ # type: ignore
|
||||||
|
Switch(
|
||||||
|
id=device_id,
|
||||||
|
name=device.label,
|
||||||
|
state=device.status.switch,
|
||||||
|
data={
|
||||||
|
'location_id': getattr(device, 'location_id', None),
|
||||||
|
'room_id': getattr(device, 'room_id', None),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'device_id': device_id,
|
'device_id': device_id,
|
||||||
'name': device.label,
|
'name': device.label,
|
||||||
|
@ -407,7 +442,7 @@ class SmartthingsPlugin(SwitchPlugin):
|
||||||
for cap in device.capabilities
|
for cap in device.capabilities
|
||||||
if hasattr(device.status, cap)
|
if hasattr(device.status, cap)
|
||||||
and not callable(getattr(device.status, cap))
|
and not callable(getattr(device.status, cap))
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
async def _refresh_status(self, devices: List[str]) -> List[dict]:
|
async def _refresh_status(self, devices: List[str]) -> List[dict]:
|
||||||
|
@ -434,7 +469,9 @@ class SmartthingsPlugin(SwitchPlugin):
|
||||||
parse_device_id(dev)
|
parse_device_id(dev)
|
||||||
|
|
||||||
# Fail if some devices haven't been found after refreshing
|
# Fail if some devices haven't been found after refreshing
|
||||||
assert not missing_device_ids, 'Could not find the following devices: {}'.format(list(missing_device_ids))
|
assert (
|
||||||
|
not missing_device_ids
|
||||||
|
), 'Could not find the following devices: {}'.format(list(missing_device_ids))
|
||||||
|
|
||||||
async with aiohttp.ClientSession(timeout=self._timeout) as session:
|
async with aiohttp.ClientSession(timeout=self._timeout) as session:
|
||||||
api = pysmartthings.SmartThings(session, self._access_token)
|
api = pysmartthings.SmartThings(session, self._access_token)
|
||||||
|
@ -529,13 +566,19 @@ class SmartthingsPlugin(SwitchPlugin):
|
||||||
async with aiohttp.ClientSession(timeout=self._timeout) as session:
|
async with aiohttp.ClientSession(timeout=self._timeout) as session:
|
||||||
api = pysmartthings.SmartThings(session, self._access_token)
|
api = pysmartthings.SmartThings(session, self._access_token)
|
||||||
dev = await api.device(device_id)
|
dev = await api.device(device_id)
|
||||||
assert 'switch' in dev.capabilities, 'The device {} has no switch capability'.format(dev.label)
|
assert (
|
||||||
|
'switch' in dev.capabilities
|
||||||
|
), 'The device {} has no switch capability'.format(dev.label)
|
||||||
|
|
||||||
await dev.status.refresh()
|
await dev.status.refresh()
|
||||||
state = 'off' if dev.status.switch else 'on'
|
state = 'off' if dev.status.switch else 'on'
|
||||||
ret = await dev.command(component_id='main', capability='switch', command=state, args=args)
|
ret = await dev.command(
|
||||||
|
component_id='main', capability='switch', command=state, args=args
|
||||||
|
)
|
||||||
|
|
||||||
assert ret, 'The command switch={state} failed on device {device}'.format(state=state, device=dev.label)
|
assert ret, 'The command switch={state} failed on device {device}'.format(
|
||||||
|
state=state, device=dev.label
|
||||||
|
)
|
||||||
return not dev.status.switch
|
return not dev.status.switch
|
||||||
|
|
||||||
with self._refresh_lock:
|
with self._refresh_lock:
|
||||||
|
|
Loading…
Reference in a new issue