Esempio n. 1
0
def test_handle_get_characteristics_encrypted(driver):
    """Verify an encrypted get_characteristics."""
    acc = Accessory(driver, "TestAcc", aid=1)
    assert acc.aid == 1
    service = acc.driver.loader.get_service("GarageDoorOpener")
    acc.add_service(service)
    driver.add_accessory(acc)

    handler = hap_handler.HAPServerHandler(driver, "peername")
    handler.is_encrypted = True

    response = hap_handler.HAPResponse()
    handler.response = response
    handler.path = "/characteristics?id=1.9"
    handler.handle_get_characteristics()

    assert response.status_code == 207
    assert b'"value": 0' in response.body

    with patch.object(acc.iid_manager,
                      "get_obj",
                      side_effect=CharacteristicError):
        response = hap_handler.HAPResponse()
        handler.response = response
        handler.path = "/characteristics?id=1.9"
        handler.handle_get_characteristics()

    assert response.status_code == 207
    assert b"-70402" in response.body
Esempio n. 2
0
def test_get_characteristics_with_crypto(driver):
    """Verify an encrypt characteristics request."""
    loop = MagicMock()
    transport = MagicMock()
    connections = {}

    acc = Accessory(driver, "TestAcc", aid=1)
    assert acc.aid == 1
    service = acc.driver.loader.get_service("TemperatureSensor")
    acc.add_service(service)
    driver.add_accessory(acc)

    hap_proto = hap_protocol.HAPServerProtocol(loop, connections, driver)
    hap_proto.connection_made(transport)

    hap_proto.hap_crypto = MockHAPCrypto()
    hap_proto.handler.is_encrypted = True

    with patch.object(hap_proto.transport, "write") as writer:
        hap_proto.data_received(
            b"GET /characteristics?id=3762173001.7 HTTP/1.1\r\nHost: HASS\\032Bridge\\032YPHW\\032B223AD._hap._tcp.local\r\n\r\n"  # pylint: disable=line-too-long
        )
        hap_proto.data_received(
            b"GET /characteristics?id=1.5 HTTP/1.1\r\nHost: HASS\\032Bridge\\032YPHW\\032B223AD._hap._tcp.local\r\n\r\n"  # pylint: disable=line-too-long
        )

    hap_proto.close()
    assert b"Content-Length:" in writer.call_args_list[0][0][0]
    assert b"Transfer-Encoding: chunked\r\n\r\n" not in writer.call_args_list[0][0][0]
    assert b"-70402" in writer.call_args_list[0][0][0]

    assert b"Content-Length:" in writer.call_args_list[1][0][0]
    assert b"Transfer-Encoding: chunked\r\n\r\n" not in writer.call_args_list[1][0][0]
    assert b"TestAcc" in writer.call_args_list[1][0][0]
Esempio n. 3
0
def test_set_characteristics_with_crypto(driver):
    """Verify an encrypt characteristics request."""
    loop = MagicMock()
    transport = MagicMock()
    connections = {}

    acc = Accessory(driver, "TestAcc", aid=1)
    assert acc.aid == 1
    service = acc.driver.loader.get_service("GarageDoorOpener")
    acc.add_service(service)
    driver.add_accessory(acc)

    hap_proto = hap_protocol.HAPServerProtocol(loop, connections, driver)
    hap_proto.connection_made(transport)

    hap_proto.hap_crypto = MockHAPCrypto()
    hap_proto.handler.is_encrypted = True

    with patch.object(hap_proto.transport, "write") as writer:
        hap_proto.data_received(
            b'PUT /characteristics HTTP/1.1\r\nHost: HASS12\\032AD1C22._hap._tcp.local\r\nContent-Length: 49\r\nContent-Type: application/hap+json\r\n\r\n{"characteristics":[{"aid":1,"iid":9,"ev":true}]}'  # pylint: disable=line-too-long
        )

    hap_proto.close()
    assert writer.call_args_list[0][0][0] == b"HTTP/1.1 204 No Content\r\n\r\n"
