diff --git a/platypush/backend/http/webapp/src/components/panels/Entities/Battery.vue b/platypush/backend/http/webapp/src/components/panels/Entities/Battery.vue
new file mode 100644
index 000000000..5a1968293
--- /dev/null
+++ b/platypush/backend/http/webapp/src/components/panels/Entities/Battery.vue
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
diff --git a/platypush/backend/http/webapp/src/components/panels/Entities/meta.json b/platypush/backend/http/webapp/src/components/panels/Entities/meta.json
index 98f457585..42c32de6d 100644
--- a/platypush/backend/http/webapp/src/components/panels/Entities/meta.json
+++ b/platypush/backend/http/webapp/src/components/panels/Entities/meta.json
@@ -1,9 +1,9 @@
{
- "entity": {
- "name": "Entity",
- "name_plural": "Entities",
+ "battery": {
+ "name": "Battery",
+ "name_plural": "Batteries",
"icon": {
- "class": "fas fa-circle-question"
+ "class": "fas fa-battery-full"
}
},
@@ -15,11 +15,19 @@
}
},
- "switch": {
- "name": "Switch",
- "name_plural": "Switches",
+ "dimmer": {
+ "name": "Dimmer",
+ "name_plural": "Dimmers",
"icon": {
- "class": "fas fa-toggle-on"
+ "class": "fas fa-gauge"
+ }
+ },
+
+ "entity": {
+ "name": "Entity",
+ "name_plural": "Entities",
+ "icon": {
+ "class": "fas fa-circle-question"
}
},
@@ -31,11 +39,11 @@
}
},
- "dimmer": {
- "name": "Dimmer",
- "name_plural": "Dimmers",
+ "switch": {
+ "name": "Switch",
+ "name_plural": "Switches",
"icon": {
- "class": "fas fa-gauge"
+ "class": "fas fa-toggle-on"
}
}
}
diff --git a/platypush/entities/batteries.py b/platypush/entities/batteries.py
new file mode 100644
index 000000000..3eaacc54b
--- /dev/null
+++ b/platypush/entities/batteries.py
@@ -0,0 +1,37 @@
+from sqlalchemy import Column, Integer, ForeignKey
+
+from .devices import entity_types_registry
+from .sensors import NumericSensor
+
+
+if not entity_types_registry.get('Battery'):
+
+ class Battery(NumericSensor):
+ __tablename__ = 'battery'
+
+ def __init__(
+ self,
+ *args,
+ value,
+ unit: str = '%',
+ min: float = 0,
+ max: float = 100,
+ **kwargs
+ ):
+ super().__init__(*args, **kwargs)
+ self.value = float(value)
+ self.unit = unit
+ self.min = min
+ self.max = max
+
+ id = Column(
+ Integer, ForeignKey(NumericSensor.id, ondelete='CASCADE'), primary_key=True
+ )
+
+ __mapper_args__ = {
+ 'polymorphic_identity': __tablename__,
+ }
+
+ entity_types_registry['Battery'] = Battery
+else:
+ Battery = entity_types_registry['Battery']
diff --git a/platypush/entities/sensors.py b/platypush/entities/sensors.py
new file mode 100644
index 000000000..16e3f33cc
--- /dev/null
+++ b/platypush/entities/sensors.py
@@ -0,0 +1,43 @@
+from sqlalchemy import Column, Integer, ForeignKey, Numeric, String
+
+from .devices import Device, entity_types_registry
+
+
+if not entity_types_registry.get('RawSensor'):
+
+ class RawSensor(Device):
+ __tablename__ = 'raw_sensor'
+
+ id = Column(
+ Integer, ForeignKey(Device.id, ondelete='CASCADE'), primary_key=True
+ )
+ value = Column(String)
+
+ __mapper_args__ = {
+ 'polymorphic_identity': __tablename__,
+ }
+
+ entity_types_registry['RawSensor'] = RawSensor
+else:
+ RawSensor = entity_types_registry['RawSensor']
+
+
+if not entity_types_registry.get('NumericSensor'):
+
+ class NumericSensor(Device):
+ __tablename__ = 'numeric_sensor'
+
+ id = Column(
+ Integer, ForeignKey(Device.id, ondelete='CASCADE'), primary_key=True
+ )
+ value = Column(Numeric)
+ min = Column(Numeric)
+ max = Column(Numeric)
+
+ __mapper_args__ = {
+ 'polymorphic_identity': __tablename__,
+ }
+
+ entity_types_registry['NumericSensor'] = NumericSensor
+else:
+ NumericSensor = entity_types_registry['NumericSensor']
diff --git a/platypush/plugins/zwave/_base.py b/platypush/plugins/zwave/_base.py
index 3fe9d637b..9b145ef9f 100644
--- a/platypush/plugins/zwave/_base.py
+++ b/platypush/plugins/zwave/_base.py
@@ -2,13 +2,14 @@ from abc import ABC, abstractmethod
from typing import Any, Dict, Optional, List, Union
from platypush.entities import manages
+from platypush.entities.batteries import Battery
from platypush.entities.dimmers import Dimmer
from platypush.entities.lights import Light
from platypush.entities.switches import Switch
from platypush.plugins import Plugin, action
-@manages(Dimmer, Light, Switch)
+@manages(Battery, Dimmer, Light, Switch)
class ZwaveBasePlugin(Plugin, ABC):
"""
Base class for Z-Wave plugins.
diff --git a/platypush/plugins/zwave/mqtt/__init__.py b/platypush/plugins/zwave/mqtt/__init__.py
index 4d069bc99..b272e0d8c 100644
--- a/platypush/plugins/zwave/mqtt/__init__.py
+++ b/platypush/plugins/zwave/mqtt/__init__.py
@@ -6,6 +6,7 @@ from datetime import datetime
from threading import Timer
from typing import Optional, List, Any, Dict, Union, Iterable, Mapping, Callable
+from platypush.entities.batteries import Battery
from platypush.entities.dimmers import Dimmer
from platypush.entities.switches import Switch
from platypush.message.event.zwave import ZwaveNodeRenamedEvent, ZwaveNodeEvent
@@ -482,6 +483,14 @@ class ZwaveMqttPlugin(MqttPlugin, ZwaveBasePlugin):
value, 'switch_multilevel', 'switch_toggle_multilevel'
) and not value.get('is_read_only')
+ @classmethod
+ def _is_battery(cls, value: Mapping):
+ return (
+ cls._matches_classes(value, 'battery')
+ and value.get('is_read_only')
+ and not value['id'].endswith('-isLow')
+ )
+
def _to_entity_args(self, value: Mapping) -> dict:
if value['id'].endswith('-targetValue'):
current_value_id = '-'.join(value['id'].split('-')[:-1] + ['currentValue'])
@@ -525,6 +534,12 @@ class ZwaveMqttPlugin(MqttPlugin, ZwaveBasePlugin):
entity_args['value'] = value['data']
entity_args['min'] = value['min']
entity_args['max'] = value['max']
+ elif self._is_battery(value):
+ entity_type = Battery
+ entity_args['value'] = value['data']
+ entity_args['min'] = value['min']
+ entity_args['max'] = value['max']
+ entity_args['unit'] = value.get('units', '%')
elif self._is_switch(value):
entity_type = Switch
entity_args['state'] = value['data']