`dasbus.error.DBusError: No valid service object found` from Bluez - bluetooth-lowenergy

I've run out of ideas on how to debug this... I'm attempting to implement a Bluetooth Low Energy Peripheral in Python, and keep getting this error:
dasbus.error.DBusError: No valid service object found
Here's what's happening on sudo dbus-monitor --system "destination='org.bluez'" "sender='org.bluez'"
method call time=1638075250.607870 sender=:1.45928 -> destination=org.bluez serial=2 path=/org/bluez/hci0; interface=org.freedesktop.DBus.Introspectable; member=Introspect
method return time=1638075250.608574 sender=:1.5 -> destination=:1.45928 serial=1663 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node><interface name="org.freedesktop.DBus.Introspectable"><method name="Introspect"><arg name="xml" type="s" direction="out"/>
</method></interface><interface name="org.bluez.Adapter1"><method name="StartDiscovery"></method><method name="SetDiscoveryFilter"><arg name="properties" type="a{sv}" direction="in"/>
</method><method name="StopDiscovery"></method><method name="RemoveDevice"><arg name="device" type="o" direction="in"/>
</method><method name="GetDiscoveryFilters"><arg name="filters" type="as" direction="out"/>
</method><property name="Address" type="s" access="read"></property><property name="AddressType" type="s" access="read"></property><property name="Name" type="s" access="read"></property><property name="Alias" type="s" access="readwrite"></property><property name="Class" type="u" access="read"></property><property name="Powered" type="b" access="readwrite"></property><property name="Discoverable" type="b" access="readwrite"></property><property name="DiscoverableTimeout" type="u" access="readwrite"></property><property name="Pairable" type="b" access="readwrite"></property><property name="PairableTimeout" type="u" access="readwrite"></property><property name="Discovering" type="b" access="read"></property><property name="UUIDs" type="as" access="read"></property><property name="Modalias" type="s" access="read"></property></interface><interface name="org.freedesktop.DBus.Properties"><method name="Get"><arg name="interface" type="s" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="value" type="v" direction="out"/>
</method><method name="Set"><arg name="interface" type="s" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
</method><method name="GetAll"><arg name="interface" type="s" direction="in"/>
<arg name="properties" type="a{sv}" direction="out"/>
</method><signal name="PropertiesChanged"><arg name="interface" type="s"/>
<arg name="changed_properties" type="a{sv}"/>
<arg name="invalidated_properties" type="as"/>
</signal>
</interface><interface name="org.bluez.GattManager1"><method name="RegisterApplication"><arg name="application" type="o" direction="in"/>
<arg name="options" type="a{sv}" direction="in"/>
</method><method name="UnregisterApplication"><arg name="application" type="o" direction="in"/>
</method></interface><interface name="org.bluez.LEAdvertisingManager1"><method name="RegisterAdvertisement"><arg name="advertisement" type="o" direction="in"/>
<arg name="options" type="a{sv}" direction="in"/>
</method><method name="UnregisterAdvertisement"><arg name="service" type="o" direction="in"/>
</method><property name="ActiveInstances" type="y" access="read"></property><property name="SupportedInstances" type="y" access="read"></property><property name="SupportedIncludes" type="as" access="read"></property></interface><interface name="org.bluez.Media1"><method name="RegisterEndpoint"><arg name="endpoint" type="o" direction="in"/>
<arg name="properties" type="a{sv}" direction="in"/>
</method><method name="UnregisterEndpoint"><arg name="endpoint" type="o" direction="in"/>
</method><method name="RegisterPlayer"><arg name="player" type="o" direction="in"/>
<arg name="properties" type="a{sv}" direction="in"/>
</method><method name="UnregisterPlayer"><arg name="player" type="o" direction="in"/>
</method></interface><interface name="org.bluez.NetworkServer1"><method name="Register"><arg name="uuid" type="s" direction="in"/>
<arg name="bridge" type="s" direction="in"/>
</method><method name="Unregister"><arg name="uuid" type="s" direction="in"/>
</method></interface></node>"
method call time=1638075250.617093 sender=:1.45928 -> destination=org.bluez serial=3 path=/org/bluez/hci0; interface=org.freedesktop.DBus.Properties; member=Set
string "org.bluez.Adapter1"
string "Powered"
variant boolean true
method return time=1638075250.617637 sender=:1.5 -> destination=:1.45928 serial=1664 reply_serial=3
method call time=1638075250.619644 sender=:1.45928 -> destination=org.bluez serial=4 path=/org/bluez/hci0; interface=org.freedesktop.DBus.Properties; member=Get
string "org.bluez.Adapter1"
string "Powered"
method return time=1638075250.620197 sender=:1.5 -> destination=:1.45928 serial=1665 reply_serial=4
variant boolean true
method call time=1638075250.627078 sender=:1.45928 -> destination=org.bluez serial=5 path=/org/bluez/hci0; interface=org.bluez.LEAdvertisingManager1; member=RegisterAdvertisement
object path "/com/ah/Biofeedback/Advertisement/1"
array [
]
method call time=1638075250.627809 sender=:1.5 -> destination=org.freedesktop.DBus serial=1666 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
string "type='signal',sender='org.freedesktop.DBus',path='/org/freedesktop/DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0=':1.45928'"
method return time=1638075250.627870 sender=org.freedesktop.DBus -> destination=:1.5 serial=1174 reply_serial=1666
method call time=1638075250.628483 sender=:1.5 -> destination=org.freedesktop.DBus serial=1667 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=GetNameOwner
string ":1.45928"
method return time=1638075250.628534 sender=org.freedesktop.DBus -> destination=:1.5 serial=1175 reply_serial=1667
string ":1.45928"
method call time=1638075250.628903 sender=:1.5 -> destination=org.freedesktop.DBus serial=1668 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
string "type='signal',sender=':1.45928',path='/com/ah/Biofeedback/Advertisement/1',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'"
method return time=1638075250.628958 sender=org.freedesktop.DBus -> destination=:1.5 serial=1176 reply_serial=1668
method call time=1638075250.629497 sender=:1.5 -> destination=org.freedesktop.DBus serial=1669 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
string "type='signal',sender=':1.45928',path='/com/ah/Biofeedback/Advertisement/1',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved'"
method return time=1638075250.629572 sender=org.freedesktop.DBus -> destination=:1.5 serial=1177 reply_serial=1669
method call time=1638075250.629875 sender=:1.5 -> destination=org.freedesktop.DBus serial=1670 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
string "type='signal',sender=':1.45928',path_namespace='/com/ah/Biofeedback/Advertisement/1'"
method return time=1638075250.629928 sender=org.freedesktop.DBus -> destination=:1.5 serial=1178 reply_serial=1670
method call time=1638075250.630459 sender=:1.5 -> destination=org.freedesktop.DBus serial=1671 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
string "type='signal',sender=':1.45928',path='/com/ah/Biofeedback/Advertisement/1',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',arg0='org.bluez.LEAdvertisement1'"
method return time=1638075250.630517 sender=org.freedesktop.DBus -> destination=:1.5 serial=1179 reply_serial=1671
method call time=1638075250.631390 sender=:1.5 -> destination=org.freedesktop.DBus serial=1672 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=StartServiceByName
string ":1.45928"
uint32 0
error time=1638075250.631460 sender=org.freedesktop.DBus -> destination=:1.5 error_name=org.freedesktop.DBus.Error.ServiceUnknown reply_serial=1672
string "The name :1.45928 was not provided by any .service files"
method call time=1638075250.631553 sender=:1.5 -> destination=:1.45928 serial=1673 path=/com/ah/Biofeedback/Advertisement/1; interface=org.freedesktop.DBus.ObjectManager; member=GetManagedObjects
error time=1638075250.632629 sender=:1.45928 -> destination=:1.5 error_name=org.freedesktop.DBus.Error.UnknownMethod reply_serial=1673
string "No such interface “org.freedesktop.DBus.ObjectManager” on object at path /com/ah/Biofeedback/Advertisement/1"
method call time=1638075250.632916 sender=:1.5 -> destination=:1.45928 serial=1674 path=/com/ah/Biofeedback/Advertisement/1; interface=org.freedesktop.DBus.Properties; member=GetAll
string "org.bluez.LEAdvertisement1"
method return time=1638075250.639601 sender=:1.45928 -> destination=:1.5 serial=7 reply_serial=1674
array [
dict entry(
string "Appearance"
variant uint16 768
)
dict entry(
string "Discoverable"
variant boolean true
)
dict entry(
string "DiscoverableTimeout"
variant uint16 10000
)
dict entry(
string "LocalName"
variant string "AH Biofeedback Device"
)
dict entry(
string "ServiceUUIDS"
variant array [
string "what"
]
)
dict entry(
string "Type"
variant string "peripheral"
)
]
signal time=1638075250.641071 sender=:1.5 -> destination=(null destination) serial=1675 path=/org/bluez/hci0; interface=org.freedesktop.DBus.Properties; member=PropertiesChanged
string "org.bluez.LEAdvertisingManager1"
array [
dict entry(
string "SupportedInstances"
variant byte 4
)
dict entry(
string "ActiveInstances"
variant byte 1
)
]
array [
]
method return time=1638075250.641346 sender=:1.5 -> destination=:1.45928 serial=1676 reply_serial=5
method call time=1638075250.649537 sender=:1.45928 -> destination=org.bluez serial=8 path=/org/bluez/hci0; interface=org.bluez.GattManager1; member=RegisterApplication
object path "/com/ah/Biofeedback/Application/1"
array [
]
method call time=1638075250.649994 sender=:1.5 -> destination=org.freedesktop.DBus serial=1677 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=GetNameOwner
string ":1.45928"
method return time=1638075250.650051 sender=org.freedesktop.DBus -> destination=:1.5 serial=1181 reply_serial=1677
string ":1.45928"
method call time=1638075250.650459 sender=:1.5 -> destination=org.freedesktop.DBus serial=1678 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
string "type='signal',sender=':1.45928',path='/com/ah/Biofeedback/Application/1',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'"
method return time=1638075250.650515 sender=org.freedesktop.DBus -> destination=:1.5 serial=1182 reply_serial=1678
method call time=1638075250.651102 sender=:1.5 -> destination=org.freedesktop.DBus serial=1679 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
string "type='signal',sender=':1.45928',path='/com/ah/Biofeedback/Application/1',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved'"
method return time=1638075250.651159 sender=org.freedesktop.DBus -> destination=:1.5 serial=1183 reply_serial=1679
method call time=1638075250.651590 sender=:1.5 -> destination=org.freedesktop.DBus serial=1680 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
string "type='signal',sender=':1.45928',path_namespace='/com/ah/Biofeedback/Application/1'"
method return time=1638075250.651646 sender=org.freedesktop.DBus -> destination=:1.5 serial=1184 reply_serial=1680
method call time=1638075250.651759 sender=:1.5 -> destination=:1.45928 serial=1681 path=/com/ah/Biofeedback/Application/1; interface=org.freedesktop.DBus.ObjectManager; member=GetManagedObjects
method return time=1638075250.659644 sender=:1.45928 -> destination=:1.5 serial=9 reply_serial=1681
array [
dict entry(
object path "/com/ah/Biofeedback/Service/1"
array [
dict entry(
string "org.bluez.GattService1"
array [
dict entry(
string "UUID"
variant string "service-uuid-abc"
)
dict entry(
string "Primary"
variant boolean true
)
dict entry(
string "Characteristics"
variant array [
object path "/com/ah/Biofeedback/Characteristic/1"
]
)
]
)
]
)
dict entry(
object path "/com/ah/Biofeedback/Characteristic/1"
array [
dict entry(
string "org.bluez.GattCharacteristic1"
array [
dict entry(
string "Service"
variant object path "/com/ah/Biofeedback/Service/1"
)
dict entry(
string "UUID"
variant string "characteristic-uuid-abc"
)
dict entry(
string "Flags"
variant array [
string "broadcast"
]
)
dict entry(
string "Descriptors"
variant array [
]
)
]
)
]
)
]
method call time=1638075250.660665 sender=:1.5 -> destination=org.freedesktop.DBus serial=1682 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
string "type='signal',sender=':1.45928',path='/com/ah/Biofeedback/Service/1',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',arg0='org.bluez.GattService1'"
method return time=1638075250.660724 sender=org.freedesktop.DBus -> destination=:1.5 serial=1185 reply_serial=1682
method call time=1638075250.663350 sender=:1.5 -> destination=org.freedesktop.DBus serial=1683 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
string "type='signal',sender=':1.45928',path='/com/ah/Biofeedback/Characteristic/1',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',arg0='org.bluez.GattCharacteristic1'"
method return time=1638075250.663436 sender=org.freedesktop.DBus -> destination=:1.5 serial=1186 reply_serial=1683
error time=1638075250.663466 sender=:1.5 -> destination=:1.45928 error_name=org.bluez.Error.Failed reply_serial=8
string "No valid service object found"
method call time=1638075250.663509 sender=:1.5 -> destination=org.freedesktop.DBus serial=1685 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=RemoveMatch
string "type='signal',sender=':1.45928',path_namespace='/com/ah/Biofeedback/Application/1'"
method return time=1638075250.663556 sender=org.freedesktop.DBus -> destination=:1.5 serial=1187 reply_serial=1685
method call time=1638075250.663584 sender=:1.5 -> destination=org.freedesktop.DBus serial=1686 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=RemoveMatch
string "type='signal',sender=':1.45928',path='/com/ah/Biofeedback/Service/1',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',arg0='org.bluez.GattService1'"
method return time=1638075250.663634 sender=org.freedesktop.DBus -> destination=:1.5 serial=1188 reply_serial=1686
method call time=1638075250.663663 sender=:1.5 -> destination=org.freedesktop.DBus serial=1687 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=RemoveMatch
string "type='signal',sender=':1.45928',path='/com/ah/Biofeedback/Characteristic/1',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',arg0='org.bluez.GattCharacteristic1'"
method return time=1638075250.663712 sender=org.freedesktop.DBus -> destination=:1.5 serial=1189 reply_serial=1687
method call time=1638075250.663827 sender=:1.5 -> destination=org.freedesktop.DBus serial=1688 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=RemoveMatch
string "type='signal',sender=':1.45928',path='/com/ah/Biofeedback/Application/1',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'"
method return time=1638075250.663880 sender=org.freedesktop.DBus -> destination=:1.5 serial=1190 reply_serial=1688
method call time=1638075250.664064 sender=:1.5 -> destination=org.freedesktop.DBus serial=1689 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=RemoveMatch
string "type='signal',sender=':1.45928',path='/com/ah/Biofeedback/Application/1',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved'"
method return time=1638075250.664119 sender=org.freedesktop.DBus -> destination=:1.5 serial=1191 reply_serial=1689
Here's my code:
from dasbus.connection import SystemMessageBus
from dasbus.typing import *
from dasbus.server.interface import dbus_interface
from dasbus.server.interface import dbus_signal
from dasbus.server.publishable import Publishable
from dasbus.server.template import InterfaceTemplate
from dasbus.loop import EventLoop
from threading import Thread
#dbus_interface("org.bluez.LEAdvertisement1")
class BiofeedackAdvertisement(object):
def Release(self) -> None:
print("released")
#property
def Type(self) -> Str:
return "peripheral"
#property
def ServiceUUIDS(self) -> List[Str]:
return ["what"]
#property
def Discoverable(self) -> Bool:
return True
#property
def DiscoverableTimeout(self) -> UInt16:
return 10000
#property
def LocalName(self) -> Str:
return "AH Biofeedback Device"
# https://specificationrefs.bluetooth.com/assigned-values/Appearance%20Values.pdf
#property
def Appearance(self) -> UInt16:
return 768 # Thermometer
# path: "/com/ah/Biofeedback/Application/1"
class Application(Publishable):
def for_publication(self):
return ApplicationInterface(self)
#dbus_interface("org.bluez.GattApplication1")
class ApplicationGattInterface(InterfaceTemplate):
pass
#dbus_interface("org.freedesktop.DBus.ObjectManager")
class ApplicationInterface(ApplicationGattInterface):
def GetManagedObjects(self) -> Dict[ObjPath, Dict[Str, Dict[Str, Variant]]]:
return {
"/com/ah/Biofeedback/Service/1": ServiceInterface(Service()).get_properties(),
"/com/ah/Biofeedback/Characteristic/1": CharacteristicInterface(Characteristic()).get_properties(),
# "/com/ah/Biofeedback/Descriptor/1": Descriptor().get_properties(),
}
class Service(Publishable):
def for_publication(self):
return ServiceInterface(self)
# Not sure if this is being used, but it's not the problem right now.
#dbus_interface("org.freedesktop.DBus.Properties")
class ServiceInterfaceProperties(InterfaceTemplate):
#dbus_signal
def PropertiesChanged(self, interface: Str, changed_properties: Dict[Str, Variant], invalidated_properties: List[Str]):
print("PROPS CHANGED")
pass
def GetAll(self, interface: Str) -> Dict[Str, Variant]:
print("GETALL")
return self.get_properties()["org.bluez.GattService1"]
#dbus_interface("org.bluez.GattService1")
class ServiceInterfaceGatt(InterfaceTemplate):
#property
def UUID(self) -> Str:
return "service-uuid-abc"
#property
def Primary(self) -> Bool:
return True
# path: /com/ah/Biofeedback/Service/1
class ServiceInterface(ServiceInterfaceGatt, ServiceInterfaceProperties):
_UUID = Variant("s", "service-uuid-abc")
def get_properties(self):
return {
"org.bluez.GattService1": {
"UUID": self._UUID,
"Primary": Variant("b", True),
"Characteristics": Variant.new_array(None, [Variant("o", "/com/ah/Biofeedback/Characteristic/1")]),
}
}
class Characteristic(Publishable):
def for_publication(self):
return CharacteristicInterface(self)
#dbus_interface("org.bluez.GattCharacteristic1")
class CharacteristicInterfaceGatt(InterfaceTemplate):
def ReadValue(self, options: Dict[Str, Str]) -> List[Byte]:
print("TRIED TO READ VALUE")
return [0xDEAD]
def WriteValue(self, value: List[Byte], options: Dict[Str, Str]) -> None:
print("TRIED TO WRITE VALUE")
pass
#property
def UUID(self) -> Str:
return "characteristic-uuid-abc"
#property
def Service(self) -> ObjPath:
return "/com/ah/Biofeedback/Service/1"
# path: /com/ah/Biofeedback/Characteristic/1
class CharacteristicInterface(CharacteristicInterfaceGatt):
_UUID = Variant("s", "characteristic-uuid-abc")
def get_properties(self):
return {
"org.bluez.GattCharacteristic1": {
"Service": Variant("s", "/com/ah/Biofeedback/Service/1"),
"UUID": self._UUID,
"Flags": Variant.new_array(None, [Variant("s", "broadcast")]),
#"Descriptors": Variant.new_array(None, [Variant("o", "/com/ah/Biofeedback/Descriptor/1")]),
"Descriptors": Variant.new_array(VariantType("t"), []),
}
}
# path: /com/ah/Biofeedback/Descriptor/1
# #dbus_interface("org.bluez.GattDescriptor1")
class Descriptor(object):
_UUID = Variant("s", "descriptor-uuid-1vc")
def get_properties(self):
return {
"org.bluez.GattDescriptor1": {
"Characteristic": Variant("s", "/com/ah/Biofeedback/Characteristic/1"),
"UUID": self._UUID,
"Flags": Variant.new_array(VariantType("t"), []),
}
}
def main():
bus = SystemMessageBus()
Thread(target=EventLoop().run).start()
bus.register_service("com.ah.Biofeedback")
proxy = bus.get_proxy("org.bluez", "/org/bluez/hci0")
proxy.Set("org.bluez.Adapter1", "Powered", Variant("b", True))
print(proxy.Get("org.bluez.Adapter1", "Powered"))
advertisementPath = "/com/ah/Biofeedback/Advertisement/1"
bus.publish_object(advertisementPath, BiofeedackAdvertisement())
proxy.RegisterAdvertisement(advertisementPath, {})
bus.publish_object("/com/ah/Biofeedback/Service/1", Service().for_publication())
bus.publish_object("/com/ah/Biofeedback/Characteristic/1", Characteristic().for_publication())
appPath = "/com/ah/Biofeedback/Application/1"
bus.publish_object(appPath, Application().for_publication())
proxy.RegisterApplication(appPath, {})
if __name__ == '__main__':
main()
It looks like the Service is registered on the bus:
sudo dbus-send --system --print-reply \
> --dest=:1.46935 \
> /com/ah/Biofeedback/Service/1 \
> org.freedesktop.DBus.Introspectable.Introspect
method return time=1638120479.547238 sender=:1.13051 -> destination=:1.13060 serial=12 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<!-- GDBus 2.58.3 -->
<node>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg type="s" name="interface_name" direction="in"/>
<arg type="s" name="property_name" direction="in"/>
<arg type="v" name="value" direction="out"/>
</method>
<method name="GetAll">
<arg type="s" name="interface_name" direction="in"/>
<arg type="a{sv}" name="properties" direction="out"/>
</method>
<method name="Set">
<arg type="s" name="interface_name" direction="in"/>
<arg type="s" name="property_name" direction="in"/>
<arg type="v" name="value" direction="in"/>
</method>
<signal name="PropertiesChanged">
<arg type="s" name="interface_name"/>
<arg type="a{sv}" name="changed_properties"/>
<arg type="as" name="invalidated_properties"/>
</signal>
</interface>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg type="s" name="xml_data" direction="out"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Peer">
<method name="Ping"/>
<method name="GetMachineId">
<arg type="s" name="machine_uuid" direction="out"/>
</method>
</interface>
<interface name="org.bluez.GattService1">
<property type="b" name="Primary" access="read">
</property>
<property type="s" name="UUID" access="read">
</property>
</interface>
</node>
"
/var/log/syslog gives a little clue:
bluetoothd[22894]: Failed to read "UUID" property of service
bluetoothd[22894]: Failed to add service
bluetoothd[22894]: Failed to create GATT service entry in local database
The dependencies were a pain to setup, so here's a docker container:
docker run -it --rm \
--privileged \
--workdir /app \
--volume "$PWD":/app \
--volume /var/run/dbus/:/var/run/dbus/:z \
soodesune/python3-bluetooth dbus-bluetooth.py
What else can I do to debug this error? How can I figure out what's wrong with my Service UUID?

