diff --git a/mash.yaml b/mash.yaml new file mode 100644 index 0000000..b8a389c --- /dev/null +++ b/mash.yaml @@ -0,0 +1,11 @@ +# An example config file for MaSH + +services: + philips-hue: + ip: 192.168.178.42 + +rooms: + - name: Office + - name: Hallway + adjacent: + - Office diff --git a/requirements.txt b/requirements.txt index 27dcd81..01ecf54 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,12 @@ smbus2 vl53l1x +# To parse the config file +pyyaml + +# To hot reload the config file +watchdog + # For Philips Hue Counter phue diff --git a/src/models/devices.py b/src/models/devices.py new file mode 100644 index 0000000..041af03 --- /dev/null +++ b/src/models/devices.py @@ -0,0 +1,39 @@ +import abc + +from models.light import LightColor + +class GenericDevice: + """A generic device.""" + def __init__(self, name: str): + self.name = name + + +class SwitchDevice(GenericDevice): + """Abstract device that can be turned on and off.""" + + def toggle(self): + self.is_on = not self.is_on + + @property + @abc.abstractmethod + def is_on(self) -> bool: + raise NotImplementedError + + @is_on.setter + @abc.abstractmethod + def is_on(self, is_on: bool): + raise NotImplementedError + + +class LightDevice(SwitchDevice): + """Abstract device that can be turned on and off and has a color.""" + + @property + @abc.abstractmethod + def color(self) -> LightColor: + raise NotImplementedError + + @color.setter + @abc.abstractmethod + def color(self, color: LightColor): + raise NotImplementedError diff --git a/src/models/groups.py b/src/models/groups.py new file mode 100644 index 0000000..e0f0e2d --- /dev/null +++ b/src/models/groups.py @@ -0,0 +1,71 @@ +from models.helper import filter_devices +from models.devices import GenericDevice, LightDevice, SwitchDevice +from models.light import LightColor, LightScene + + +class DeviceGroup(LightDevice): + """A group of devices that allows group operations.""" + + def __init__(self, devices: list[GenericDevice]): + self._devices = devices + + @property + def devices(self) -> list[GenericDevice]: + return self._devices + + @property + def switches(self) -> list[SwitchDevice]: + return filter_devices(self.devices, SwitchDevice) + + @property + def lights(self) -> list[LightDevice]: + return filter_devices(self.devices, LightDevice) + + @property + def is_on(self) -> bool: + """Returns true if any device is on.""" + return any(device.is_on for device in self.switches) + + @is_on.setter + def is_on(self, is_on: bool): + """Sets all devices to the same state.""" + for device in self.switches: + device.is_on = is_on + + @property + def color(self) -> LightColor: + """Returns the color of the first light in the group.""" + return self.lights[0].color + + @color.setter + def color(self, color: LightColor): + """Sets all lights in the group to the same color.""" + for device in self.lights: + device.color = color + + @property + def scene(self) -> LightScene: + """Returns the current scene of the group.""" + return LightScene(self.name, {device: device.color for device in self.lights}) + + +class Room(DeviceGroup): + """A group of devices that has additional properties a can be seen as a room.""" + + def __init__(self, name: str, devices: list[GenericDevice], people_count: int = 0): + """ + Args: + name (str): Name of the room. + devices (list[GenericDevice]): Devices in this room. + people_count (int, optional): Number of people in this room. Defaults to 0. + """ + super().__init__(devices) + self.name = name + self._people_count = people_count + + @property + def people_count(self) -> int: + return self._people_count + + def __str__(self): + return self.name diff --git a/src/models/helper.py b/src/models/helper.py new file mode 100644 index 0000000..52fe107 --- /dev/null +++ b/src/models/helper.py @@ -0,0 +1,10 @@ +from typing import TypeVar +from models.devices import GenericDevice, LightDevice, SwitchDevice + + +DEVICE_TYPE = TypeVar("DEVICE_TYPE", type(GenericDevice), type(SwitchDevice), type(LightDevice)) + + +def filter_devices(devices: list[GenericDevice], type: DEVICE_TYPE) -> list[DEVICE_TYPE]: + """Filters out devices that are not of a specific type.""" + return [device for device in devices if isinstance(device, DEVICE_TYPE)] diff --git a/src/models/light.py b/src/models/light.py new file mode 100644 index 0000000..0dd0e2e --- /dev/null +++ b/src/models/light.py @@ -0,0 +1,24 @@ +from models.devices import LightDevice + + +class LightColor: + """The color of a light source.""" + + def __init__(self, red: int, green: int, blue: int, is_on: bool = True): + self.red = red + self.green = green + self.blue = blue + self.is_on = is_on + + +class LightScene: + """A scene describing a state of a collection of light sources.""" + + def __init__(self, name: str, device_colors: dict[LightDevice, LightColor]): + self.name = name + self.device_colors = device_colors + + def set_scene(self): + """Sets the scene on all devices.""" + for device, color in self.device_colors.items(): + device.color = color