Add support for v1 and v2 HomeKit fans. (#30503)
* Add support for v1 and v2 HomeKit fans. * Lint fixpull/30505/head
parent
8625e962ce
commit
fffc5a5fbb
|
@ -27,4 +27,6 @@ HOMEKIT_ACCESSORY_DISPATCH = {
|
|||
"temperature": "sensor",
|
||||
"battery": "sensor",
|
||||
"smoke": "binary_sensor",
|
||||
"fan": "fan",
|
||||
"fanv2": "fan",
|
||||
}
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
"""Support for Homekit fans."""
|
||||
import logging
|
||||
|
||||
from homekit.model.characteristics import CharacteristicsTypes
|
||||
|
||||
from homeassistant.components.fan import (
|
||||
DIRECTION_FORWARD,
|
||||
DIRECTION_REVERSE,
|
||||
SPEED_HIGH,
|
||||
SPEED_LOW,
|
||||
SPEED_MEDIUM,
|
||||
SPEED_OFF,
|
||||
SUPPORT_DIRECTION,
|
||||
SUPPORT_OSCILLATE,
|
||||
SUPPORT_SET_SPEED,
|
||||
FanEntity,
|
||||
)
|
||||
|
||||
from . import KNOWN_DEVICES, HomeKitEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# 0 is clockwise, 1 is counter-clockwise. The match to forward and reverse is so that
|
||||
# its consistent with homeassistant.components.homekit.
|
||||
DIRECTION_TO_HK = {
|
||||
DIRECTION_REVERSE: 1,
|
||||
DIRECTION_FORWARD: 0,
|
||||
}
|
||||
HK_DIRECTION_TO_HA = {v: k for (k, v) in DIRECTION_TO_HK.items()}
|
||||
|
||||
SPEED_TO_PCNT = {
|
||||
SPEED_HIGH: 100,
|
||||
SPEED_MEDIUM: 50,
|
||||
SPEED_LOW: 25,
|
||||
SPEED_OFF: 0,
|
||||
}
|
||||
|
||||
|
||||
class BaseHomeKitFan(HomeKitEntity, FanEntity):
|
||||
"""Representation of a Homekit fan."""
|
||||
|
||||
# This must be set in subclasses to the name of a boolean characteristic
|
||||
# that controls whether the fan is on or off.
|
||||
on_characteristic = None
|
||||
|
||||
def __init__(self, *args):
|
||||
"""Initialise the fan."""
|
||||
self._on = None
|
||||
self._features = 0
|
||||
self._rotation_direction = 0
|
||||
self._rotation_speed = 0
|
||||
self._swing_mode = 0
|
||||
|
||||
super().__init__(*args)
|
||||
|
||||
def get_characteristic_types(self):
|
||||
"""Define the homekit characteristics the entity cares about."""
|
||||
return [
|
||||
CharacteristicsTypes.SWING_MODE,
|
||||
CharacteristicsTypes.ROTATION_DIRECTION,
|
||||
CharacteristicsTypes.ROTATION_SPEED,
|
||||
]
|
||||
|
||||
def _setup_rotation_direction(self, char):
|
||||
self._features |= SUPPORT_DIRECTION
|
||||
|
||||
def _setup_rotation_speed(self, char):
|
||||
self._features |= SUPPORT_SET_SPEED
|
||||
|
||||
def _setup_swing_mode(self, char):
|
||||
self._features |= SUPPORT_OSCILLATE
|
||||
|
||||
def _update_rotation_direction(self, value):
|
||||
self._rotation_direction = value
|
||||
|
||||
def _update_rotation_speed(self, value):
|
||||
self._rotation_speed = value
|
||||
|
||||
def _update_swing_mode(self, value):
|
||||
self._swing_mode = value
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return true if device is on."""
|
||||
return self._on
|
||||
|
||||
@property
|
||||
def speed(self):
|
||||
"""Return the current speed."""
|
||||
if not self.is_on:
|
||||
return SPEED_OFF
|
||||
if self._rotation_speed > SPEED_TO_PCNT[SPEED_MEDIUM]:
|
||||
return SPEED_HIGH
|
||||
if self._rotation_speed > SPEED_TO_PCNT[SPEED_LOW]:
|
||||
return SPEED_MEDIUM
|
||||
if self._rotation_speed > SPEED_TO_PCNT[SPEED_OFF]:
|
||||
return SPEED_LOW
|
||||
return SPEED_OFF
|
||||
|
||||
@property
|
||||
def speed_list(self):
|
||||
"""Get the list of available speeds."""
|
||||
if self.supported_features & SUPPORT_SET_SPEED:
|
||||
return [SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
|
||||
return []
|
||||
|
||||
@property
|
||||
def current_direction(self):
|
||||
"""Return the current direction of the fan."""
|
||||
return HK_DIRECTION_TO_HA[self._rotation_direction]
|
||||
|
||||
@property
|
||||
def oscillating(self):
|
||||
"""Return whether or not the fan is currently oscillating."""
|
||||
return self._swing_mode == 1
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Flag supported features."""
|
||||
return self._features
|
||||
|
||||
async def async_set_direction(self, direction):
|
||||
"""Set the direction of the fan."""
|
||||
if self.supported_features & SUPPORT_DIRECTION:
|
||||
await self._accessory.put_characteristics(
|
||||
[
|
||||
{
|
||||
"aid": self._aid,
|
||||
"iid": self._chars["rotation.direction"],
|
||||
"value": DIRECTION_TO_HK[direction],
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
async def async_set_speed(self, speed):
|
||||
"""Set the speed of the fan."""
|
||||
if speed == SPEED_OFF:
|
||||
return await self.async_turn_off()
|
||||
|
||||
if self.supported_features & SUPPORT_SET_SPEED:
|
||||
await self._accessory.put_characteristics(
|
||||
[
|
||||
{
|
||||
"aid": self._aid,
|
||||
"iid": self._chars["rotation.speed"],
|
||||
"value": SPEED_TO_PCNT[speed],
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
async def async_oscillate(self, oscillating: bool):
|
||||
"""Oscillate the fan."""
|
||||
if self.supported_features & SUPPORT_OSCILLATE:
|
||||
await self._accessory.put_characteristics(
|
||||
[
|
||||
{
|
||||
"aid": self._aid,
|
||||
"iid": self._chars["swing-mode"],
|
||||
"value": 1 if oscillating else 0,
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
async def async_turn_on(self, speed=None, **kwargs):
|
||||
"""Turn the specified fan on."""
|
||||
|
||||
characteristics = []
|
||||
|
||||
if not self.is_on:
|
||||
characteristics.append(
|
||||
{
|
||||
"aid": self._aid,
|
||||
"iid": self._chars[self.on_characteristic],
|
||||
"value": True,
|
||||
}
|
||||
)
|
||||
|
||||
if self.supported_features & SUPPORT_SET_SPEED and speed:
|
||||
characteristics.append(
|
||||
{
|
||||
"aid": self._aid,
|
||||
"iid": self._chars["rotation.speed"],
|
||||
"value": SPEED_TO_PCNT[speed],
|
||||
},
|
||||
)
|
||||
|
||||
if not characteristics:
|
||||
return
|
||||
|
||||
await self._accessory.put_characteristics(characteristics)
|
||||
|
||||
async def async_turn_off(self, **kwargs):
|
||||
"""Turn the specified fan off."""
|
||||
characteristics = [
|
||||
{
|
||||
"aid": self._aid,
|
||||
"iid": self._chars[self.on_characteristic],
|
||||
"value": False,
|
||||
}
|
||||
]
|
||||
await self._accessory.put_characteristics(characteristics)
|
||||
|
||||
|
||||
class HomeKitFanV1(BaseHomeKitFan):
|
||||
"""Implement fan support for public.hap.service.fan."""
|
||||
|
||||
on_characteristic = "on"
|
||||
|
||||
def get_characteristic_types(self):
|
||||
"""Define the homekit characteristics the entity cares about."""
|
||||
return [CharacteristicsTypes.ON] + super().get_characteristic_types()
|
||||
|
||||
def _update_on(self, value):
|
||||
self._on = value == 1
|
||||
|
||||
|
||||
class HomeKitFanV2(BaseHomeKitFan):
|
||||
"""Implement fan support for public.hap.service.fanv2."""
|
||||
|
||||
on_characteristic = "active"
|
||||
|
||||
def get_characteristic_types(self):
|
||||
"""Define the homekit characteristics the entity cares about."""
|
||||
return [CharacteristicsTypes.ACTIVE] + super().get_characteristic_types()
|
||||
|
||||
def _update_active(self, value):
|
||||
self._on = value == 1
|
||||
|
||||
|
||||
ENTITY_TYPES = {
|
||||
"fan": HomeKitFanV1,
|
||||
"fanv2": HomeKitFanV2,
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Legacy set up platform."""
|
||||
pass
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up Homekit fans."""
|
||||
hkid = config_entry.data["AccessoryPairingID"]
|
||||
conn = hass.data[KNOWN_DEVICES][hkid]
|
||||
|
||||
def async_add_service(aid, service):
|
||||
entity_class = ENTITY_TYPES.get(service["stype"])
|
||||
if not entity_class:
|
||||
return False
|
||||
info = {"aid": aid, "iid": service["iid"]}
|
||||
async_add_entities([entity_class(conn, info)], True)
|
||||
return True
|
||||
|
||||
conn.add_listener(async_add_service)
|
|
@ -0,0 +1,53 @@
|
|||
"""Test against characteristics captured from the Home Assistant HomeKit bridge running demo platforms."""
|
||||
|
||||
from homeassistant.components.fan import (
|
||||
SUPPORT_DIRECTION,
|
||||
SUPPORT_OSCILLATE,
|
||||
SUPPORT_SET_SPEED,
|
||||
)
|
||||
|
||||
from tests.components.homekit_controller.common import (
|
||||
Helper,
|
||||
setup_accessories_from_file,
|
||||
setup_test_accessories,
|
||||
)
|
||||
|
||||
|
||||
async def test_homeassistant_bridge_fan_setup(hass):
|
||||
"""Test that a SIMPLEconnect fan can be correctly setup in HA."""
|
||||
accessories = await setup_accessories_from_file(
|
||||
hass, "home_assistant_bridge_fan.json"
|
||||
)
|
||||
config_entry, pairing = await setup_test_accessories(hass, accessories)
|
||||
|
||||
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
|
||||
# Check that the fan is correctly found and set up
|
||||
fan_id = "fan.living_room_fan"
|
||||
fan = entity_registry.async_get(fan_id)
|
||||
assert fan.unique_id == "homekit-fan.living_room_fan-8"
|
||||
|
||||
fan_helper = Helper(
|
||||
hass, "fan.living_room_fan", pairing, accessories[0], config_entry,
|
||||
)
|
||||
|
||||
fan_state = await fan_helper.poll_and_get_state()
|
||||
assert fan_state.attributes["friendly_name"] == "Living Room Fan"
|
||||
assert fan_state.state == "off"
|
||||
assert fan_state.attributes["supported_features"] == (
|
||||
SUPPORT_DIRECTION | SUPPORT_SET_SPEED | SUPPORT_OSCILLATE
|
||||
)
|
||||
|
||||
device_registry = await hass.helpers.device_registry.async_get_registry()
|
||||
|
||||
device = device_registry.async_get(fan.device_id)
|
||||
assert device.manufacturer == "Home Assistant"
|
||||
assert device.name == "Living Room Fan"
|
||||
assert device.model == "Fan"
|
||||
assert device.sw_version == "0.104.0.dev0"
|
||||
|
||||
bridge = device = device_registry.async_get(device.via_device_id)
|
||||
assert bridge.manufacturer == "Home Assistant"
|
||||
assert bridge.name == "Home Assistant Bridge"
|
||||
assert bridge.model == "Bridge"
|
||||
assert bridge.sw_version == "0.104.0.dev0"
|
|
@ -0,0 +1,46 @@
|
|||
"""
|
||||
Test against characteristics captured from a SIMPLEconnect Fan.
|
||||
|
||||
https://github.com/home-assistant/home-assistant/issues/26180
|
||||
"""
|
||||
|
||||
from homeassistant.components.fan import SUPPORT_DIRECTION, SUPPORT_SET_SPEED
|
||||
|
||||
from tests.components.homekit_controller.common import (
|
||||
Helper,
|
||||
setup_accessories_from_file,
|
||||
setup_test_accessories,
|
||||
)
|
||||
|
||||
|
||||
async def test_simpleconnect_fan_setup(hass):
|
||||
"""Test that a SIMPLEconnect fan can be correctly setup in HA."""
|
||||
accessories = await setup_accessories_from_file(hass, "simpleconnect_fan.json")
|
||||
config_entry, pairing = await setup_test_accessories(hass, accessories)
|
||||
|
||||
entity_registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
|
||||
# Check that the fan is correctly found and set up
|
||||
fan_id = "fan.simpleconnect_fan_06f674"
|
||||
fan = entity_registry.async_get(fan_id)
|
||||
assert fan.unique_id == "homekit-1234567890abcd-8"
|
||||
|
||||
fan_helper = Helper(
|
||||
hass, "fan.simpleconnect_fan_06f674", pairing, accessories[0], config_entry,
|
||||
)
|
||||
|
||||
fan_state = await fan_helper.poll_and_get_state()
|
||||
assert fan_state.attributes["friendly_name"] == "SIMPLEconnect Fan-06F674"
|
||||
assert fan_state.state == "off"
|
||||
assert fan_state.attributes["supported_features"] == (
|
||||
SUPPORT_DIRECTION | SUPPORT_SET_SPEED
|
||||
)
|
||||
|
||||
device_registry = await hass.helpers.device_registry.async_get_registry()
|
||||
|
||||
device = device_registry.async_get(fan.device_id)
|
||||
assert device.manufacturer == "Hunter Fan"
|
||||
assert device.name == "SIMPLEconnect Fan-06F674"
|
||||
assert device.model == "SIMPLEconnect"
|
||||
assert device.sw_version == ""
|
||||
assert device.via_device_id is None
|
|
@ -0,0 +1,407 @@
|
|||
"""Basic checks for HomeKit motion sensors and contact sensors."""
|
||||
from tests.components.homekit_controller.common import FakeService, setup_test_component
|
||||
|
||||
V1_ON = ("fan", "on")
|
||||
V1_ROTATION_DIRECTION = ("fan", "rotation.direction")
|
||||
V1_ROTATION_SPEED = ("fan", "rotation.speed")
|
||||
|
||||
V2_ACTIVE = ("fanv2", "active")
|
||||
V2_ROTATION_DIRECTION = ("fanv2", "rotation.direction")
|
||||
V2_ROTATION_SPEED = ("fanv2", "rotation.speed")
|
||||
V2_SWING_MODE = ("fanv2", "swing-mode")
|
||||
|
||||
|
||||
def create_fan_service():
|
||||
"""
|
||||
Define fan v1 characteristics as per HAP spec.
|
||||
|
||||
This service is no longer documented in R2 of the public HAP spec but existing
|
||||
devices out there use it (like the SIMPLEconnect fan)
|
||||
"""
|
||||
service = FakeService("public.hap.service.fan")
|
||||
|
||||
cur_state = service.add_characteristic("on")
|
||||
cur_state.value = 0
|
||||
|
||||
cur_state = service.add_characteristic("rotation.direction")
|
||||
cur_state.value = 0
|
||||
|
||||
cur_state = service.add_characteristic("rotation.speed")
|
||||
cur_state.value = 0
|
||||
|
||||
return service
|
||||
|
||||
|
||||
def create_fanv2_service():
|
||||
"""Define fan v2 characteristics as per HAP spec."""
|
||||
service = FakeService("public.hap.service.fanv2")
|
||||
|
||||
cur_state = service.add_characteristic("active")
|
||||
cur_state.value = 0
|
||||
|
||||
cur_state = service.add_characteristic("rotation.direction")
|
||||
cur_state.value = 0
|
||||
|
||||
cur_state = service.add_characteristic("rotation.speed")
|
||||
cur_state.value = 0
|
||||
|
||||
cur_state = service.add_characteristic("swing-mode")
|
||||
cur_state.value = 0
|
||||
|
||||
return service
|
||||
|
||||
|
||||
async def test_fan_read_state(hass, utcnow):
|
||||
"""Test that we can read the state of a HomeKit fan accessory."""
|
||||
sensor = create_fan_service()
|
||||
helper = await setup_test_component(hass, [sensor])
|
||||
|
||||
helper.characteristics[V1_ON].value = False
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.state == "off"
|
||||
|
||||
helper.characteristics[V1_ON].value = True
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.state == "on"
|
||||
|
||||
|
||||
async def test_turn_on(hass, utcnow):
|
||||
"""Test that we can turn a fan on."""
|
||||
fan = create_fan_service()
|
||||
helper = await setup_test_component(hass, [fan])
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"turn_on",
|
||||
{"entity_id": "fan.testdevice", "speed": "high"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V1_ON].value == 1
|
||||
assert helper.characteristics[V1_ROTATION_SPEED].value == 100
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"turn_on",
|
||||
{"entity_id": "fan.testdevice", "speed": "medium"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V1_ON].value == 1
|
||||
assert helper.characteristics[V1_ROTATION_SPEED].value == 50
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"turn_on",
|
||||
{"entity_id": "fan.testdevice", "speed": "low"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V1_ON].value == 1
|
||||
assert helper.characteristics[V1_ROTATION_SPEED].value == 25
|
||||
|
||||
|
||||
async def test_turn_off(hass, utcnow):
|
||||
"""Test that we can turn a fan off."""
|
||||
fan = create_fan_service()
|
||||
helper = await setup_test_component(hass, [fan])
|
||||
|
||||
helper.characteristics[V1_ON].value = 1
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan", "turn_off", {"entity_id": "fan.testdevice"}, blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V1_ON].value == 0
|
||||
|
||||
|
||||
async def test_set_speed(hass, utcnow):
|
||||
"""Test that we set fan speed."""
|
||||
fan = create_fan_service()
|
||||
helper = await setup_test_component(hass, [fan])
|
||||
|
||||
helper.characteristics[V1_ON].value = 1
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"set_speed",
|
||||
{"entity_id": "fan.testdevice", "speed": "high"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V1_ROTATION_SPEED].value == 100
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"set_speed",
|
||||
{"entity_id": "fan.testdevice", "speed": "medium"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V1_ROTATION_SPEED].value == 50
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"set_speed",
|
||||
{"entity_id": "fan.testdevice", "speed": "low"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V1_ROTATION_SPEED].value == 25
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"set_speed",
|
||||
{"entity_id": "fan.testdevice", "speed": "off"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V1_ON].value == 0
|
||||
|
||||
|
||||
async def test_speed_read(hass, utcnow):
|
||||
"""Test that we can read a fans oscillation."""
|
||||
fan = create_fan_service()
|
||||
helper = await setup_test_component(hass, [fan])
|
||||
|
||||
helper.characteristics[V1_ON].value = 1
|
||||
helper.characteristics[V1_ROTATION_SPEED].value = 100
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.attributes["speed"] == "high"
|
||||
|
||||
helper.characteristics[V1_ROTATION_SPEED].value = 50
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.attributes["speed"] == "medium"
|
||||
|
||||
helper.characteristics[V1_ROTATION_SPEED].value = 25
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.attributes["speed"] == "low"
|
||||
|
||||
helper.characteristics[V1_ON].value = 0
|
||||
helper.characteristics[V1_ROTATION_SPEED].value = 0
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.attributes["speed"] == "off"
|
||||
|
||||
|
||||
async def test_set_direction(hass, utcnow):
|
||||
"""Test that we can set fan spin direction."""
|
||||
fan = create_fan_service()
|
||||
helper = await setup_test_component(hass, [fan])
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"set_direction",
|
||||
{"entity_id": "fan.testdevice", "direction": "reverse"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V1_ROTATION_DIRECTION].value == 1
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"set_direction",
|
||||
{"entity_id": "fan.testdevice", "direction": "forward"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V1_ROTATION_DIRECTION].value == 0
|
||||
|
||||
|
||||
async def test_direction_read(hass, utcnow):
|
||||
"""Test that we can read a fans oscillation."""
|
||||
fan = create_fan_service()
|
||||
helper = await setup_test_component(hass, [fan])
|
||||
|
||||
helper.characteristics[V1_ROTATION_DIRECTION].value = 0
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.attributes["direction"] == "forward"
|
||||
|
||||
helper.characteristics[V1_ROTATION_DIRECTION].value = 1
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.attributes["direction"] == "reverse"
|
||||
|
||||
|
||||
async def test_fanv2_read_state(hass, utcnow):
|
||||
"""Test that we can read the state of a HomeKit fan accessory."""
|
||||
sensor = create_fanv2_service()
|
||||
helper = await setup_test_component(hass, [sensor])
|
||||
|
||||
helper.characteristics[V2_ACTIVE].value = False
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.state == "off"
|
||||
|
||||
helper.characteristics[V2_ACTIVE].value = True
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.state == "on"
|
||||
|
||||
|
||||
async def test_v2_turn_on(hass, utcnow):
|
||||
"""Test that we can turn a fan on."""
|
||||
fan = create_fanv2_service()
|
||||
helper = await setup_test_component(hass, [fan])
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"turn_on",
|
||||
{"entity_id": "fan.testdevice", "speed": "high"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V2_ACTIVE].value == 1
|
||||
assert helper.characteristics[V2_ROTATION_SPEED].value == 100
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"turn_on",
|
||||
{"entity_id": "fan.testdevice", "speed": "medium"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V2_ACTIVE].value == 1
|
||||
assert helper.characteristics[V2_ROTATION_SPEED].value == 50
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"turn_on",
|
||||
{"entity_id": "fan.testdevice", "speed": "low"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V2_ACTIVE].value == 1
|
||||
assert helper.characteristics[V2_ROTATION_SPEED].value == 25
|
||||
|
||||
|
||||
async def test_v2_turn_off(hass, utcnow):
|
||||
"""Test that we can turn a fan off."""
|
||||
fan = create_fanv2_service()
|
||||
helper = await setup_test_component(hass, [fan])
|
||||
|
||||
helper.characteristics[V2_ACTIVE].value = 1
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan", "turn_off", {"entity_id": "fan.testdevice"}, blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V2_ACTIVE].value == 0
|
||||
|
||||
|
||||
async def test_v2_set_speed(hass, utcnow):
|
||||
"""Test that we set fan speed."""
|
||||
fan = create_fanv2_service()
|
||||
helper = await setup_test_component(hass, [fan])
|
||||
|
||||
helper.characteristics[V2_ACTIVE].value = 1
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"set_speed",
|
||||
{"entity_id": "fan.testdevice", "speed": "high"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V2_ROTATION_SPEED].value == 100
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"set_speed",
|
||||
{"entity_id": "fan.testdevice", "speed": "medium"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V2_ROTATION_SPEED].value == 50
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"set_speed",
|
||||
{"entity_id": "fan.testdevice", "speed": "low"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V2_ROTATION_SPEED].value == 25
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"set_speed",
|
||||
{"entity_id": "fan.testdevice", "speed": "off"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V2_ACTIVE].value == 0
|
||||
|
||||
|
||||
async def test_v2_speed_read(hass, utcnow):
|
||||
"""Test that we can read a fans oscillation."""
|
||||
fan = create_fanv2_service()
|
||||
helper = await setup_test_component(hass, [fan])
|
||||
|
||||
helper.characteristics[V2_ACTIVE].value = 1
|
||||
helper.characteristics[V2_ROTATION_SPEED].value = 100
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.attributes["speed"] == "high"
|
||||
|
||||
helper.characteristics[V2_ROTATION_SPEED].value = 50
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.attributes["speed"] == "medium"
|
||||
|
||||
helper.characteristics[V2_ROTATION_SPEED].value = 25
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.attributes["speed"] == "low"
|
||||
|
||||
helper.characteristics[V2_ACTIVE].value = 0
|
||||
helper.characteristics[V2_ROTATION_SPEED].value = 0
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.attributes["speed"] == "off"
|
||||
|
||||
|
||||
async def test_v2_set_direction(hass, utcnow):
|
||||
"""Test that we can set fan spin direction."""
|
||||
fan = create_fanv2_service()
|
||||
helper = await setup_test_component(hass, [fan])
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"set_direction",
|
||||
{"entity_id": "fan.testdevice", "direction": "reverse"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V2_ROTATION_DIRECTION].value == 1
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"set_direction",
|
||||
{"entity_id": "fan.testdevice", "direction": "forward"},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V2_ROTATION_DIRECTION].value == 0
|
||||
|
||||
|
||||
async def test_v2_direction_read(hass, utcnow):
|
||||
"""Test that we can read a fans oscillation."""
|
||||
fan = create_fanv2_service()
|
||||
helper = await setup_test_component(hass, [fan])
|
||||
|
||||
helper.characteristics[V2_ROTATION_DIRECTION].value = 0
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.attributes["direction"] == "forward"
|
||||
|
||||
helper.characteristics[V2_ROTATION_DIRECTION].value = 1
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.attributes["direction"] == "reverse"
|
||||
|
||||
|
||||
async def test_v2_oscillate(hass, utcnow):
|
||||
"""Test that we can control a fans oscillation."""
|
||||
fan = create_fanv2_service()
|
||||
helper = await setup_test_component(hass, [fan])
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"oscillate",
|
||||
{"entity_id": "fan.testdevice", "oscillating": True},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V2_SWING_MODE].value == 1
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"oscillate",
|
||||
{"entity_id": "fan.testdevice", "oscillating": False},
|
||||
blocking=True,
|
||||
)
|
||||
assert helper.characteristics[V2_SWING_MODE].value == 0
|
||||
|
||||
|
||||
async def test_v2_oscillate_read(hass, utcnow):
|
||||
"""Test that we can read a fans oscillation."""
|
||||
fan = create_fanv2_service()
|
||||
helper = await setup_test_component(hass, [fan])
|
||||
|
||||
helper.characteristics[V2_SWING_MODE].value = 0
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.attributes["oscillating"] is False
|
||||
|
||||
helper.characteristics[V2_SWING_MODE].value = 1
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.attributes["oscillating"] is True
|
|
@ -0,0 +1,325 @@
|
|||
[
|
||||
{
|
||||
"aid": 1,
|
||||
"services": [
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"description": "Identify",
|
||||
"format": "bool",
|
||||
"iid": 2,
|
||||
"perms": [
|
||||
"pw"
|
||||
],
|
||||
"type": "00000014-0000-1000-8000-0026BB765291"
|
||||
},
|
||||
{
|
||||
"description": "Manufacturer",
|
||||
"format": "string",
|
||||
"iid": 3,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000020-0000-1000-8000-0026BB765291",
|
||||
"value": "Home Assistant"
|
||||
},
|
||||
{
|
||||
"description": "Model",
|
||||
"format": "string",
|
||||
"iid": 4,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000021-0000-1000-8000-0026BB765291",
|
||||
"value": "Bridge"
|
||||
},
|
||||
{
|
||||
"description": "Name",
|
||||
"format": "string",
|
||||
"iid": 5,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000023-0000-1000-8000-0026BB765291",
|
||||
"value": "Home Assistant Bridge"
|
||||
},
|
||||
{
|
||||
"description": "SerialNumber",
|
||||
"format": "string",
|
||||
"iid": 6,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000030-0000-1000-8000-0026BB765291",
|
||||
"value": "homekit.bridge"
|
||||
},
|
||||
{
|
||||
"description": "FirmwareRevision",
|
||||
"format": "string",
|
||||
"iid": 7,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000052-0000-1000-8000-0026BB765291",
|
||||
"value": "0.104.0.dev0"
|
||||
}
|
||||
],
|
||||
"iid": 1,
|
||||
"stype": "accessory-information",
|
||||
"type": "0000003E-0000-1000-8000-0026BB765291"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aid": 1256851357,
|
||||
"services": [
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"description": "Identify",
|
||||
"format": "bool",
|
||||
"iid": 2,
|
||||
"perms": [
|
||||
"pw"
|
||||
],
|
||||
"type": "00000014-0000-1000-8000-0026BB765291"
|
||||
},
|
||||
{
|
||||
"description": "Manufacturer",
|
||||
"format": "string",
|
||||
"iid": 3,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000020-0000-1000-8000-0026BB765291",
|
||||
"value": "Home Assistant"
|
||||
},
|
||||
{
|
||||
"description": "Model",
|
||||
"format": "string",
|
||||
"iid": 4,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000021-0000-1000-8000-0026BB765291",
|
||||
"value": "Fan"
|
||||
},
|
||||
{
|
||||
"description": "Name",
|
||||
"format": "string",
|
||||
"iid": 5,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000023-0000-1000-8000-0026BB765291",
|
||||
"value": "Living Room Fan"
|
||||
},
|
||||
{
|
||||
"description": "SerialNumber",
|
||||
"format": "string",
|
||||
"iid": 6,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000030-0000-1000-8000-0026BB765291",
|
||||
"value": "fan.living_room_fan"
|
||||
},
|
||||
{
|
||||
"description": "FirmwareRevision",
|
||||
"format": "string",
|
||||
"iid": 7,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000052-0000-1000-8000-0026BB765291",
|
||||
"value": "0.104.0.dev0"
|
||||
}
|
||||
],
|
||||
"iid": 1,
|
||||
"stype": "accessory-information",
|
||||
"type": "0000003E-0000-1000-8000-0026BB765291"
|
||||
},
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"description": "Active",
|
||||
"format": "uint8",
|
||||
"iid": 9,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "000000B0-0000-1000-8000-0026BB765291",
|
||||
"valid-values": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"description": "RotationDirection",
|
||||
"format": "int",
|
||||
"iid": 10,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "00000028-0000-1000-8000-0026BB765291",
|
||||
"valid-values": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"description": "SwingMode",
|
||||
"format": "uint8",
|
||||
"iid": 11,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "000000B6-0000-1000-8000-0026BB765291",
|
||||
"valid-values": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"description": "RotationSpeed",
|
||||
"format": "float",
|
||||
"iid": 12,
|
||||
"maxValue": 100,
|
||||
"minStep": 1,
|
||||
"minValue": 0,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "00000029-0000-1000-8000-0026BB765291",
|
||||
"unit": "percentage",
|
||||
"value": 100
|
||||
}
|
||||
],
|
||||
"iid": 8,
|
||||
"stype": "fanv2",
|
||||
"type": "000000B7-0000-1000-8000-0026BB765291"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aid": 766313939,
|
||||
"services": [
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"description": "Identify",
|
||||
"format": "bool",
|
||||
"iid": 2,
|
||||
"perms": [
|
||||
"pw"
|
||||
],
|
||||
"type": "00000014-0000-1000-8000-0026BB765291"
|
||||
},
|
||||
{
|
||||
"description": "Manufacturer",
|
||||
"format": "string",
|
||||
"iid": 3,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000020-0000-1000-8000-0026BB765291",
|
||||
"value": "Home Assistant"
|
||||
},
|
||||
{
|
||||
"description": "Model",
|
||||
"format": "string",
|
||||
"iid": 4,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000021-0000-1000-8000-0026BB765291",
|
||||
"value": "Fan"
|
||||
},
|
||||
{
|
||||
"description": "Name",
|
||||
"format": "string",
|
||||
"iid": 5,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000023-0000-1000-8000-0026BB765291",
|
||||
"value": "Ceiling Fan"
|
||||
},
|
||||
{
|
||||
"description": "SerialNumber",
|
||||
"format": "string",
|
||||
"iid": 6,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000030-0000-1000-8000-0026BB765291",
|
||||
"value": "fan.ceiling_fan"
|
||||
},
|
||||
{
|
||||
"description": "FirmwareRevision",
|
||||
"format": "string",
|
||||
"iid": 7,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000052-0000-1000-8000-0026BB765291",
|
||||
"value": "0.104.0.dev0"
|
||||
}
|
||||
],
|
||||
"iid": 1,
|
||||
"stype": "accessory-information",
|
||||
"type": "0000003E-0000-1000-8000-0026BB765291"
|
||||
},
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"description": "Active",
|
||||
"format": "uint8",
|
||||
"iid": 9,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "000000B0-0000-1000-8000-0026BB765291",
|
||||
"valid-values": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"description": "RotationSpeed",
|
||||
"format": "float",
|
||||
"iid": 10,
|
||||
"maxValue": 100,
|
||||
"minStep": 1,
|
||||
"minValue": 0,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "00000029-0000-1000-8000-0026BB765291",
|
||||
"unit": "percentage",
|
||||
"value": 100
|
||||
}
|
||||
],
|
||||
"iid": 8,
|
||||
"stype": "fanv2",
|
||||
"type": "000000B7-0000-1000-8000-0026BB765291"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -0,0 +1,769 @@
|
|||
[
|
||||
{
|
||||
"aid": 1,
|
||||
"services": [
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"format": "string",
|
||||
"iid": 2,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000023-0000-1000-8000-0026BB765291",
|
||||
"value": "SIMPLEconnect Fan-06F674"
|
||||
},
|
||||
{
|
||||
"format": "string",
|
||||
"iid": 3,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000020-0000-1000-8000-0026BB765291",
|
||||
"value": "Hunter Fan"
|
||||
},
|
||||
{
|
||||
"format": "string",
|
||||
"iid": 4,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000021-0000-1000-8000-0026BB765291",
|
||||
"value": "SIMPLEconnect"
|
||||
},
|
||||
{
|
||||
"format": "string",
|
||||
"iid": 5,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000030-0000-1000-8000-0026BB765291",
|
||||
"value": "1234567890abcd"
|
||||
},
|
||||
{
|
||||
"format": "bool",
|
||||
"iid": 6,
|
||||
"perms": [
|
||||
"pw"
|
||||
],
|
||||
"type": "00000014-0000-1000-8000-0026BB765291"
|
||||
},
|
||||
{
|
||||
"format": "string",
|
||||
"iid": 7,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "54",
|
||||
"value": "0.22"
|
||||
}
|
||||
],
|
||||
"iid": 1,
|
||||
"stype": "accessory-information",
|
||||
"type": "0000003E-0000-1000-8000-0026BB765291"
|
||||
},
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"ev": false,
|
||||
"format": "bool",
|
||||
"iid": 9,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "00000025-0000-1000-8000-0026BB765291",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"format": "string",
|
||||
"iid": 10,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000023-0000-1000-8000-0026BB765291",
|
||||
"value": "Hunter Fan"
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "float",
|
||||
"iid": 11,
|
||||
"maxValue": 100.0,
|
||||
"minStep": 25.0,
|
||||
"minValue": 0.0,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "00000029-0000-1000-8000-0026BB765291",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 12,
|
||||
"maxValue": 1,
|
||||
"minValue": 0,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "00000028-0000-1000-8000-0026BB765291",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"description": "Set Fan Fast On",
|
||||
"ev": false,
|
||||
"format": "bool",
|
||||
"iid": 13,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "2BD83CC0-6C60-11E5-A837-0800200C9A66",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"description": "Set Fan Fast Off",
|
||||
"ev": false,
|
||||
"format": "bool",
|
||||
"iid": 14,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "2BD83CC1-6C60-11E5-A837-0800200C9A66",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"description": "Is BLDC in Scope",
|
||||
"ev": false,
|
||||
"format": "bool",
|
||||
"iid": 15,
|
||||
"perms": [
|
||||
"pr",
|
||||
"ev"
|
||||
],
|
||||
"type": "2BD83CC5-6C60-11E5-A837-0800200C9A66",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "bool",
|
||||
"iid": 16,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "00000005-0000-1000-8000-0026BB765291",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 17,
|
||||
"perms": [
|
||||
"pr",
|
||||
"ev"
|
||||
],
|
||||
"type": "2BD83CC4-6C60-11E5-A837-0800200C9A66",
|
||||
"value": 341
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 18,
|
||||
"perms": [
|
||||
"pr",
|
||||
"ev"
|
||||
],
|
||||
"type": "2BD83CC3-6C60-11E5-A837-0800200C9A66",
|
||||
"value": 0
|
||||
}
|
||||
],
|
||||
"iid": 8,
|
||||
"stype": "fan",
|
||||
"type": "00000040-0000-1000-8000-0026BB765291"
|
||||
},
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"description": "FW Upgrade supported types",
|
||||
"format": "string",
|
||||
"iid": 20,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "151909D2-3802-11E4-916C-0800200C9A66",
|
||||
"value": "url,data"
|
||||
},
|
||||
{
|
||||
"description": "FW Upgrade URL",
|
||||
"format": "string",
|
||||
"iid": 21,
|
||||
"maxLen": 256,
|
||||
"perms": [
|
||||
"pw"
|
||||
],
|
||||
"type": "151909D1-3802-11E4-916C-0800200C9A66"
|
||||
},
|
||||
{
|
||||
"description": "FW Upgrade Status",
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 22,
|
||||
"perms": [
|
||||
"pr",
|
||||
"ev"
|
||||
],
|
||||
"type": "151909D6-3802-11E4-916C-0800200C9A66",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"description": "FW Upgrade Data",
|
||||
"format": "data",
|
||||
"iid": 23,
|
||||
"perms": [
|
||||
"pw"
|
||||
],
|
||||
"type": "151909D7-3802-11E4-916C-0800200C9A66"
|
||||
}
|
||||
],
|
||||
"iid": 19,
|
||||
"stype": "Unknown Service: 151909D0-3802-11E4-916C-0800200C9A66",
|
||||
"type": "151909D0-3802-11E4-916C-0800200C9A66"
|
||||
},
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"description": "FW Upgrade supported types",
|
||||
"format": "string",
|
||||
"iid": 25,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "151909D2-3802-11E4-916C-0800200C9A66",
|
||||
"value": "url,data"
|
||||
},
|
||||
{
|
||||
"description": "FW Upgrade URL",
|
||||
"format": "string",
|
||||
"iid": 26,
|
||||
"maxLen": 256,
|
||||
"perms": [
|
||||
"pw"
|
||||
],
|
||||
"type": "151909D1-3802-11E4-916C-0800200C9A66"
|
||||
},
|
||||
{
|
||||
"description": "FW Upgrade Status",
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 27,
|
||||
"perms": [
|
||||
"pr",
|
||||
"ev"
|
||||
],
|
||||
"type": "151909D6-3802-11E4-916C-0800200C9A66",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"description": "FW Upgrade Data",
|
||||
"format": "data",
|
||||
"iid": 28,
|
||||
"perms": [
|
||||
"pw"
|
||||
],
|
||||
"type": "151909D7-3802-11E4-916C-0800200C9A66"
|
||||
}
|
||||
],
|
||||
"iid": 24,
|
||||
"stype": "Unknown Service: 151909D8-3802-11E4-916C-0800200C9A66",
|
||||
"type": "151909D8-3802-11E4-916C-0800200C9A66"
|
||||
},
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"ev": false,
|
||||
"format": "bool",
|
||||
"iid": 30,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "00000025-0000-1000-8000-0026BB765291",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"format": "string",
|
||||
"iid": 31,
|
||||
"perms": [
|
||||
"pr"
|
||||
],
|
||||
"type": "00000023-0000-1000-8000-0026BB765291",
|
||||
"value": "Hunter Light"
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 32,
|
||||
"maxValue": 100,
|
||||
"minStep": 10,
|
||||
"minValue": 0,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "00000008-0000-1000-8000-0026BB765291",
|
||||
"value": 30
|
||||
},
|
||||
{
|
||||
"description": "Set Light Dimming",
|
||||
"ev": false,
|
||||
"format": "bool",
|
||||
"iid": 33,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "151909DC-3802-11E4-916C-0800200C9A66",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"description": "Set Light Security",
|
||||
"ev": false,
|
||||
"format": "bool",
|
||||
"iid": 34,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "2BD815B0-6C60-11E5-A837-0800200C9A66",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"description": "Get Light Power",
|
||||
"ev": false,
|
||||
"format": "bool",
|
||||
"iid": 35,
|
||||
"perms": [
|
||||
"pr",
|
||||
"ev"
|
||||
],
|
||||
"type": "2BD815B5-6C60-11E5-A837-0800200C9A66",
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"iid": 29,
|
||||
"stype": "lightbulb",
|
||||
"type": "00000043-0000-1000-8000-0026BB765291"
|
||||
},
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 37,
|
||||
"perms": [
|
||||
"pr",
|
||||
"ev"
|
||||
],
|
||||
"type": "2BD83CC6-6C60-11E5-A837-0800200C9A66",
|
||||
"value": -65
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "bool",
|
||||
"iid": 38,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "2BD83CC7-6C60-11E5-A837-0800200C9A66",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "string",
|
||||
"iid": 39,
|
||||
"maxLen": 256,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "0049CFF1-4B37-11E5-B970-0800200C9A66",
|
||||
"value": "url, data"
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "string",
|
||||
"iid": 40,
|
||||
"maxLen": 256,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "2BD83CC2-6C60-11E5-A837-0800200C9A66",
|
||||
"value": "url, data"
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 41,
|
||||
"maxValue": 110,
|
||||
"minValue": 0,
|
||||
"perms": [
|
||||
"pr",
|
||||
"ev"
|
||||
],
|
||||
"type": "0049CFF2-4B37-11E5-B970-0800200C9A66",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "bool",
|
||||
"iid": 42,
|
||||
"perms": [
|
||||
"pr",
|
||||
"ev"
|
||||
],
|
||||
"type": "0049CFF3-4B37-11E5-B970-0800200C9A66",
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"iid": 36,
|
||||
"stype": "Unknown Service: 0049CFF0-4B37-11E5-B970-0800200C9A66",
|
||||
"type": "0049CFF0-4B37-11E5-B970-0800200C9A66"
|
||||
},
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"ev": false,
|
||||
"format": "string",
|
||||
"iid": 44,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "2BD815B2-6C60-11E5-A837-0800200C9A66",
|
||||
"value": "NULL"
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 45,
|
||||
"maxValue": 63,
|
||||
"minValue": 0,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "2BD815B4-6C60-11E5-A837-0800200C9A66",
|
||||
"value": 7
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "bool",
|
||||
"iid": 46,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "2BD815B3-6C60-11E5-A837-0800200C9A66",
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"iid": 43,
|
||||
"stype": "Unknown Service: 2BD815B1-6C60-11E5-A837-0800200C9A66",
|
||||
"type": "2BD815B1-6C60-11E5-A837-0800200C9A66"
|
||||
},
|
||||
{
|
||||
"characteristics": [
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 48,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC61-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 49,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC62-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 50,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC63-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 51,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC64-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 52,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC65-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 53,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC66-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 54,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC67-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 55,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC68-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 56,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC69-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 57,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC6A-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 58,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC6B-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 59,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC6C-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 60,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC6D-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 61,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC6E-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 62,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC6F-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 63,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC70-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 64,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC71-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 65,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC72-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 66,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC73-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 67,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "E836DC74-6C6E-11E5-A837-0800200C9A66",
|
||||
"value": 4294967295
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 68,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "CC9EA121-FC1C-11E5-A837-0800200C9A66",
|
||||
"value": 4294901760
|
||||
},
|
||||
{
|
||||
"ev": false,
|
||||
"format": "int",
|
||||
"iid": 69,
|
||||
"perms": [
|
||||
"pr",
|
||||
"pw",
|
||||
"ev"
|
||||
],
|
||||
"type": "CC9EA120-FC1C-11E5-A837-0800200C9A66",
|
||||
"value": 4294901760
|
||||
}
|
||||
],
|
||||
"iid": 47,
|
||||
"stype": "Unknown Service: E836DC60-6C6E-11E5-A837-0800200C9A66",
|
||||
"type": "E836DC60-6C6E-11E5-A837-0800200C9A66"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
Loading…
Reference in New Issue