Figured it out! There was a long list of small problems that I had to work through. Here's the process that I followed to debug:
Run sudo dbus-monitor --system "destination='org.bluez'" "sender='org.bluez'" to see what Bluez is sending/receiving from dbus
Watch /var/log/syslog to see what errors Bluez is logging, but /not/ sending to dbus
Download the source code for your version of Bluez
Run your program
Then:
Make sure the traffic on dbus looks right
When you see errors in /var/log/syslog grep the source code for the error and see what went wrong. Most of the time there was type-checking logic that I wasn't satisfying. For example: the UUIDs have some pretty specific checks, and I had a few Object Path types mis-coded as String.
Keep looping through those steps until you've got it working.
I did a really through writeup of using Bluetooth, D-Bus, BlueZ on a Raspberry Pi at raspberrypi-bluetooth.com

Related

Scala.js ReferenceError in sbt shell but not in browser

I'm new to ScalaJS, so I'm a little perplexed with this issue.
I'm trying to write a very simple facade for the peer.js library. I have this:
#js.native
#JSGlobal
class Peer() extends js.Object {
def this(id: String = ???, options: js.Object = ???) = this()
def connect(id: String, options: js.Object = ???): DataConnection = js.native
def on(event: String, callback: js.Function): Unit = js.native
def disconnect(): Unit = js.native
def reconnect(): Unit = js.native
def destroy(): Unit = js.native
def id: String = js.native
def connections: js.Object = js.native
def disconnected: Boolean = js.native
def destroyed: Boolean = js.native
}
And here is the simple code I'm trying to run:
object index {
def main(args: Array[String]): Unit = {
val peer = new Peer()
peer.on("open", (id: String) => println(id))
}
}
This small piece of code works perfectly fine in the browser, however when I try to run it in the sbt shell, I get this error:
ReferenceError: Peer is not defined
ReferenceError: Peer is not defined
at $c_Lcom_nicolaswinsten_peerscalajs_index$.main__AT__V (file:///C:/Users/mjwin/IdeaProjects/peer-scalajs/target/scala-2.13/peer-scalajs-fastopt/main.js:840:14)
at $s_Lcom_nicolaswinsten_peerscalajs_index__main__AT__V (file:///C:/Users/mjwin/IdeaProjects/peer-scalajs/target/scala-2.13/peer-scalajs-fastopt/main.js:826:47)
at file:///C:/Users/mjwin/IdeaProjects/peer-scalajs/target/scala-2.13/peer-scalajs-fastopt/main.js:2078:1
at file:///C:/Users/mjwin/IdeaProjects/peer-scalajs/target/scala-2.13/peer-scalajs-fastopt/main.js:2079:4
at Script.runInContext (vm.js:143:18)
at Object.runInContext (vm.js:294:6)
at processJavaScript (C:\Users\mjwin\IdeaProjects\peer-scalajs\node_modules\jsdom\lib\jsdom\living\nodes\HTMLScriptElement-impl.js:241:10)
at HTMLScriptElementImpl._innerEval (C:\Users\mjwin\IdeaProjects\peer-scalajs\node_modules\jsdom\lib\jsdom\living\nodes\HTMLScriptElement-impl.js:176:5)
at onLoadExternalScript (C:\Users\mjwin\IdeaProjects\peer-scalajs\node_modules\jsdom\lib\jsdom\living\nodes\HTMLScriptElement-impl.js:98:12)
at onLoadWrapped (C:\Users\mjwin\IdeaProjects\peer-scalajs\node_modules\jsdom\lib\jsdom\browser\resources\per-document-resource-loader.js:53:33)
[error] org.scalajs.jsenv.ExternalJSRun$NonZeroExitException: exited with code 1
[error] at org.scalajs.jsenv.ExternalJSRun$$anon$1.run(ExternalJSRun.scala:186)
[error] stack trace is suppressed; run 'last Compile / run' for the full output
[error] (Compile / run) org.scalajs.jsenv.ExternalJSRun$NonZeroExitException: exited with code 1
I'm sure it's something really simple, but I'm stumped. Any guesses?
Thank you

How to test methods in Saga that has internal state

given this saga:
#Saga
#Getter
#Slf4j
public class TasksForStateSaga {
#Autowired
transient CommandGateway commandGateway;
#Autowired
transient EventBus eventBus;
#Autowired
transient TaskService taskService;
Map<String, TaskStatus> tasks = new HashMap<>();
ApplicationState applicationState;
#StartSaga
#SagaEventHandler(associationProperty = "id")
public void on(ApplicationStateChangedEvent event) {
applicationState = event.getNewState();
log.info("Planning tasks for application {} in state {}", event.getId(), applicationState);
taskService.getTasksByState(applicationState).stream()
.map(task -> ScheduleTaskCommand.builder()
.applicationId(event.getId())
.taskId(IdentifierFactory.getInstance().generateIdentifier())
.targetState(applicationState)
.taskName(task.getTaskName())
.build())
.peek(command -> tasks.put(command.getTaskId(), SCHEDULED))
.forEach(command -> commandGateway.send(command));
}
#SagaEventHandler(associationProperty = "applicationId")
public void on(TaskFinishedEvent event) {
tasks.replace(event.getTaskId(), FINISHED);
long notFinished = getUnfinishedCount();
log.info("Task {} has just finished, ready {} of {}", event.getTaskName(), tasks.size() - notFinished, tasks.size());
if (notFinished == 0) {
log.info("All tasks for application {}.{} finished, ending this saga", event.getApplicationId(), applicationState);
eventBus.publish(GenericEventMessage.asEventMessage(
TaskForStateDoneEvent.builder()
.applicationId(event.getApplicationId())
.state(applicationState)
.build()
));
SagaLifecycle.end();
}
}
private long getUnfinishedCount() {
return tasks.values().stream()
.filter(state -> !FINISHED.equals(state))
.count();
}
}
And have this test (Spock) testing the first method:
class TasksForStateSagaTest extends Specification {
SagaTestFixture sagaFixture
def setup() {
sagaFixture = new SagaTestFixture<>(TasksForStateSaga)
}
def 'should schedule task for the application state'() {
given:
def applicationId = '1'
def taskService = Mock(TaskService)
def tasks = [
ApplicationStateAwareTaskDefinition.builder().taskName('task1').build(),
ApplicationStateAwareTaskDefinition.builder().taskName('task2').build(),
]
sagaFixture.registerResource(taskService)
sagaFixture.givenAggregate(applicationId)
when:
sagaFixture
.whenPublishingA(new ApplicationStateChangedEvent(id: applicationId, newState: ApplicationState.NEW))
.expectActiveSagas(1)
.expectDispatchedCommandsMatching(payloadsMatching(
exactSequenceOf(
equalTo(new ScheduleTaskCommand(applicationId: applicationId, targetState: ApplicationState.NEW, taskName: 'task1'),
new IgnoreField(ScheduleTaskCommand, 'taskId')),
equalTo(new ScheduleTaskCommand(applicationId: applicationId, targetState: ApplicationState.NEW, taskName: 'task2'),
new IgnoreField(ScheduleTaskCommand, 'taskId')),
andNoMore()
)
))
then:
1 * taskService.getTasksByState(ApplicationState.NEW) >> tasks
}
}
But I actually have no clue how to test the second method, which uses the Saga's internal state.
Could anyone advice me how to set the internal saga's state via SagaTestFixture?
Or even more, is this the good way how to implement such saga or I have there some conceptual issues preventing me to test the end saga method easily?
the #StartSaga method sets the internal state - generates taskId and sets it into map
the #EndSaga method reads the map and checks if all tasks are done prior it sends TaskForStateDoneEvent event
thanks!
I have achieved this by defining callback for the saga fixture. This callbacks catches all the commands of the desired type and stores the ids temporally
def 'saga should end when all scheduled tasks finished'() {
given:
def applicationId = '1'
def taskService = Mock(TaskService)
def tasks = [
ApplicationStateAwareTaskDefinition.builder().taskName('task1').build(),
ApplicationStateAwareTaskDefinition.builder().taskName('task2').build(),
]
def scheduleTaskCommands = []
sagaFixture.registerResource(taskService)
sagaFixture.setCallbackBehavior(new CallbackBehavior() {
#Override
Object handle(Object commandPayload, MetaData commandMetaData) {
if (commandPayload instanceof ScheduleTaskCommand) {
// catch the issued commands to get the new task ids
scheduleTaskCommands << commandPayload
}
commandPayload
}
})
when:
sagaFixture
.givenAggregate(applicationId)
.published(new ApplicationStateChangedEvent(
applicationId: applicationId,
newState: ApplicationState.NEW
))
// publish event to finish first task (first from two)
sagaFixture.givenAPublished(new TaskFinishedEvent(
taskName: 'someTaskName',
applicationId: applicationId,
taskId: scheduleTaskCommands[0].taskId,
taskResult: new TaskResult(
status: TaskResultStatus.SUCCEED,
message: 'ok'
)))
// verify the behaviour after second task finished
sagaFixture
.whenPublishingA(new TaskFinishedEvent(
taskName: 'someTaskName',
applicationId: applicationId,
taskId: scheduleTaskCommands[1].taskId,
taskResult: new TaskResult(
status: TaskResultStatus.SUCCEED,
message: 'ok'
)))
.expectActiveSagas(0)
.expectPublishedEvents(new TasksForStateFinishedEvent(
applicationId: applicationId,
state: ApplicationState.NEW
))
then:
1 * taskService.getTasksByState(ApplicationState.NEW) >> tasks
}

W/FirebaseMessaging: Missing Default Notification Channel metadata in AndroidManifest. Default value will be used

Our app now has targetSdkVersion 26 (Android 8) and the app uses FCM push notifications. When app is in the foreground state the push notification is working well. The issue appears while clicking the notification when the app is not in the foreground state. I just added the metadata in manifest but still got same error.
AndroidManifest.xml
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel"
android:value="#string/default_notification_channel_id"/>
MyFirebaseMessagingService.java
/**
* Create and show a simple notification containing the received FCM message.
*/
private void sendNotification(NotificationModel notificationModel)
{
Intent intent;
if (notificationModel.getAppLink() != null)
{
intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(notificationModel.getAppLink()));
} else
{
intent = new Intent(this, NoticeActivity.class);
}
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
if (notificationModel.getAppLink() != null)
{
notificationBuilder.setSmallIcon(R.mipmap.ic_launcher);
} else
{
notificationBuilder.setSmallIcon(R.mipmap.ic_launcher);
}
notificationBuilder.setContentTitle(notificationModel.getTitle())
.setContentText(notificationModel.getMessage())
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O)
{
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "NOTIFICATION_CHANNEL_NAME", importance);
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.enableVibration(true);
notificationChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
assert notificationManager != null;
notificationBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
notificationManager.createNotificationChannel(notificationChannel);
}
assert notificationManager != null;
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
I know this is late but I hope this helps you or at least someone else.
It has been answered before here.
Basically, you have the meta-data name wrong.
Instead of
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="#string/notification_channel_id" />
it should be
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="#string/default_notification_channel_id" />
UPDATE:
Added missing quote
Answer and tips for newer
The meta-data tag:
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="#string/notification_channel_id" />
Is going inside the application tag on AndroidManifest.xml, example:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.domain.appname">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="#mipmap/ic_launcher" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="#string/default_notification_channel_id"/>
And there is a file called "app/src/main/res/values/strings.xml" where you need to add this line inside resources tag :
<string name="default_notification_channel_id">com.domain.appname.urgent</string>
The value "com.domain.appname.urgent" need to be the same as your default created channel.
strings.xml look like:
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_name">MyApp</string>
<string name="title_activity_main">MyApp</string>
<string name="package_name">com.domain.appname</string>
<string name="custom_url_scheme">com.domain.appname</string>
<string name="default_notification_channel_id">com.domain.appname.urgent</string>
</resources>
I had the same problem as you.
You had an incorrect meta name.
Instead of
android:name="com.google.firebase.messaging.default_notification_channel"
you should use
android:name="com.google.firebase.messaging.default_notification_channel_id"
the only different is the "_id"
Ah!,you may be forget to add channel id on your manifest so check your manifest and add meta for channel id
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="#string/default_notification_channel_id"/>

