from typing import Dict, Iterable, List from typing_extensions import override from sqlalchemy import ( Boolean, Column, ForeignKey, Integer, JSON, String, ) from platypush.common.db import Base from platypush.plugins.bluetooth.model import ( MajorDeviceClass, MajorServiceClass, MinorDeviceClass, ServiceClass, ) from ..devices import Device from ._service import BluetoothService if 'bluetooth_device' not in Base.metadata: class BluetoothDevice(Device): """ Entity that represents a Bluetooth device. """ __tablename__ = 'bluetooth_device' id = Column( Integer, ForeignKey(Device.id, ondelete='CASCADE'), primary_key=True ) address = Column(String, nullable=False) """ Device address. """ connected = Column(Boolean, default=False) """ Whether the device is connected. """ supports_ble = Column(Boolean, default=False) """ Whether the device supports the Bluetooth Low-Energy specification. """ supports_legacy = Column(Boolean, default=False) """ Whether the device supports the legacy (non-BLE) specification. """ rssi = Column(Integer, default=None) """ Received Signal Strength Indicator. """ tx_power = Column(Integer, default=None) """ Reported transmission power. """ _major_service_classes = Column("major_service_classes", JSON, default=None) """ The reported major service classes, as a list of strings. """ _major_device_class = Column("major_device_class", String, default=None) """ The reported major device class. """ _minor_device_classes = Column("minor_device_classes", JSON, default=None) """ The reported minor device classes, as a list of strings. """ manufacturer = Column(String, default=None) """ Device manufacturer, as a string. """ model = Column(String, default=None) """ Device model, as a string. """ model_id = Column(String, default=None) """ Device model ID. """ __mapper_args__ = { 'polymorphic_identity': __tablename__, } @property def major_device_class(self) -> MajorDeviceClass: ret = MajorDeviceClass.UNKNOWN if self._major_device_class: matches = [ cls for cls in MajorDeviceClass if cls.value.name == self._major_device_class ] if matches: ret = matches[0] return ret @major_device_class.setter def major_device_class(self, value: MajorDeviceClass): self._major_device_class = value.value.name @property def minor_device_classes(self) -> List[MinorDeviceClass]: ret = [] for dev_cls in self._minor_device_classes or []: matches = [cls for cls in MinorDeviceClass if cls.value.name == dev_cls] if matches: ret.append(matches[0]) return ret @minor_device_classes.setter def minor_device_classes(self, value: Iterable[MinorDeviceClass]): self._minor_device_classes = [cls.value.name for cls in (value or [])] @property def major_service_classes(self) -> List[MajorServiceClass]: ret = [] for dev_cls in self._major_service_classes or []: matches = [ cls for cls in MajorServiceClass if cls.value.name == dev_cls ] if matches: ret.append(matches[0]) return ret @major_service_classes.setter def major_service_classes(self, value: Iterable[MajorServiceClass]): self._major_service_classes = [cls.value.name for cls in (value or [])] @property def services(self) -> List[BluetoothService]: """ :return: List of :class:`platypush.plugins.bluetooth.model.BluetoothService` mapping all the services exposed by the device. """ return [ child for child in self.children if isinstance(child, BluetoothService) ] @property def known_services(self) -> Dict[ServiceClass, "BluetoothService"]: """ Known services exposed by the device, indexed by :class:`platypush.plugins.bluetooth.model.ServiceClass` enum value. """ return { child.service_class: child for child in self.children if isinstance(child, BluetoothService) and child.service_class != ServiceClass.UNKNOWN } @override def to_dict(self): """ Overwrites ``to_dict`` to transform private column names into their public representation, and also include the exposed services and child entities. """ return { **{k.lstrip('_'): v for k, v in super().to_dict().items()}, 'children': [child.to_dict() for child in self.children], }