Esempio n. 4
0
def test_acc_set_primary_service(mock_driver):
    """Test method set_primary_service."""
    acc = Accessory(mock_driver, "Test Accessory")
    service = acc.driver.loader.get_service("Television")
    acc.add_service(service)
    linked_service = acc.driver.loader.get_service("TelevisionSpeaker")
    acc.add_service(linked_service)
    assert acc.get_service("Television").is_primary_service is None
    assert acc.get_service("TelevisionSpeaker").is_primary_service is None
    acc.set_primary_service(service)
    assert acc.get_service("Television").is_primary_service is True
    assert acc.get_service("TelevisionSpeaker").is_primary_service is False
Esempio n. 5
0
def test_handle_set_handle_set_characteristics_unencrypted(driver):
    """Verify an unencrypted set_characteristics."""
    acc = Accessory(driver, "TestAcc", aid=1)
    assert acc.aid == 1
    service = acc.driver.loader.get_service("GarageDoorOpener")
    acc.add_service(service)
    driver.add_accessory(acc)

    handler = hap_handler.HAPServerHandler(driver, "peername")
    handler.is_encrypted = False

    response = hap_handler.HAPResponse()
    handler.response = response
    handler.request_body = b'{"characteristics":[{"aid":1,"iid":9,"ev":true}]}'
    handler.handle_set_characteristics()

    assert response.status_code == 401
Esempio n. 6
0
def main():
    logging.basicConfig(level=logging.INFO)

    lightbulb1 = Accessory(name='Acme LED Light Bulb',
                           model='LEDBulb1,1',
                           manufacturer='Acme')

    lightbulb1_lightbulb = LightbulbService()
    lightbulb1_lightbulb.add_characteristic(On(True, bulb_on))
    lightbulb1_lightbulb.add_characteristic(Brightness(50, bulb_brightness))
    lightbulb1.add_service(lightbulb1_lightbulb)

    accessories = Accessories()
    accessories.add(lightbulb1)

    config = JsonConfig(IP_ADDRESS)
    start(config, accessories)
Esempio n. 7
0
def test_handle_set_handle_set_characteristics_encrypted_with_exception(
        driver):
    """Verify an encrypted set_characteristics."""
    acc = Accessory(driver, "TestAcc", aid=1)
    assert acc.aid == 1

    def _mock_failure(*_):
        raise ValueError

    service = acc.driver.loader.get_service("GarageDoorOpener")
    service.setter_callback = _mock_failure
    acc.add_service(service)
    driver.add_accessory(acc)

    handler = hap_handler.HAPServerHandler(driver, "peername")
    handler.is_encrypted = True

    response = hap_handler.HAPResponse()
    handler.response = response
    handler.request_body = b'{"characteristics":[{"aid":1,"iid":9,"value":1}]}'
    handler.handle_set_characteristics()

    assert response.status_code == 207
    assert b"-70402" in response.body
