Fix BlueZ GATT server registration and OBD-II mode 03/04 parsing
Add @dbus_property(PropertyAccess.READ) to GattServiceIface, GattCharacteristicIface, and GattDescriptorIface so BlueZ can validate objects via the standard Properties interface — fixes "No valid service object found" error on RegisterApplication. Also fix ELM327 emulator rejecting 2-char OBD commands (mode 03 for DTCs and mode 04 for clear) by padding to 4 chars.
This commit is contained in:
parent
d0f0565e62
commit
5dc5f640c7
@ -24,7 +24,7 @@ from typing import Any
|
|||||||
|
|
||||||
from dbus_fast import BusType, Message, MessageType, Variant
|
from dbus_fast import BusType, Message, MessageType, Variant
|
||||||
from dbus_fast.aio import MessageBus
|
from dbus_fast.aio import MessageBus
|
||||||
from dbus_fast.service import ServiceInterface, dbus_property, method
|
from dbus_fast.service import PropertyAccess, ServiceInterface, dbus_property, method
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -130,11 +130,22 @@ class GattApplication(ServiceInterface):
|
|||||||
class GattServiceIface(ServiceInterface):
|
class GattServiceIface(ServiceInterface):
|
||||||
"""org.bluez.GattService1 — exported at each service path.
|
"""org.bluez.GattService1 — exported at each service path.
|
||||||
|
|
||||||
No D-Bus methods; properties served via GetManagedObjects only.
|
Properties must be exposed via @dbus_property so BlueZ can validate
|
||||||
|
the service object through the standard Properties interface.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, uuid: str, primary: bool):
|
||||||
super().__init__("org.bluez.GattService1")
|
super().__init__("org.bluez.GattService1")
|
||||||
|
self._uuid = uuid
|
||||||
|
self._primary = primary
|
||||||
|
|
||||||
|
@dbus_property(PropertyAccess.READ)
|
||||||
|
def UUID(self) -> "s": # noqa: F821
|
||||||
|
return self._uuid
|
||||||
|
|
||||||
|
@dbus_property(PropertyAccess.READ)
|
||||||
|
def Primary(self) -> "b": # noqa: F821
|
||||||
|
return self._primary
|
||||||
|
|
||||||
|
|
||||||
class GattCharacteristicIface(ServiceInterface):
|
class GattCharacteristicIface(ServiceInterface):
|
||||||
@ -142,6 +153,7 @@ class GattCharacteristicIface(ServiceInterface):
|
|||||||
|
|
||||||
ReadValue returns the stored value. WriteValue stores the value AND
|
ReadValue returns the stored value. WriteValue stores the value AND
|
||||||
records a WriteEvent for LLM polling (like agent.py's pending_requests).
|
records a WriteEvent for LLM polling (like agent.py's pending_requests).
|
||||||
|
Properties exposed via @dbus_property for BlueZ introspection.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, char: ServerCharacteristic, manager: "GattServerManager"):
|
def __init__(self, char: ServerCharacteristic, manager: "GattServerManager"):
|
||||||
@ -149,6 +161,18 @@ class GattCharacteristicIface(ServiceInterface):
|
|||||||
self._char = char
|
self._char = char
|
||||||
self._manager = manager
|
self._manager = manager
|
||||||
|
|
||||||
|
@dbus_property(PropertyAccess.READ)
|
||||||
|
def UUID(self) -> "s": # noqa: F821
|
||||||
|
return self._char.uuid
|
||||||
|
|
||||||
|
@dbus_property(PropertyAccess.READ)
|
||||||
|
def Service(self) -> "o": # noqa: F821
|
||||||
|
return f"{APP_BASE_PATH}/{self._char.service_id}"
|
||||||
|
|
||||||
|
@dbus_property(PropertyAccess.READ)
|
||||||
|
def Flags(self) -> "as": # noqa: F821
|
||||||
|
return self._char.flags
|
||||||
|
|
||||||
@method()
|
@method()
|
||||||
def ReadValue(self, options: "a{sv}") -> "ay": # noqa: F821
|
def ReadValue(self, options: "a{sv}") -> "ay": # noqa: F821
|
||||||
offset = 0
|
offset = 0
|
||||||
@ -189,12 +213,27 @@ class GattCharacteristicIface(ServiceInterface):
|
|||||||
|
|
||||||
|
|
||||||
class GattDescriptorIface(ServiceInterface):
|
class GattDescriptorIface(ServiceInterface):
|
||||||
"""org.bluez.GattDescriptor1 — handles reads/writes on descriptors."""
|
"""org.bluez.GattDescriptor1 — handles reads/writes on descriptors.
|
||||||
|
|
||||||
|
Properties exposed via @dbus_property for BlueZ introspection.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, desc: ServerDescriptor):
|
def __init__(self, desc: ServerDescriptor):
|
||||||
super().__init__("org.bluez.GattDescriptor1")
|
super().__init__("org.bluez.GattDescriptor1")
|
||||||
self._desc = desc
|
self._desc = desc
|
||||||
|
|
||||||
|
@dbus_property(PropertyAccess.READ)
|
||||||
|
def UUID(self) -> "s": # noqa: F821
|
||||||
|
return self._desc.uuid
|
||||||
|
|
||||||
|
@dbus_property(PropertyAccess.READ)
|
||||||
|
def Characteristic(self) -> "o": # noqa: F821
|
||||||
|
return f"{APP_BASE_PATH}/{self._desc.char_id}"
|
||||||
|
|
||||||
|
@dbus_property(PropertyAccess.READ)
|
||||||
|
def Flags(self) -> "as": # noqa: F821
|
||||||
|
return self._desc.flags
|
||||||
|
|
||||||
@method()
|
@method()
|
||||||
def ReadValue(self, options: "a{sv}") -> "ay": # noqa: F821
|
def ReadValue(self, options: "a{sv}") -> "ay": # noqa: F821
|
||||||
offset = 0
|
offset = 0
|
||||||
@ -226,15 +265,15 @@ class LEAdvertisementIface(ServiceInterface):
|
|||||||
def Release(self) -> None:
|
def Release(self) -> None:
|
||||||
log.info("GATT server: advertisement released by BlueZ")
|
log.info("GATT server: advertisement released by BlueZ")
|
||||||
|
|
||||||
@dbus_property()
|
@dbus_property(PropertyAccess.READ)
|
||||||
def Type(self) -> "s": # noqa: F821
|
def Type(self) -> "s": # noqa: F821
|
||||||
return self._type
|
return self._type
|
||||||
|
|
||||||
@dbus_property()
|
@dbus_property(PropertyAccess.READ)
|
||||||
def LocalName(self) -> "s": # noqa: F821
|
def LocalName(self) -> "s": # noqa: F821
|
||||||
return self._local_name
|
return self._local_name
|
||||||
|
|
||||||
@dbus_property()
|
@dbus_property(PropertyAccess.READ)
|
||||||
def ServiceUUIDs(self) -> "as": # noqa: F821
|
def ServiceUUIDs(self) -> "as": # noqa: F821
|
||||||
return self._service_uuids
|
return self._service_uuids
|
||||||
|
|
||||||
@ -277,7 +316,7 @@ class GattServerManager:
|
|||||||
uuid=uuid,
|
uuid=uuid,
|
||||||
primary=primary,
|
primary=primary,
|
||||||
)
|
)
|
||||||
svc.dbus_obj = GattServiceIface()
|
svc.dbus_obj = GattServiceIface(uuid, primary)
|
||||||
self._services[service_id] = svc
|
self._services[service_id] = svc
|
||||||
|
|
||||||
log.info("GATT server: added service %s (UUID=%s)", service_id, uuid)
|
log.info("GATT server: added service %s (UUID=%s)", service_id, uuid)
|
||||||
|
|||||||
@ -231,7 +231,10 @@ class ELM327Emulator:
|
|||||||
cr = "\r\n" if self.linefeed else "\r"
|
cr = "\r\n" if self.linefeed else "\r"
|
||||||
cmd = cmd.replace(" ", "")
|
cmd = cmd.replace(" ", "")
|
||||||
|
|
||||||
if len(cmd) < 4:
|
# Modes 03/04 don't require a PID — pad to 4 chars
|
||||||
|
if len(cmd) == 2:
|
||||||
|
cmd += "00"
|
||||||
|
elif len(cmd) < 4:
|
||||||
return f"?{cr}{cr}>"
|
return f"?{cr}{cr}>"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user