From df4957d618ad3bfde4d43594988fe2c4af703e02 Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Thu, 2 May 2024 17:21:22 +0200 Subject: [PATCH] Progress on new server --- README.md | 2 +- example.conf.yaml | 5 +++ src/example.py | 39 ++++++++++++++++++++-- src/mash/entities/entity.py | 32 ++++++++++++++++++ src/mash/entities/group.py | 66 +++++++++++++++++++++++++++++++++++++ src/mash/home.py | 7 ++++ 6 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 src/mash/entities/entity.py create mode 100644 src/mash/entities/group.py create mode 100644 src/mash/home.py diff --git a/README.md b/README.md index 34540e7..fe304df 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Max's Smart Home - MaSH +# Max' Smart Home - MaSH Should be a very simple **server** implementation of what is required in Max's smart home. Trying not to overcomplicate things and thereby ruin motivation to work on this. diff --git a/example.conf.yaml b/example.conf.yaml index c5f5413..4ea3dbb 100644 --- a/example.conf.yaml +++ b/example.conf.yaml @@ -9,6 +9,11 @@ features: home: latitude: 52.51860 longitude: 13.37565 + + beds: + - id: max-bed + name: Bettwaage + room: *max rooms: - id : &hw hallway diff --git a/src/example.py b/src/example.py index 3eca4eb..c4849c2 100644 --- a/src/example.py +++ b/src/example.py @@ -33,19 +33,54 @@ class Automation: def trigger(self): def decorator(func): - return func + return func return decorator class PeopleCountEngineV1(Automation): @Automation.trigger( - devices=["matrixclock"], rule=lambda h: h.device("matrixclock").contrast == 6 + devices=["matrixclock"], + rule=lambda h: h.device("matrixclock").contrast == 6 ) def turn_light_on_sometimes(self, home: Home): home.room("max").lights().on = True + + + + + + + @Automation.trigger( + people=["max"], + rule=lambda h: h.person("max").athome() + ) + def turn_light_on_sometimes(self, home: Home): + home.room("max").lights().on = h.person("max").athome() + + + + + +@Automation.state(h.room("Max").lights()) +def max_room_light(): + if max.ishome(): + return "off" + + scene = "Daylight scene" + + if nighttime: + scene = "nighttime" + + if max.working: + scene.dim(0.5) + + return scene + + + from mash.mash import MaSH mash = MaSH() diff --git a/src/mash/entities/entity.py b/src/mash/entities/entity.py new file mode 100644 index 0000000..317980b --- /dev/null +++ b/src/mash/entities/entity.py @@ -0,0 +1,32 @@ +class Entity: + def __init__( + self, *, id: str, name: str, room: str, device_type: str, groups: list[str] = [] + ) -> None: + self._id = id + self._name = name + self._room = room + self._device_type = device_type + self._groups = set(groups) + + @property + def id(self) -> str: + return self._id + + @property + def name(self) -> str: + return self._name + + @property + def room(self) -> str: + return self._room + + @property + def device_type(self) -> str: + return self._device_type + + @property + def groups(self) -> set[str]: + return self._groups + + def __str__(self) -> str: + return f"{self.name} [{self.id}, type {self.device_type}, room {self.room}, in {len(self.groups)} groups]" diff --git a/src/mash/entities/group.py b/src/mash/entities/group.py new file mode 100644 index 0000000..04b818c --- /dev/null +++ b/src/mash/entities/group.py @@ -0,0 +1,66 @@ +from mash.entities.entity import Entity +from fnmatch import fnmatch + + +class Group: + def __init__(self, *, entities: list[Entity] = []) -> None: + self.entities: list[Entity] = entities + + def __len__(self): + return len(self.entities) + + def __getitem__(self, id: str) -> "Group": + if type(id) is int: + raise "Numerical index not supported." + + return self.id(id) + + def __get_entities_with_specific_property__( + entities: list[Entity], + property_getter: callable[[Entity], str], + target_pattern: str, + ) -> list[Entity]: + """Returns all entities for which the property getter matches the desired pattern. + + Args: + entities (list[Entity]): List of entities. + property_getter (callable[[Entity], str]): Takes one entity and returns the value of the filtered property. + target_pattern (str): Pattern that is matched against. + + Returns: + list[Entity]: A new group of entities or an empty group, if no entity property matches the target pattern. + """ + return Group( + entities=[ + e for e in entities if fnmatch(property_getter(e), target_pattern) + ] + ) + + def device_type(self, device_type: str) -> "Group": + return Group.__get_entities_with_specific_property__( + self.entities, lambda e: e.device_type, device_type + ) + + def id(self, id: str) -> "Group": + return Group.__get_entities_with_specific_property__( + self.entities, lambda e: e.id, id + ) + + def room(self, room: str) -> "Group": + return Group.__get_entities_with_specific_property__( + self.entities, lambda e: e.room, room + ) + + def name(self, name: str) -> "Group": + return Group.__get_entities_with_specific_property__( + self.entities, lambda e: e.name, name + ) + + def lights(self) -> "Group": + return self.device_type("light") + + def beds(self) -> "Group": + return self.device_type("bed") + + def max(self) -> "Group": + return self.room("max") diff --git a/src/mash/home.py b/src/mash/home.py new file mode 100644 index 0000000..a3778b5 --- /dev/null +++ b/src/mash/home.py @@ -0,0 +1,7 @@ +from mash.entities.entity import Entity +from mash.entities.group import Group + + +class Home(Group): + def __init__(self, *, entities: list[Entity] = []) -> None: + super().__init__(entities=entities)