diff --git a/pylintrc b/pylintrc index 5ac4465..5a366ce 100644 --- a/pylintrc +++ b/pylintrc @@ -4,3 +4,4 @@ reports=no disable= too-many-arguments, too-many-instance-attributes, + too-many-public-methods, diff --git a/pylutron_caseta/smartbridge.py b/pylutron_caseta/smartbridge.py index 5d632b4..0859179 100644 --- a/pylutron_caseta/smartbridge.py +++ b/pylutron_caseta/smartbridge.py @@ -200,6 +200,40 @@ def set_value(self, device_id, value): "Parameter": [{"Type": "Level", "Value": value}]}}} self._writer.write(cmd) + def _send_zone_create_request(self, device_id, command): + zone_id = self._get_zone_id(device_id) + if not zone_id: + return + self._writer.write({ + "CommuniqueType": "CreateRequest", + "Header": {"Url": "/zone/%s/commandprocessor" % zone_id}, + "Body": { + "Command": { + "CommandType": command + } + } + }) + + def stop_cover(self, device_id): + """Will stop a cover.""" + self._send_zone_create_request(device_id, "Stop") + + def raise_cover(self, device_id): + """Will raise a cover.""" + self._send_zone_create_request(device_id, "Raise") + # If set_value is called, we get an optimistic callback right + # away with the value, if we use Raise we have to set it + # as one won't come unless Stop is called or something goes wrong. + self.devices[device_id]['current_state'] = 100 + + def lower_cover(self, device_id): + """Will lower a cover.""" + self._send_zone_create_request(device_id, "Lower") + # If set_value is called, we get an optimistic callback right + # away with the value, if we use Lower we have to set it + # as one won't come unless Stop is called or something goes wrong. + self.devices[device_id]['current_state'] = 0 + def set_fan(self, device_id, value): """ Will set the value for a fan device with the given device ID. diff --git a/tests/responses/devices.json b/tests/responses/devices.json index c99864e..6ea2131 100644 --- a/tests/responses/devices.json +++ b/tests/responses/devices.json @@ -162,6 +162,21 @@ "href": "/devicerule/122" } ] + }, + { + "href": "/device/7", + "Name": "Living Shade 3", + "FullyQualifiedName": ["Living Room", "Living Shade 3"], + "Parent": { + "href": "/project" + }, + "SerialNumber": 1234, + "ModelNumber": "QSYC-J-RCVR", + "DeviceType": "QsWirelessShade", + "LocalZones": [{"href": "/zone/6"}], + "AssociatedArea": {"href": "/area/4"}, + "LinkNodes": [{"href": "/device/10/linknode/9"}], + "DeviceRules": [{"href": "/devicerule/10"}] } ] } diff --git a/tests/test_smartbridge.py b/tests/test_smartbridge.py index 6beb302..b863c12 100644 --- a/tests/test_smartbridge.py +++ b/tests/test_smartbridge.py @@ -119,14 +119,18 @@ async def _accept_connection(self, reader, writer, wait): ) # Finally, we should check the zone status on each zone requested_zones = [] - for _ in range(0, 2): + for _ in range(0, 3): value = await wait(writer.queue.get()) logging.info("Read %s", value) assert value["CommuniqueType"] == "ReadRequest" requested_zones.append(value["Header"]["Url"]) writer.queue.task_done() requested_zones.sort() - assert requested_zones == ["/zone/1/status", "/zone/2/status"] + assert requested_zones == [ + "/zone/1/status", + "/zone/2/status", + "/zone/6/status" + ] async def disconnect(self, exception=None): """Disconnect SmartBridge.""" @@ -320,6 +324,15 @@ async def test_device_list(bridge): "current_state": -1, "fan_speed": None, "zone": None}, + "7": { + "device_id": "7", + "name": "Living Room_Living Shade 3", + "type": "QsWirelessShade", + "model": "QSYC-J-RCVR", + "serial": 1234, + "current_state": -1, + "fan_speed": None, + "zone": "6"} } assert devices == expected_devices @@ -591,6 +604,52 @@ async def test_set_fan(bridge): "FanSpeedParameters": {"FanSpeed": "Medium"}}}} +@pytest.mark.asyncio +async def test_lower_cover(bridge): + """Test that lowering a cover produces the right commands.""" + devices = bridge.target.get_devices() + bridge.target.lower_cover('7') + command = await asyncio.wait_for(bridge.writer.queue.get(), 10) + bridge.writer.queue.task_done() + assert command == { + "CommuniqueType": "CreateRequest", + "Header": {"Url": "/zone/6/commandprocessor"}, + "Body": { + "Command": { + "CommandType": "Lower"}}} + assert devices['7']['current_state'] == 0 + + +@pytest.mark.asyncio +async def test_raise_cover(bridge): + """Test that raising a cover produces the right commands.""" + devices = bridge.target.get_devices() + bridge.target.raise_cover('7') + command = await asyncio.wait_for(bridge.writer.queue.get(), 10) + bridge.writer.queue.task_done() + assert command == { + "CommuniqueType": "CreateRequest", + "Header": {"Url": "/zone/6/commandprocessor"}, + "Body": { + "Command": { + "CommandType": "Raise"}}} + assert devices['7']['current_state'] == 100 + + +@pytest.mark.asyncio +async def test_stop_cover(bridge): + """Test that stopping a cover produces the right commands.""" + bridge.target.stop_cover('7') + command = await asyncio.wait_for(bridge.writer.queue.get(), 10) + bridge.writer.queue.task_done() + assert command == { + "CommuniqueType": "CreateRequest", + "Header": {"Url": "/zone/6/commandprocessor"}, + "Body": { + "Command": { + "CommandType": "Stop"}}} + + @pytest.mark.asyncio async def test_activate_scene(bridge): """Test that activating scenes produces the right commands."""