def test_mixing_service_char_callbacks_partial_failure(driver):
    bridge = Bridge(driver, "mybridge")
    acc = Accessory(driver, "TestAcc", aid=2)
    acc2 = UnavailableAccessory(driver, "TestAcc2", aid=3)

    service = Service(uuid1(), "Lightbulb")
    char_on = Characteristic("On", uuid1(), CHAR_PROPS)
    char_brightness = Characteristic("Brightness", uuid1(), CHAR_PROPS)

    service.add_characteristic(char_on)
    service.add_characteristic(char_brightness)

    def fail_callback(*_):
        raise ValueError

    service.setter_callback = fail_callback

    acc.add_service(service)
    bridge.add_accessory(acc)

    service2 = Service(uuid1(), "Lightbulb")
    char_on2 = Characteristic("On", uuid1(), CHAR_PROPS)
    char_brightness2 = Characteristic("Brightness", uuid1(), CHAR_PROPS)

    service2.add_characteristic(char_on2)
    service2.add_characteristic(char_brightness2)

    char_on2.setter_callback = fail_callback

    acc2.add_service(service2)
    bridge.add_accessory(acc2)

    char_on_iid = char_on.to_HAP()[HAP_REPR_IID]
    char_brightness_iid = char_brightness.to_HAP()[HAP_REPR_IID]
    char_on2_iid = char_on2.to_HAP()[HAP_REPR_IID]
    char_brightness2_iid = char_brightness2.to_HAP()[HAP_REPR_IID]

    driver.add_accessory(bridge)

    response = driver.set_characteristics(
        {
            HAP_REPR_CHARS: [
                {
                    HAP_REPR_AID: acc.aid,
                    HAP_REPR_IID: char_on_iid,
                    HAP_REPR_VALUE: True,
                },
                {
                    HAP_REPR_AID: acc.aid,
                    HAP_REPR_IID: char_brightness_iid,
                    HAP_REPR_VALUE: 88,
                },
                {
                    HAP_REPR_AID: acc2.aid,
                    HAP_REPR_IID: char_on2_iid,
                    HAP_REPR_VALUE: True,
                },
                {
                    HAP_REPR_AID: acc2.aid,
                    HAP_REPR_IID: char_brightness2_iid,
                    HAP_REPR_VALUE: 12,
                },
            ]
        },
        "mock_addr",
    )

    assert response == {
        HAP_REPR_CHARS: [
            {
                HAP_REPR_AID: acc.aid,
                HAP_REPR_IID: char_on_iid,
                HAP_REPR_STATUS:
                HAP_SERVER_STATUS.SERVICE_COMMUNICATION_FAILURE,
            },
            {
                HAP_REPR_AID: acc.aid,
                HAP_REPR_IID: char_brightness_iid,
                HAP_REPR_STATUS:
                HAP_SERVER_STATUS.SERVICE_COMMUNICATION_FAILURE,
            },
            {
                HAP_REPR_AID: acc2.aid,
                HAP_REPR_IID: char_on2_iid,
                HAP_REPR_STATUS:
                HAP_SERVER_STATUS.SERVICE_COMMUNICATION_FAILURE,
            },
            {
                HAP_REPR_AID: acc2.aid,
                HAP_REPR_IID: char_brightness2_iid,
                HAP_REPR_STATUS: 0,
            },
        ]
    }
def test_service_callbacks(driver):
    bridge = Bridge(driver, "mybridge")
    acc = Accessory(driver, "TestAcc", aid=2)
    acc2 = UnavailableAccessory(driver, "TestAcc2", aid=3)

    service = Service(uuid1(), "Lightbulb")
    char_on = Characteristic("On", uuid1(), CHAR_PROPS)
    char_brightness = Characteristic("Brightness", uuid1(), CHAR_PROPS)

    service.add_characteristic(char_on)
    service.add_characteristic(char_brightness)

    mock_callback = MagicMock()
    service.setter_callback = mock_callback

    acc.add_service(service)
    bridge.add_accessory(acc)

    service2 = Service(uuid1(), "Lightbulb")
    char_on2 = Characteristic("On", uuid1(), CHAR_PROPS)
    char_brightness2 = Characteristic("Brightness", uuid1(), CHAR_PROPS)

    service2.add_characteristic(char_on2)
    service2.add_characteristic(char_brightness2)

    mock_callback2 = MagicMock()
    service2.setter_callback = mock_callback2

    acc2.add_service(service2)
    bridge.add_accessory(acc2)

    char_on_iid = char_on.to_HAP()[HAP_REPR_IID]
    char_brightness_iid = char_brightness.to_HAP()[HAP_REPR_IID]
    char_on2_iid = char_on2.to_HAP()[HAP_REPR_IID]
    char_brightness2_iid = char_brightness2.to_HAP()[HAP_REPR_IID]

    driver.add_accessory(bridge)

    response = driver.set_characteristics(
        {
            HAP_REPR_CHARS: [
                {
                    HAP_REPR_AID: acc.aid,
                    HAP_REPR_IID: char_on_iid,
                    HAP_REPR_VALUE: True,
                },
                {
                    HAP_REPR_AID: acc.aid,
                    HAP_REPR_IID: char_brightness_iid,
                    HAP_REPR_VALUE: 88,
                },
                {
                    HAP_REPR_AID: acc2.aid,
                    HAP_REPR_IID: char_on2_iid,
                    HAP_REPR_VALUE: True,
                },
                {
                    HAP_REPR_AID: acc2.aid,
                    HAP_REPR_IID: char_brightness2_iid,
                    HAP_REPR_VALUE: 12,
                },
            ]
        },
        "mock_addr",
    )
    assert response is None

    mock_callback2.assert_called_with({"On": True, "Brightness": 12})
    mock_callback.assert_called_with({"On": True, "Brightness": 88})

    get_chars = driver.get_characteristics([
        "{}.{}".format(acc.aid, char_on_iid),
        "{}.{}".format(acc2.aid, char_on2_iid)
    ])
    assert get_chars == {
        "characteristics": [
            {
                "aid": acc.aid,
                "iid": char_on_iid,
                "status": 0,
                "value": True
            },
            {
                "aid": acc2.aid,
                "iid": char_on2_iid,
                "status": -70402
            },
        ]
    }

    def _fail_func():
        raise ValueError

    char_brightness.getter_callback = _fail_func
    get_chars = driver.get_characteristics([
        "{}.{}".format(acc.aid, char_on_iid),
        "{}.{}".format(acc2.aid, char_on2_iid),
        "{}.{}".format(acc2.aid, char_brightness_iid),
        "{}.{}".format(acc.aid, char_brightness2_iid),
    ])
    assert get_chars == {
        "characteristics": [
            {
                "aid": acc.aid,
                "iid": char_on_iid,
                "status": 0,
                "value": True
            },
            {
                "aid": acc2.aid,
                "iid": char_on2_iid,
                "status": -70402
            },
            {
                "aid": acc2.aid,
                "iid": char_brightness2_iid,
                "status": -70402
            },
            {
                "aid": acc.aid,
                "iid": char_brightness_iid,
                "status": -70402
            },
        ]
    }
