Skip to content

Commit ef456cf

Browse files
authored
Merge pull request #18 from StephanU/feature/cams
Added support for cams
2 parents 7633d3d + 88d0089 commit ef456cf

File tree

6 files changed

+228
-2
lines changed

6 files changed

+228
-2
lines changed

PyMyGekko/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
from PyMyGekko.resources.AlarmsLogics import AlarmsLogicValueAccessor
1414
from PyMyGekko.resources.Blinds import Blind
1515
from PyMyGekko.resources.Blinds import BlindValueAccessor
16+
from PyMyGekko.resources.Cams import Cam
17+
from PyMyGekko.resources.Cams import CamValueAccessor
1618
from PyMyGekko.resources.EnergyCosts import EnergyCost
1719
from PyMyGekko.resources.EnergyCosts import EnergyCostValueAccessor
1820
from PyMyGekko.resources.HotWaterSystems import HotWaterSystem
@@ -70,6 +72,7 @@ def __init__(
7072
self._data_provider
7173
)
7274
self._blind_value_accessor = BlindValueAccessor(self._data_provider)
75+
self._cam_value_accessor = CamValueAccessor(self._data_provider)
7376
self._energy_costs_value_accessor = EnergyCostValueAccessor(self._data_provider)
7477
self._hot_water_systems_value_accessor = HotWaterSystemValueAccessor(
7578
self._data_provider
@@ -120,6 +123,10 @@ def get_blinds(self) -> list[Blind]:
120123
"""Returns the MyGekko blinds"""
121124
return self._blind_value_accessor.blinds
122125

126+
def get_cams(self) -> list[Cam]:
127+
"""Returns the MyGekko cams"""
128+
return self._cam_value_accessor.cams
129+
123130
def get_energy_costs(self) -> list[EnergyCost]:
124131
"""Returns the MyGekko energy_costs"""
125132
return self._energy_costs_value_accessor.energy_costs

PyMyGekko/resources/Cams.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
"""MyGekko Cams implementation"""
2+
from __future__ import annotations
3+
4+
from enum import IntEnum
5+
6+
from PyMyGekko.data_provider import DataProviderBase
7+
from PyMyGekko.data_provider import EntityValueAccessor
8+
from PyMyGekko.resources import Entity
9+
10+
11+
class Cam(Entity):
12+
"""Class for MyGekko Cam"""
13+
14+
def __init__(
15+
self, entity_id: str, name: str, value_accessor: CamValueAccessor
16+
) -> None:
17+
super().__init__(entity_id, name, "/cams/")
18+
self._value_accessor = value_accessor
19+
self._supported_features = self._value_accessor.get_features(self)
20+
21+
@property
22+
def supported_features(self) -> list[CamFeature]:
23+
"""Returns the supported features"""
24+
return self._supported_features
25+
26+
@property
27+
def image_url(self) -> str | None:
28+
"""Returns the image url"""
29+
return self._value_accessor.get_value(self, "imagepath")
30+
31+
@property
32+
def stream_url(self) -> str | None:
33+
"""Returns the stream url"""
34+
return self._value_accessor.get_value(self, "streampath")
35+
36+
37+
class CamNewRecordAvailableState(IntEnum):
38+
"""MyGekko Cams Record Available State"""
39+
40+
NO = 0
41+
YES = 1
42+
43+
44+
class CamFeature(IntEnum):
45+
"""MyGekko Cams Feature"""
46+
47+
ON_OFF = 0
48+
STREAM = 1
49+
50+
51+
class CamValueAccessor(EntityValueAccessor):
52+
"""Cam value accessor"""
53+
54+
def __init__(self, data_provider: DataProviderBase):
55+
self._data = {}
56+
self._data_provider = data_provider
57+
self._data_provider.subscribe(self)
58+
59+
def update_status(self, status, hardware):
60+
if status is not None and "cams" in status:
61+
cams = status["cams"]
62+
for key in cams:
63+
if key.startswith("item"):
64+
if key not in self._data:
65+
self._data[key] = {}
66+
67+
if "sumstate" in cams[key] and "value" in cams[key]["sumstate"]:
68+
(
69+
self._data[key]["newRecordsAvailableState"],
70+
*_other,
71+
) = cams[
72+
key
73+
]["sumstate"]["value"].split(
74+
";",
75+
)
76+
77+
def update_resources(self, resources):
78+
if resources is not None and "cams" in resources:
79+
cams = resources["cams"]
80+
for key in cams:
81+
if key.startswith("item"):
82+
if key not in self._data:
83+
self._data[key] = {}
84+
self._data[key]["name"] = cams[key]["name"]
85+
self._data[key]["imagepath"] = cams[key]["imagepath"]
86+
self._data[key]["streampath"] = cams[key]["streampath"]
87+
88+
@property
89+
def cams(self):
90+
"""Returns the cams read from MyGekko"""
91+
result: list[Cam] = []
92+
for key, data in self._data.items():
93+
result.append(Cam(key, data["name"], self))
94+
95+
return result
96+
97+
def get_features(self, door: Cam) -> list[CamFeature]:
98+
"""Returns the supported features"""
99+
result = list()
100+
101+
if door and door.entity_id:
102+
if door.entity_id in self._data:
103+
data = self._data[door.entity_id]
104+
if "streampath" in data and data["streampath"]:
105+
result.append(CamFeature.STREAM)
106+
107+
return result

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,10 @@ hatch build
7373
### Release
7474

7575
- Increase version, then
76+
7677
```
77-
hatch build
78-
hatch publish -u __token__ -a <PyPiToken>
78+
hatch build
79+
hatch publish -u __token__ -a <PyPiToken>
7980
```
8081

8182
---
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"cams": {
3+
"item2": {
4+
"name": "DoorBird",
5+
"page": "Eingang",
6+
"imagepath": "http://XXX@192.168.1.50/bha-api/image.cgi",
7+
"streampath": "http://XXX@192.168.1.50/bha-api/video.cgi",
8+
"sumstate": {
9+
"description": "Summary of the states and their formats",
10+
"format": "newRecordsAvailableState enum[0=no,1=yes](); ",
11+
"type": "STRING",
12+
"permission": "READ"
13+
}
14+
},
15+
"group0": {
16+
"name": "Grp 1",
17+
"sumstate": {
18+
"description": "Summary of the group states and their formats",
19+
"format": "State[0=Off|1=On]",
20+
"type": "AO",
21+
"permission": "READ",
22+
"index": 7500000
23+
}
24+
}
25+
}
26+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"globals": {
3+
"network": {
4+
"gekkoname": {
5+
"value": "myGEKKO"
6+
},
7+
"language": {
8+
"value": "0"
9+
},
10+
"version": {
11+
"value": "879015"
12+
},
13+
"hardware": {
14+
"value": "Slide 2 (AC0DFE3012E6)"
15+
}
16+
}
17+
},
18+
"cams": {
19+
"item2": {
20+
"sumstate": {
21+
"value": "0;"
22+
}
23+
},
24+
"group0": {
25+
"sumstate": {
26+
"value": "1"
27+
}
28+
}
29+
}
30+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import logging
2+
3+
import pytest
4+
from aiohttp import ClientSession
5+
from aiohttp import web
6+
from PyMyGekko import MyGekkoApiClientBase
7+
from PyMyGekko.resources.Cams import CamFeature
8+
9+
_LOGGER: logging.Logger = logging.getLogger(__name__)
10+
11+
12+
async def var_response(request):
13+
varResponseFile = open("tests/cams/data/api_var_response_879015.json")
14+
return web.Response(status=200, body=varResponseFile.read())
15+
16+
17+
async def var_status_response(request):
18+
statusResponseFile = open("tests/cams/data/api_var_status_response_879015.json")
19+
return web.Response(status=200, body=statusResponseFile.read())
20+
21+
22+
@pytest.fixture
23+
def mock_server(aiohttp_server):
24+
app = web.Application()
25+
app.router.add_get("/api/v1/var", var_response)
26+
app.router.add_get("/api/v1/var/status", var_status_response)
27+
return aiohttp_server(app)
28+
29+
30+
@pytest.mark.asyncio
31+
async def test_get_cams(mock_server):
32+
_LOGGER.setLevel(logging.DEBUG)
33+
34+
server = await mock_server
35+
async with ClientSession() as session:
36+
api = MyGekkoApiClientBase(
37+
{},
38+
session,
39+
scheme=server.scheme,
40+
host=server.host,
41+
port=server.port,
42+
)
43+
44+
await api.read_data()
45+
cams = api.get_cams()
46+
47+
assert cams is not None
48+
assert len(cams) == 1
49+
50+
assert cams[0].entity_id == "item2"
51+
assert cams[0].name == "DoorBird"
52+
assert cams[0].image_url == "http://XXX@192.168.1.50/bha-api/image.cgi"
53+
assert cams[0].stream_url == "http://XXX@192.168.1.50/bha-api/video.cgi"
54+
assert len(cams[0].supported_features) == 1
55+
assert CamFeature.STREAM in cams[0].supported_features

0 commit comments

Comments
 (0)