platypush/platypush/entities/bluetooth/_service.py

134 lines
3.8 KiB
Python

from typing import Union
from typing_extensions import override
from uuid import UUID
from sqlalchemy import (
Boolean,
Column,
ForeignKey,
Integer,
String,
)
from platypush.common.db import Base
from platypush.entities import Entity
from platypush.plugins.bluetooth.model import (
Protocol,
RawServiceClass,
ServiceClass,
)
if 'bluetooth_service' not in Base.metadata:
class BluetoothService(Entity):
"""
Entity that represents a Bluetooth service.
"""
__tablename__ = 'bluetooth_service'
id = Column(
Integer, ForeignKey(Entity.id, ondelete='CASCADE'), primary_key=True
)
_uuid = Column('uuid', String, nullable=False)
"""
The service class UUID. It can be either a 16-bit or a 128-bit UUID.
"""
_protocol = Column('protocol', String, default=None)
""" The protocol used by the service. """
port = Column(Integer, default=None)
""" The port used by the service. """
version = Column(Integer, default=None)
""" The version of the service profile. """
is_ble = Column(Boolean, default=False)
""" Whether the service is a BLE service. """
__mapper_args__ = {
'polymorphic_identity': __tablename__,
}
@staticmethod
def to_uuid(value: Union[str, RawServiceClass]) -> RawServiceClass:
"""
Convert a raw UUID string to a service class UUID.
"""
# If it's already a UUID or an int, just return it.
if isinstance(value, (UUID, int)):
return value
try:
# If it's formatted like a 128-bit UUID, convert it to a UUID
# object.
return UUID(value)
except ValueError:
# Hex string case
return int(value, 16)
@property
def uuid(self) -> RawServiceClass:
"""
Getter for the service class UUID.
"""
return self.to_uuid(self._uuid)
@uuid.setter
def uuid(self, value: Union[RawServiceClass, str]):
"""
Setter for the service class UUID.
"""
uuid: Union[RawServiceClass, str] = self.to_uuid(value)
if isinstance(uuid, int):
# Hex-encoded 16-bit UUID case
uuid = f'{uuid:04X}'
self._uuid = str(uuid)
@property
def protocol(self) -> Protocol:
"""
Getter for the protocol used by the service.
"""
try:
return Protocol(self._protocol)
except ValueError:
return Protocol.UNKNOWN
@protocol.setter
def protocol(self, value: Union[str, Protocol]):
"""
Setter for the protocol used by the service.
"""
protocol = Protocol.UNKNOWN
if isinstance(value, Protocol):
protocol = value
else:
try:
protocol = Protocol(value)
except ValueError:
pass
self._protocol = protocol.value
@property
def service_class(self) -> ServiceClass:
"""
The :class:`platypush.plugins.bluetooth.model.ServiceClass` enum
value.
"""
try:
return ServiceClass.get(self.uuid)
except (TypeError, ValueError):
return ServiceClass.UNKNOWN
@override
def to_dict(self) -> dict:
return {
**{k.lstrip('_'): v for k, v in super().to_dict().items()},
# Human-readable service class name
'service_class': str(self.service_class),
}