Alfresco JLan SMB/CIFS Custom Authenticator error

I'm creating an custom authenticator to my Alfresco JLan.
Here's my jlanConfig.xml:
<?xml version="1.0" standalone="no"?>
<!-- <!DOCTYPE jlanserver SYSTEM "jlanserver.dtd"> -->
<jlanserver>
<servers>
<SMB/>
<noFTP/>
<noNFS/>
</servers>
<SMB>
<host name="NUAGESERVER" domain="NUAGE">
<broadcast>255.255.255.0</broadcast>
<smbdialects>LanMan,NT</smbdialects>
<comment>Alfresco JLAN Server</comment>
<Win32NetBIOS/>
<Win32Announce interval="5"/>
<!-- To run the server using a non-root account on linux, Mac OS X, Solaris -->
<netBIOSSMB sessionPort="1139" namePort="1137" datagramPort="1138" platforms="linux,macosx,solaris"/>
<tcpipSMB port="1445" platforms="linux,macosx,solaris"/>
<hostAnnounce interval="5"/>
</host>
<sessionDebug flags="Negotiate,Socket,Tree"/>
<netbiosDebug/>
<announceDebug/>
<authenticator>
<class>com.ye.nuage.auth.NuageAuthenticator</class>
<Debug/>
</authenticator>
</SMB>
<FTP>
<port>21</port>
<allowAnonymous/>
<debug flags="File,Search,Error,DataPort,Directory"/>
</FTP>
<NFS>
<enablePortMapper/>
<debug flags="File,FileIO"/>
</NFS>
<debug>
<output>
<class>org.alfresco.jlan.debug.ConsoleDebug</class>
<logFile>jlansrv.log</logFile>
<append/>
</output>
</debug>
<shares>
<diskshare name="JLAN" comment="Test share">
<driver>
<class>org.alfresco.jlan.smb.server.disk.JavaFileDiskDriver</class>
<LocalPath>.</LocalPath>
</driver>
</diskshare>
</shares>
<security>
<JCEProvider>cryptix.jce.provider.CryptixCrypto</JCEProvider>
<authenticator>
<class>com.ye.nuage.auth.NuageAuthenticator</class>
<mode>USER</mode>
</authenticator>
<users>
<user name="jlansrv">
<password>jlan</password>
<comment>System administrator</comment>
<administrator/>
</user>
<user name="normal">
<password>normal</password>
</user>
</users>
</security>
</jlanserver>
My NuageAuthenticator is a copy of CifsAuthenticator, excepts those methods:
Override
public int authenticateUser(ClientInfo client, SrvSession sess, int alg) {
// Check if the user exists in the user list
UserAccount userAcc = null;
try {
userAcc = getNuageUserDetails(client.getUserName());
} catch (YeException e) {
e.printStackTrace();
}
if (userAcc != null) {
// Validate the password
boolean authSts = false;
if (client.getPassword() != null) {
// Validate using the Unicode password
authSts = validateNuagePassword(userAcc, client, sess.getAuthenticationContext(), alg);
} else if (client.hasANSIPassword()) {
// Validate using the ANSI password with the LanMan encryption
authSts = validateNuagePassword(userAcc, client, sess.getAuthenticationContext(), LANMAN);
}
// Return the authentication status
return authSts == true ? AUTH_ALLOW : AUTH_BADPASSWORD;
}
// Check if this is an SMB/CIFS null session logon.
//
// The null session will only be allowed to connect to the IPC$ named
// pipe share.
if (client.isNullSession() && sess instanceof SMBSrvSession)
return AUTH_ALLOW;
// Unknown user
return allowGuest() ? AUTH_GUEST : AUTH_DISALLOW;
}
private UserAccount getNuageUserDetails(String userName) throws YeException {
if (context == null) {
context = new ClassPathXmlApplicationContext("/applicationContext-nuage.xml");
}
userRepository = context.getBean(UserRepository.class);
User u = userRepository.findByUserLogin(userName); // Search the user into my repository
if (u != null) {
UserAccount ua = new UserAccount();
ua.setMD4Password(u.getUserMd4Password().getBytes());
ua.setUserName(userName);
ua.setRealName(u.getUserFirstName() + " " + u.getUserLastName());
return ua;
}
return null;
}
But when I try to login, I receive the following error when I'm calling the validatePassword method.
[T2] Closing session due to exception
java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at com.ye.nuage.auth.NuageAuthenticator.validatePassword(NuageAuthenticator.java:123)
at com.ye.nuage.auth.NuageAuthenticator.authenticateUser(NuageAuthenticator.java:60)
at org.alfresco.jlan.server.auth.CifsAuthenticator.processSessionSetup(CifsAuthenticator.java:572)
at org.alfresco.jlan.smb.server.NTProtocolHandler.procSessionSetup(NTProtocolHandler.java:396)
at org.alfresco.jlan.smb.server.NTProtocolHandler.runProtocol(NTProtocolHandler.java:213)
at org.alfresco.jlan.smb.server.SMBSrvSession.processPacket(SMBSrvSession.java:1439)
at org.alfresco.jlan.smb.server.nio.NIOCIFSThreadRequest.runRequest(NIOCIFSThreadRequest.java:104)
at org.alfresco.jlan.server.thread.ThreadRequestPool$ThreadWorker.run(ThreadRequestPool.java:141)
at java.lang.Thread.run(Thread.java:722)
java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at com.ye.nuage.auth.NuageAuthenticator.validatePassword(NuageAuthenticator.java:123)
at com.ye.nuage.auth.NuageAuthenticator.authenticateUser(NuageAuthenticator.java:60)
at org.alfresco.jlan.server.auth.CifsAuthenticator.processSessionSetup(CifsAuthenticator.java:572)
at org.alfresco.jlan.smb.server.NTProtocolHandler.procSessionSetup(NTProtocolHandler.java:396)
at org.alfresco.jlan.smb.server.NTProtocolHandler.runProtocol(NTProtocolHandler.java:213)
at org.alfresco.jlan.smb.server.SMBSrvSession.processPacket(SMBSrvSession.java:1439)
at org.alfresco.jlan.smb.server.nio.NIOCIFSThreadRequest.runRequest(NIOCIFSThreadRequest.java:104)
at org.alfresco.jlan.server.thread.ThreadRequestPool$ThreadWorker.run(ThreadRequestPool.java:141)
at java.lang.Thread.run(Thread.java:722)
Here's a method snip:
if (user.hasMD4Password() && alg != LANMAN) {
try {
// Generate the encrpyted password
if (alg == NTLM1) {
// Get the MD4 hashed password
byte[] p21 = new byte[21];
System.arraycopy(user.getMD4Password(), 0, p21, 0, user.getMD4Password().length); **//THE ERROR OCCURS HERE!**
// Generate an NTLMv1 encrypted password
`
The error occurs here:
System.arraycopy(user.getMD4Password(), 0, p21, 0, user.getMD4Password().length);
But the question is very simple: Why this error occurs?
The problem is with the MD4 32 bits algorithm. See more at : http://forums.alfresco.com/forum/installation-upgrades-configuration-integration/authentication-ldap-sso/alfresco-jlan-smbcifs

How to generate class with field of type Date from xs:string element with JAXB

I have xsd schema file which I can't change.
Here is an excerpt that makes my problem:
<xs:element name="Event" maxOccurs="unbounded">
<xs:complexType>
<xs:all>
<xs:element name="EventDate" type="xs:string" minOccurs="0">
...
Here is an example of string data I get for EventDate:
2012-05-30T12:30:00 CEST
I'm compiling with xjc and I get Event class with String field.
Is there a way to get Event class with some kind of Date field?
I guess I should write some kind of adapter and that's ok but I don't know how to tell xjc to use it only on EventDate element.
you can use 'XmlAdapter' . It supports this functionality.
I'm compiling with:
xjc -b temp.xml schema.xsd
This is part of temp.xml:
<jaxb:bindings node="//xs:element[#name='EventDate']">
<jaxb:property>
<jaxb:baseType>
<javaType name="java.util.Date"
parseMethod="com.mydomain.adapters.DateAdapter.parseDate"
printMethod="com.mydomain.adapters.DateAdapter.printDate"
/>
</jaxb:baseType>
</jaxb:property>
</jaxb:bindings>
And here is part of com.mydomain.adapters.DateAdapter class:
private static final DateFormatter df = new DateFormatter("yyyy-MM-dd'T'HH:mm:ss z");
public static Date parseDate(String v) {
Date date = null;
try {
date = df.parse(v, Locale.getDefault());
} catch (ParseException e) {
throw new MyException("Could not parse date:" + v);
}
return date;
}
public static String printDate(Date v) {
return df.print(v, Locale.getDefault());
}

Resources