diff options
-rwxr-xr-x | weahome.py | 85 |
1 files changed, 61 insertions, 24 deletions
@@ -5,16 +5,17 @@ from enum import Enum from argparse import ArgumentParser from functools import partial import dbus -from gi.repository import GObject +from gi.repository import GObject, GLib from dbus.mainloop.glib import DBusGMainLoop -BLUEZ_SERVICE_NAME = 'org.bluez' -DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager' -DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties' +BLUEZ_SERVICE_NAME = 'org.bluez' +DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager' +DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties' -BLUEZ_DEVICE_IFACE = 'org.bluez.Device1' -GATT_SERVICE_IFACE = 'org.bluez.GattService1' -GATT_CHAR_IFACE = 'org.bluez.GattCharacteristic1' +BLUEZ_ADAPTER_IFACE = 'org.bluez.Adapter1' +BLUEZ_DEVICE_IFACE = 'org.bluez.Device1' +GATT_SERVICE_IFACE = 'org.bluez.GattService1' +GATT_CHAR_IFACE = 'org.bluez.GattCharacteristic1' WEAHOME_SERVICE = '74e7fe00-c6a4-11e2-b7a9-0002a5d5c51b' DEVINFO_CHAR = '74e78e02-c6a4-11e2-b7a9-0002a5d5c51b' @@ -41,6 +42,12 @@ def _get_bluez_objects(): obj_manager = bus.get_object(BLUEZ_SERVICE_NAME, '/') return obj_manager.GetManagedObjects(dbus_interface=DBUS_OM_IFACE) +def _get_bluez_iface_objs(objects, iface): + for path, interfaces in objects.items(): + if iface in interfaces.keys(): + obj = bus.get_object(BLUEZ_SERVICE_NAME, path) + yield obj, path, interfaces + class Event: __slots__ = ['_observers'] @@ -229,7 +236,10 @@ class WeahomeDevice: return value / 10.0 def _parse_weather(self, value): - return WeahomeWeather(value) + try: + return WeahomeWeather(value) + except ValueError: + return WeahomeWeather.NO_DATA def _process_sensor_data_1(self, offset, data): s0_temperature, s1_temperature, s2_temperature, s3_temperature, \ @@ -452,6 +462,10 @@ class WeahomeDevice: else: return "Unknown" + @property + def address(self): + return str(self._get_device_prop("Address")) + def connect(self): self._device.Connect(dbus_interface=BLUEZ_DEVICE_IFACE) @@ -464,19 +478,16 @@ class WeahomeDevice: settings.datetime = datetime.datetime.now() self._write_settings(settings) -def _is_weahome_device(path): - device = bus.get_object(BLUEZ_SERVICE_NAME, path) - uuids = device.Get(BLUEZ_DEVICE_IFACE, 'UUIDs', dbus_interface=DBUS_PROP_IFACE) +def _is_weahome_device(dbus_obj): + uuids = dbus_obj.Get(BLUEZ_DEVICE_IFACE, 'UUIDs', dbus_interface=DBUS_PROP_IFACE) return WEAHOME_SERVICE in uuids def scan_for_weahome_devices(): objects = _get_bluez_objects() devices = [] - for path, interfaces in objects.items(): - if BLUEZ_DEVICE_IFACE not in interfaces.keys(): - continue - if not _is_weahome_device(path): + for obj, path, _ in _get_bluez_iface_objs(objects, BLUEZ_DEVICE_IFACE): + if not _is_weahome_device(obj): continue device = WeahomeDevice(path) @@ -484,6 +495,23 @@ def scan_for_weahome_devices(): return devices +def connect_to_weahome_device(address): + address = str(address).upper() + objects = _get_bluez_objects() + + # First, find preexisting devices with the same address and return it + for obj, path, _ in _get_bluez_iface_objs(objects, BLUEZ_DEVICE_IFACE): + device_addr = obj.Get(BLUEZ_DEVICE_IFACE, 'Address', dbus_interface=DBUS_PROP_IFACE) + if device_addr == address: + device = WeahomeDevice(path) + device.connect() + return device + + # TODO Adapter1 ConnectDevice is still experimental... + + # No device, no adapters... + raise KeyError("Device with address '{0}' not found".format(address)) + def print_sensor(device, sensor): print("{0} Sensor {1}:".format(device.name, sensor.name)) if sensor.has_temperature: @@ -507,12 +535,6 @@ def print_sensor(device, sensor): if sensor.has_weather: print(" weather = {0}".format(sensor.weather)) -def disconnect_all_devices(): - print("Disconnecting devices") - for device in devices: - if device.connected: - device.disconnect() - # Example client program: def _connected_changed_cb(sender): @@ -535,21 +557,36 @@ def _settings_changed_cb(sender): settings = sender.settings log.info("{0} Settings: status={1} datetime={2} moon_phase={3} position={4},{5} sun={6},{7} moon={8},{9}".format(sender.name, settings.status, settings.datetime, settings.moon_phase, settings.latitude, settings.longitude, settings.sun_rise, settings.sun_set, settings.moon_rise, settings.moon_set)) +def _disconnect_all_devices(): + print("Disconnecting devices") + for device in devices: + if device.connected: + device.disconnect() + if __name__ == '__main__': parser = ArgumentParser(description="Weather@home") + parser.add_argument("--device", "-d", action='append', help="Connect to specific address") parser.add_argument("--sync", "-s", action='store_true', help="Sync time") parser.add_argument("--verbose", "-v", action='store_true', help="Verbose log") args = parser.parse_args() - mainloop = GObject.MainLoop() + mainloop = GLib.MainLoop() if args.verbose: logging.basicConfig(level=logging.DEBUG) - devices = scan_for_weahome_devices() + devices = [] + + if args.device: + for address in args.device: + device = connect_to_weahome_device(address) + devices.append(device) + else: + devices = scan_for_weahome_devices() + log.info("Found {0} weather@home devices: {1}".format(len(devices), [dev.name for dev in devices])) - atexit.register(disconnect_all_devices) + atexit.register(_disconnect_all_devices) for device in devices: device.connected_changed.observe(_connected_changed_cb) |