From 8d4aa310f4822037ded922fdbad2ebf912cca8af Mon Sep 17 00:00:00 2001
From: Fabio Manganiello <fabio@manganiello.tech>
Date: Sun, 2 Apr 2023 02:02:08 +0200
Subject: [PATCH] Support for values passed in dict format to `ThreeAxisSensor`

---
 platypush/entities/three_axis.py | 35 +++++++++++++++++++++++---------
 1 file changed, 25 insertions(+), 10 deletions(-)

diff --git a/platypush/entities/three_axis.py b/platypush/entities/three_axis.py
index 2758dfa80..0789539eb 100644
--- a/platypush/entities/three_axis.py
+++ b/platypush/entities/three_axis.py
@@ -1,7 +1,8 @@
+from typing import Iterable, Mapping, Optional, Union
 from sqlalchemy import Column, Integer, ForeignKey
-from sqlalchemy.orm import reconstructor, validates
 
 from platypush.common.db import Base
+from platypush.common.sensors import Numeric
 
 from .sensors import RawSensor
 
@@ -23,16 +24,30 @@ if 'three_axis_sensor' not in Base.metadata:
             'polymorphic_identity': __tablename__,
         }
 
-        @validates('_value')
-        def check_value(self, _, value):
+        @RawSensor.value.setter
+        def value(
+            self, value: Optional[Union[Iterable[Numeric], Mapping[str, Numeric]]]
+        ):
+            """
+            Validates and normalizes the given value to a list of 3 numeric
+            values.
+            """
+            if value is None:
+                return
+
+            if isinstance(value, dict):
+                assert set(value.keys()) == {
+                    'x',
+                    'y',
+                    'z',
+                }, f'Invalid keys for entity of type {self.__class__}: "{value}"'
+
+                value = [value[k] for k in ['x', 'y', 'z']]  # type: ignore
+
             assert (
-                isinstance('_value', (list, tuple))
-                and len(value) == 3
+                isinstance(value, (list, tuple))
+                and len(value) == 3  # type: ignore
                 and all(isinstance(v, (int, float)) for v in value)
             ), f'Invalid 3-axis value: {value}'
 
-            return list(value)
-
-        @reconstructor
-        def post_init(self):
-            self.is_json = True
+            super(ThreeAxisSensor, type(self)).value.fset(self, value)  # type: ignore