def test_service_callbacks(driver):
    bridge = Bridge(driver, "mybridge")
    acc = Accessory(driver, 'TestAcc', aid=2)
    acc2 = Accessory(driver, 'TestAcc2', aid=3)

    service = Service(uuid1(), 'Lightbulb')
    char_on = Characteristic('On', uuid1(), CHAR_PROPS)
    char_brightness = Characteristic('Brightness', uuid1(), CHAR_PROPS)

    service.add_characteristic(char_on)
    service.add_characteristic(char_brightness)

    mock_callback = MagicMock()
    service.setter_callback = mock_callback

    acc.add_service(service)
    bridge.add_accessory(acc)

    service2 = Service(uuid1(), 'Lightbulb')
    char_on2 = Characteristic('On', uuid1(), CHAR_PROPS)
    char_brightness2 = Characteristic('Brightness', uuid1(), CHAR_PROPS)

    service2.add_characteristic(char_on2)
    service2.add_characteristic(char_brightness2)

    mock_callback2 = MagicMock()
    service2.setter_callback = mock_callback2

    acc2.add_service(service2)
    bridge.add_accessory(acc2)

    char_on_iid = char_on.to_HAP()[HAP_REPR_IID]
    char_brightness_iid = char_brightness.to_HAP()[HAP_REPR_IID]
    char_on2_iid = char_on2.to_HAP()[HAP_REPR_IID]
    char_brightness2_iid = char_brightness2.to_HAP()[HAP_REPR_IID]

    driver.add_accessory(bridge)

    driver.set_characteristics(
        {
            HAP_REPR_CHARS: [{
                HAP_REPR_AID: acc.aid,
                HAP_REPR_IID: char_on_iid,
                HAP_REPR_VALUE: True
            }, {
                HAP_REPR_AID: acc.aid,
                HAP_REPR_IID: char_brightness_iid,
                HAP_REPR_VALUE: 88
            }, {
                HAP_REPR_AID: acc2.aid,
                HAP_REPR_IID: char_on2_iid,
                HAP_REPR_VALUE: True
            }, {
                HAP_REPR_AID: acc2.aid,
                HAP_REPR_IID: char_brightness2_iid,
                HAP_REPR_VALUE: 12
            }]
        }, "mock_addr")

    mock_callback2.assert_called_with({'On': True, 'Brightness': 12})
    mock_callback.assert_called_with({'On': True, 'Brightness': 88})
