Skip to content

Commit 122650e

Browse files
retoofabaff
andauthored
Improve shade integration, add dimmer support (#6)
* Improve shade integration, add dimmer support * Update comments Co-authored-by: Reto Schuettel <[email protected]> Co-authored-by: Fabian Affolter <[email protected]> Co-authored-by: Fabian Affolter <[email protected]>
1 parent d16b08c commit 122650e

File tree

7 files changed

+454
-112
lines changed

7 files changed

+454
-112
lines changed

CHANGELOG.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,24 @@
11
Changelog
22
=========
33

4+
0.4.0 (2021-04-18)
5+
------------------
6+
7+
- Support for dimmers
8+
- Refactored shades
9+
10+
- With version 0.3.0
11+
::
12+
13+
dingz.shade_down(shade_id)
14+
15+
- Starting from 0.4.0:
16+
::
17+
18+
shade = dingz.shades.get(shade_id)
19+
shade.shade_down()
20+
21+
422
0.3.0 (2020-11-08)
523
------------------
624

README.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,30 @@ Every unit has its own web interface: `http://IP_ADDRESS <http://IP_ADDRESS>`_ .
5959

6060
See `example.py` for detail about module.
6161

62+
63+
How to operate shades / dimmers
64+
-------------------------------
65+
66+
.. code:: python
67+
68+
d = Dingz("ip_address_or_host")
69+
# Fetch config, this has to be done once to fetch all details about the shades/dimmers
70+
await d.get_devices_config()
71+
72+
# Fetch the current state of the lights/vers
73+
await d.get_state()
74+
75+
# Get details about shade
76+
shade_0 = d.shades.get(0)
77+
print("Blinds: %s Lamella: %s" % (shade_0.current_blind_level(), shade_0.current_lamella_level()))
78+
79+
# Operate shade
80+
shade_0.shade_down()
81+
82+
# Turn on light
83+
d.dimmers.get(2).turn_on(brightness_pct=70)
84+
85+
6286
CLI usage
6387
---------
6488

dingz/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
# Configuration endpoints
3939
PIR_CONFIGURATION = "pir_config"
4040
BLIND_CONFIGURATION = "blind_config"
41+
DIMMER_CONFIGURATION = "dimmer_config"
4142
THERMOSTAT_CONFIGURATION = "thermostat_config"
4243
INPUT_CONFIGURATION = "input_config"
4344

dingz/dimmer.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
from yarl import URL
2+
3+
from dingz import make_call
4+
from dingz.constants import DIMMER
5+
from dingz.registry import BaseRegistry, organize_by_absolute_index
6+
7+
8+
class Dimmer(object):
9+
def __init__(self, absolute_index, dingz):
10+
self.dingz = dingz
11+
self.absolute_index = absolute_index
12+
self.on = None
13+
self.brightness_pct = None
14+
self.enabled = None
15+
self.dimmable = None
16+
self.output = None
17+
self.name = None
18+
self.seen_state = False
19+
20+
async def toggle(self, brightness_pct=100):
21+
"""
22+
Toggle light (based on internal state).
23+
:param brightness_pct: brightness in percent, or None
24+
:return:
25+
"""
26+
action = "off" if self.on else "on"
27+
await self.operate_light(action, brightness_pct)
28+
29+
async def turn_on(self, brightness_pct=100):
30+
""" Turn light on.
31+
:param brightness_pct: brightness in percent, or None.
32+
"""
33+
await self.operate_light("on", brightness_pct)
34+
35+
async def turn_off(self):
36+
""" Rurn light off."""
37+
await self.operate_light("off")
38+
39+
VALID_OPERATIONS = ('on', 'off')
40+
41+
async def operate_light(self, action, brightness_pct=None):
42+
"""
43+
Operate the light (turn it on or off).
44+
:param action: 'on' or 'off'
45+
:param brightness_pct: brightness in percent or None if not defined
46+
:return:
47+
"""
48+
if action not in Dimmer.VALID_OPERATIONS:
49+
raise ValueError("invalid action %s, expected one of %s" %
50+
(repr(action), repr(Dimmer.VALID_OPERATIONS)))
51+
52+
if brightness_pct is not None and (brightness_pct > 100 or brightness_pct < 0):
53+
raise ValueError("invalid brightness_pct %s, expected value between 0 and 100" %
54+
(repr(brightness_pct)))
55+
56+
url = URL(self.dingz.uri).join(URL("%s/%s/%s" % (DIMMER, self.index_relative, action)))
57+
params = {}
58+
if brightness_pct is not None:
59+
params["value"] = str(brightness_pct)
60+
61+
await make_call(self.dingz, uri=url, method="POST", parameters=params)
62+
self.on = action
63+
if brightness_pct is not None:
64+
self.brightness_pct = brightness_pct
65+
66+
def _consume_state(self, state_details):
67+
"""
68+
Example for state_details:
69+
{
70+
"on": true, "output": 100, "ramp": 0, "readonly": false,
71+
"index": { "relative": 0, "absolute": 2 }
72+
}
73+
:param state_details:
74+
:return:
75+
"""
76+
assert self.absolute_index == state_details['index']['absolute']
77+
self.seen_state = True
78+
self.index_relative = state_details['index']['relative']
79+
self.on = state_details['on']
80+
self.brightness_pct = state_details['output']
81+
82+
def _consume_config(self, config):
83+
# "output": "halogen", "name": "Dimmable 3", "feedback": null, "feedback_intensity": 10
84+
self.output = config['output']
85+
self.enabled = config['output'] != 'not_connected'
86+
self.dimmable = config['output'] != 'non_dimmable'
87+
self.name = config['name']
88+
89+
90+
class DimmerRegistry(BaseRegistry[Dimmer]):
91+
def __init__(self, dingz):
92+
super().__init__(factory=lambda absolute_index: Dimmer(absolute_index, dingz))
93+
94+
def _consume_config(self, dimmer_configs):
95+
for absolute_index, dimmer_config in enumerate(dimmer_configs):
96+
dimmer = self._get_or_create(absolute_index)
97+
dimmer._consume_config(dimmer_config)
98+
99+
def _consume_dimmer_state(self, dimmer_states):
100+
for absolute_index, dimmer_state in organize_by_absolute_index(dimmer_states):
101+
dimmer = self._get_or_create(absolute_index)
102+
dimmer._consume_state(dimmer_state)

0 commit comments

Comments
 (0)