Esempio n. 11
0
def test_acc_publish_no_broker(mock_driver):
    acc = Accessory(mock_driver, "Test Accessory")
    service = acc.driver.loader.get_service("TemperatureSensor")
    char = service.get_characteristic("CurrentTemperature")
    acc.add_service(service)
    char.set_value(25, should_notify=True)
Esempio n. 12
0
logging.basicConfig(level=logging.INFO,
                    format="[%(asctime)s] [%(module)-16s] [%(levelname)-8s] %(message)s",
                    datefmt="%I:%M:%S %p")

# Start the accessory on port 51826
driver = AccessoryDriver(port=51827,
                         persist_file=accessory_state)

mqtt_bridge = HapMqtt(MQTTSERVER, driver, "mqtt_bridge")

# get loaders
service_loader = loader.get_loader()
char_loader = loader.get_loader()

test = Accessory(driver, "Switch", aid=999196)
test.add_service(service_loader.get_service("Switch"))

accessories = list()
accessories.append(BasicLight("outside", MQTTSERVER, driver, "flood_1", aid=1111))
accessories.append(BasicLight("outside", MQTTSERVER, driver, "flood_2", aid=2222))
accessories.append(BasicLight("outside", MQTTSERVER, driver, "flood_3", aid=3333))
accessories.append(BasicLight("outside", MQTTSERVER, driver, "flood_4", aid=4444))
accessories.append(TemperatureSensor(driver, "fake_temp"))


# accessories.append(test)
# MqttAccessories(TemperatureSensor(MQTTSERVER, driver, "Battery_1", aid=2323))
# MqttAccessories.accessories[2].add_service(service_loader.get_service("BatteryService"))


# Add the accessories and the topics to the mqtt bridge
Esempio n. 13
0
    def test_write_characteristic(self):
        accessory = Accessory(name='PyHAP',
                              model='PyHAP1,1',
                              manufacturer='PyHAP',
                              hardware_revision='0')
        service = LightbulbService()

        bool_characteristic = On(False)
        int_characteristic = Brightness(8)
        float_characteristic = Hue(5.0)

        service.add_characteristic(bool_characteristic)
        service.add_characteristic(int_characteristic)
        service.add_characteristic(float_characteristic)

        accessories = Accessories()
        accessory.add_service(service)
        accessories.add(accessory)

        # bool characteristic
        callback = AsyncMock()
        bool_characteristic.callback = callback

        self.assertEqual(bool_characteristic.value, False)
        result = asyncio.get_event_loop().run_until_complete(
            accessories.write_characteristic([{
                'aid': 2,
                'iid': 10,
                'value': True
            }]))
        callback.assert_called_once_with(True)
        self.assertEqual(result, [])
        self.assertEqual(bool_characteristic.value, True)

        # int characteristic write
        callback = AsyncMock()
        int_characteristic.callback = callback

        self.assertEqual(int_characteristic.value, 8)
        result = asyncio.get_event_loop().run_until_complete(
            accessories.write_characteristic([{
                'aid': 2,
                'iid': 11,
                'value': 12
            }]))
        callback.assert_called_once_with(12)
        self.assertEqual(result, [])
        self.assertEqual(int_characteristic.value, 12)

        # float characteristic write
        callback = AsyncMock()
        float_characteristic.callback = callback

        self.assertEqual(float_characteristic.value, 5.0)
        result = asyncio.get_event_loop().run_until_complete(
            accessories.write_characteristic([{
                'aid': 2,
                'iid': 12,
                'value': 7.0
            }]))
        callback.assert_called_once_with(7.0)
        self.assertEqual(result, [])
        self.assertEqual(float_characteristic.value, 7.0)

        # None value during write, leave previous value
        previous_value = bool_characteristic.value
        result = asyncio.get_event_loop().run_until_complete(
            accessories.write_characteristic([{
                'aid': 2,
                'iid': 10
            }]))
        self.assertEqual(result, [])
        self.assertEqual(bool_characteristic.value, previous_value)
        bool_characteristic.value = previous_value

        # None callback
        bool_characteristic.callback = None
        result = asyncio.get_event_loop().run_until_complete(
            accessories.write_characteristic([{
                'aid': 2,
                'iid': 10,
                'value': True
            }]))
        self.assertEqual(result, [])

        # Exception in callback
        bool_characteristic.callback = exception_callback
        result = asyncio.get_event_loop().run_until_complete(
            accessories.write_characteristic([{
                'aid': 2,
                'iid': 10,
                'value': True
            }]))
        self.assertEqual(result, [{
            'aid': 2,
            'iid': 10,
            'status': -70402,
        }])
Esempio n. 14
0
    def load_accessories(self, override_ids=False):
        """
        Return a list of initialized accessories specified by the config files.

        The characteristics of those accessories should contain three additional
        values: topic_in, topic_out and adapter used by the MqttBridge class.

        :param override_ids: override accessory ids for a new bridge config
        :type override_ids: bool
        """
        # find all cfg files, but skip the bridge.cfg
        fnames = []
        for r, _, fs in os.walk(self.cfg_path):
            fnames += [
                os.path.join(r, f) for f in fs
                if f != 'bridge.cfg' and f != 'accessory.state'
            ]

        for fname in fnames:
            cfg = configparser.ConfigParser()
            cfg.optionxform = str
            cfg.fname = fname

            try:
                cfg.read(fname)
                self.cfgs.append(cfg)
            except Exception as e:
                logger.warn('Skipping "{}" because of Exception: {}'.format(
                    fname, str(e)))

        # sort by aid
        max_aid = 2**63 - 1
        self.cfgs = sorted(
            self.cfgs,
            key=lambda cfg: int(cfg['Accessory'].get('AID', max_aid)))

        self.accs = []
        for cfg in self.cfgs:
            try:
                # init accessory
                acc_def = cfg['Accessory']
                if acc_def['Category'] not in categories:
                    logger.warn('Unknown category: "{}"'.format(
                        acc_def['category']))
                    continue
                if 'DisplayName' not in acc_def.keys():
                    # use filename as display name
                    acc_def['DisplayName'] = os.path.basename(fname).split(
                        '.')[0]

                aid = None
                if not override_ids:
                    aid = acc_def.get('AID', None)
                if aid is not None:
                    aid = int(aid)

                acc = Accessory(self.driver, acc_def['DisplayName'], aid=aid)
                acc.category = categories[acc_def.get('Category', 'Other')]

                acc.set_info_service(acc_def.get('FirmwareRevision', None),
                                     acc_def.get('Manufacturer', None),
                                     acc_def.get('Model', None),
                                     acc_def.get('SerialNumber', None))

                # init services
                serv_types = cfg.sections()
                serv_types.remove('Accessory')
                for serv_type in serv_types:
                    serv_def = cfg[serv_type]
                    serv = loader.get_loader().get_service(serv_type)
                    char_types = serv_def.keys()

                    for char_type in char_types:
                        char_def = serv_def[char_type]
                        char_def = char_def.split()

                        # init characteristic
                        try:
                            char = loader.get_loader().get_char(char_type)

                            if len(char_def) != 3:
                                logger.warn(
                                    'Skipping caracteristic "{}" because of invalid format'
                                    .format(char_type))
                                continue

                            # add topics and adapter
                            char.properties['topic_in'] = None
                            char.properties['topic_out'] = None
                            char.properties['adapter'] = None

                            if char_def[0] != '_':
                                char.properties['topic_in'] = char_def[0]
                            if char_def[1] != '_':
                                char.properties['topic_out'] = char_def[1]
                            if char_def[2] != '_':
                                char.properties['adapter'] = char_def[2]

                            # add characteristic
                            added = False
                            for i, old_char in enumerate(serv.characteristics):
                                if old_char.type_id == char.type_id:
                                    serv.characteristics[i] = char
                                    added = True
                                    break

                            if not added:
                                serv.add_characteristic(char)

                        except KeyError as e:
                            continue

                    acc.add_service(serv)

                self.accs.append(acc)
                logger.info('Added accessory "{}"'.format(acc.display_name))

            except Exception as e:
                logger.warn(
                    'Skipping "{}" because of Exception: {}: {}'.format(
                        type(e), cfg.fname, str(e)))

        return self.accs