Skip to content

Commit 6c000db

Browse files
authored
Merge pull request #89 from pyalarmdotcom/gate-support
Gate support
2 parents 0c79ca4 + cfcf6bb commit 6c000db

File tree

16 files changed

+279
-89
lines changed

16 files changed

+279
-89
lines changed

.deepsource.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ version = 1
44
name = "python"
55
enabled = true
66

7-
[analyzers.meta]
8-
runtime_version = "3.x.x"
7+
[analyzers.meta]
8+
runtime_version = "3.x.x"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,4 @@ test.py
155155
*.ignore.*
156156
temp/
157157
node_modules/
158+
hassfest/

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ ci:
55

66
repos:
77
- repo: https://github.com/asottile/pyupgrade
8-
rev: v3.3.1
8+
rev: v3.3.0
99
hooks:
1010
- id: pyupgrade
1111
args: [--py39-plus]
@@ -19,7 +19,7 @@ repos:
1919
- id: autoflake
2020
files: ^((pyalarmdotcomajax)/.+)?[^/]+\.py$
2121
- repo: https://github.com/psf/black
22-
rev: 22.12.0
22+
rev: 22.10.0
2323
hooks:
2424
- id: black
2525
args:
@@ -84,7 +84,7 @@ repos:
8484
entry: pylint
8585
language: system
8686
types: [python]
87-
exclude: (.vscode|.devcontainer|.mypy_cache|custom_components/.*/__pycache__|pylint)
87+
exclude: (.vscode|.devcontainer|.mypy_cache|pylint)
8888
args:
8989
- --output-format=colorized
9090
- -d W0511

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ Pyalarmdotcomajax supports core features (monitoring and using actions) of the d
5151
| Sensor | `device_subtype` | (none) | |
5252
| Locks | `desired_state` | lock, unlock | |
5353
| Garage Door | (none) | open, close | |
54+
| Gate | `supports_remote_close` | open, close | |
5455
| Image Sensor | `images` | peek_in | |
5556
| Light | `brightness` | turn_on (with brightness), turn_off | No support for RGB/W, effects, temperature, etc. |
5657
| Thermostat | `temp_average`, `temp_at_tstat`, `step_value`, `supports_fan_mode`, `supports_fan_indefinite`, `supports_fan_circulate_when_off`, `supported_fan_durations`, `fan_mode`, `supports_heat`, `supports_heat_aux`, `supports_cool`, `supports_auto`, `min_heat_setpoint`, `min_cool_setpoint`, `max_heat_setpoint`, `max_cool_setpoint`, `heat_setpoint`, `cool_setpoint`, `supports_humidity`, `humidity`, `supports_schedules`, `supports_schedules_smart`, `schedule_mode` | set_attribute | |
@@ -82,7 +83,7 @@ Pyalarmdotcomajax supports changing configuration options for the devices listed
8283

8384
### Skybell HD
8485

85-
**Doorbell Camera**
86+
#### Doorbell Camera
8687

8788
| Configuration Option | Slug | Supported Values | Notes |
8889
| ------------------------- | -------------------- | ------------------------------------ | -------------------------- |
@@ -121,6 +122,20 @@ actions:
121122
{get,set}
122123
get get data from alarm.com. use 'adc get --help' for parameters.
123124
set set device configuration option. use 'adc set --help' for parameters
125+
126+
get options:
127+
-h, --help show this help message and exit
128+
-x, --include-unsupported
129+
return basic data for all known unsupported devices. always outputs in verbose format.
130+
131+
set options:
132+
-h, --help show this help message and exit
133+
-i DEVICE_ID, --device-id DEVICE_ID
134+
Numeric Alarm.com device identifier.
135+
-s SETTING_SLUG, --setting-slug SETTING_SLUG
136+
Identifier for setting. Appears in parenthesis after setting name in adc set human readable output.
137+
-k NEW_VALUE, --new-value NEW_VALUE
138+
New value for setting.
124139
```
125140
126141
### Examples

pyalarmdotcomajax/__init__.py

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
)
2424
from .devices.camera import Camera
2525
from .devices.garage_door import GarageDoor
26+
from .devices.gate import Gate
2627
from .devices.image_sensor import ImageSensor
2728
from .devices.light import Light
2829
from .devices.lock import Lock
@@ -44,14 +45,15 @@
4445
ExtendedProperties,
4546
)
4647

47-
__version__ = "0.4.7"
48+
__version__ = "0.4.8-beta"
4849

4950
log = logging.getLogger(__name__)
5051

5152
# Map DeviceType enum to device class.
5253
DEVICE_CLASSES: dict = {
5354
DeviceType.CAMERA: Camera,
5455
DeviceType.GARAGE_DOOR: GarageDoor,
56+
DeviceType.GATE: Gate,
5557
DeviceType.IMAGE_SENSOR: ImageSensor,
5658
DeviceType.LIGHT: Light,
5759
DeviceType.LOCK: Lock,
@@ -153,6 +155,7 @@ def __init__(
153155
self.sensors: list[Sensor] = []
154156
self.locks: list[Lock] = []
155157
self.garage_doors: list[GarageDoor] = []
158+
self.gates: list[Gate] = []
156159
self.image_sensors: list[ImageSensor] = []
157160
self.lights: list[Light] = []
158161
self.cameras: list[Camera] = []
@@ -353,6 +356,7 @@ async def async_send_command(
353356
event: Lock.Command
354357
| Partition.Command
355358
| GarageDoor.Command
359+
| Gate.Command
356360
| Light.Command
357361
| ImageSensor.Command
358362
| Thermostat.Command,
@@ -516,6 +520,7 @@ def get_device_by_id(self, device_id: str) -> BaseDevice | None:
516520
*self.sensors,
517521
*self.locks,
518522
*self.garage_doors,
523+
*self.gates,
519524
*self.image_sensors,
520525
*self.lights,
521526
*self.cameras,
@@ -569,10 +574,8 @@ async def _async_get_and_build_devices(
569574

570575
except DataFetchFailed:
571576
log.error(
572-
(
573-
"Encountered data error while fetching %ss. Skipping this"
574-
" device type."
575-
),
577+
"Encountered data error while fetching %ss. Skipping this"
578+
" device type.",
576579
device_type.name,
577580
)
578581

@@ -731,6 +734,8 @@ async def _async_get_and_build_devices(
731734
self.sensors[:] = temp_device_storage
732735
elif device_class is GarageDoor:
733736
self.garage_doors[:] = temp_device_storage
737+
elif device_class is Gate:
738+
self.gates[:] = temp_device_storage
734739
elif device_class is Lock:
735740
self.locks[:] = temp_device_storage
736741
elif device_class is Light:
@@ -860,10 +865,8 @@ async def _async_get_trouble_conditions(self) -> None:
860865

861866
except aiohttp.ContentTypeError as err:
862867
log.error(
863-
(
864-
"Server returned wrong content type. Response: %s\n\nResponse"
865-
" Text:\n\n%s\n\n"
866-
),
868+
"Server returned wrong content type. Response: %s\n\nResponse"
869+
" Text:\n\n%s\n\n",
867870
resp,
868871
resp.text(),
869872
)
@@ -930,11 +933,9 @@ async def _async_get_items_and_subordinates(
930933

931934
if rsp_errors[0].get("status") == "423":
932935
log.debug(
933-
(
934-
"Error fetching data from Alarm.com. This account either"
935-
" doesn't have permission to %s, is on a plan that does not"
936-
" support %s, or is part of a system with %s turned off."
937-
),
936+
"Error fetching data from Alarm.com. This account either"
937+
" doesn't have permission to %s, is on a plan that does not"
938+
" support %s, or is part of a system with %s turned off.",
938939
device_type,
939940
device_type,
940941
device_type,
@@ -954,10 +955,8 @@ async def _async_get_items_and_subordinates(
954955

955956
if not retry_on_failure:
956957
log.debug(
957-
(
958-
"Got 403 status when fetching data for device type %s."
959-
" Logging in again didn't help."
960-
),
958+
"Got 403 status when fetching data for device type %s."
959+
" Logging in again didn't help.",
961960
device_type,
962961
)
963962

pyalarmdotcomajax/cli.py

Lines changed: 23 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,21 @@
1818
from termcolor import colored, cprint
1919

2020
import pyalarmdotcomajax
21-
from pyalarmdotcomajax import AlarmController, AuthResult
22-
from pyalarmdotcomajax.errors import (
21+
22+
from . import AlarmController, AuthResult
23+
from .devices import DEVICE_URLS, BaseDevice, DeviceType
24+
from .devices.light import Light
25+
from .devices.sensor import Sensor
26+
from .devices.system import System
27+
from .errors import (
2328
AuthenticationFailed,
2429
DataFetchFailed,
2530
InvalidConfigurationOption,
2631
NagScreen,
2732
UnexpectedDataStructure,
2833
)
29-
from pyalarmdotcomajax.extensions import ConfigurationOption
30-
from pyalarmdotcomajax.helpers import ExtendedEnumMixin, slug_to_title
31-
32-
from .devices import DEVICE_URLS, BaseDevice, DeviceType
33-
from .devices.light import Light
34-
from .devices.sensor import Sensor
35-
from .devices.system import System
34+
from .extensions import ConfigurationOption
35+
from .helpers import ExtendedEnumMixin, slug_to_title
3636

3737
CLI_CARD_BREAK = "" # "--------"
3838

@@ -221,10 +221,8 @@ async def cli() -> None:
221221
login_result = await alarm.async_login()
222222
except NagScreen:
223223
cprint(
224-
(
225-
"Unable to log in. Please set up two-factor authentication for this"
226-
" account."
227-
),
224+
"Unable to log in. Please set up two-factor authentication for this"
225+
" account.",
228226
"red",
229227
)
230228
sys.exit()
@@ -244,20 +242,16 @@ async def cli() -> None:
244242
)
245243
else:
246244
cprint(
247-
(
248-
"Not enough information provided to make a decision regarding"
249-
" two-factor authentication."
250-
),
245+
"Not enough information provided to make a decision regarding"
246+
" two-factor authentication.",
251247
"red",
252248
)
253249
sys.exit()
254250

255251
if login_result == AuthResult.ENABLE_TWO_FACTOR:
256252
cprint(
257-
(
258-
"Unable to log in. Please set up two-factor authentication for this"
259-
" account."
260-
),
253+
"Unable to log in. Please set up two-factor authentication for this"
254+
" account.",
261255
"red",
262256
)
263257
sys.exit()
@@ -347,10 +341,8 @@ async def cli() -> None:
347341
config_option: ConfigurationOption = device.settings[setting_slug]
348342
except KeyError:
349343
cprint(
350-
(
351-
f"{device.name} ({device_id}) does not have the setting"
352-
f" {setting_slug}."
353-
),
344+
f"{device.name} ({device_id}) does not have the setting"
345+
f" {setting_slug}.",
354346
"red",
355347
)
356348
sys.exit(0)
@@ -375,10 +367,8 @@ async def cli() -> None:
375367
typed_new_value = config_option_type.enum_from_key(new_value)
376368
except ValueError:
377369
cprint(
378-
(
379-
f"Acceptable valures for {setting_slug} are:"
380-
f" {', '.join([member_name.lower() for member_name in config_option_type.names()])}"
381-
),
370+
f"Acceptable valures for {setting_slug} are:"
371+
f" {', '.join([member_name.lower() for member_name in config_option_type.names()])}",
382372
"red",
383373
)
384374
sys.exit(0)
@@ -422,10 +412,8 @@ async def cli() -> None:
422412

423413
if str(reported_value).upper() == str(new_value).upper():
424414
cprint(
425-
(
426-
f"{config_option.name} was successfully changed to"
427-
f" {new_value} for {device.name}."
428-
),
415+
f"{config_option.name} was successfully changed to"
416+
f" {new_value} for {device.name}.",
429417
"green",
430418
)
431419
else:
@@ -457,10 +445,8 @@ async def _async_machine_output(
457445
cprint("Connection error.", "red")
458446
except AuthenticationFailed:
459447
cprint(
460-
(
461-
"Authentication error. Check that your two factor authentication cookie"
462-
" is correct."
463-
),
448+
"Authentication error. Check that your two factor authentication cookie"
449+
" is correct.",
464450
"red",
465451
)
466452

pyalarmdotcomajax/devices/__init__.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class DeviceType(ExtendedEnumMixin):
3434

3535
# Supported
3636
GARAGE_DOOR = "garageDoors"
37+
GATE = "gates"
3738
IMAGE_SENSOR = "imageSensors"
3839
LIGHT = "lights"
3940
LOCK = "locks"
@@ -50,7 +51,6 @@ class DeviceType(ExtendedEnumMixin):
5051
COMMERCIAL_TEMP = "commercialTemperatureSensors"
5152
# CONFIGURATION = "configuration"
5253
# FENCE = "fences"
53-
GATE = "gates"
5454
GEO_DEVICE = "geoDevices"
5555
IQ_ROUTER = "iqRouters"
5656
REMOTE_TEMP = "remoteTemperatureSensors"
@@ -76,6 +76,10 @@ class DeviceType(ExtendedEnumMixin):
7676
"relationshipId": "devices/garage-door",
7777
"endpoint": "{}web/api/devices/garageDoors/{}",
7878
},
79+
DeviceType.GATE: {
80+
"relationshipId": "devices/gate",
81+
"endpoint": "{}web/api/devices/gates/{}",
82+
},
7983
DeviceType.IMAGE_SENSOR: {
8084
"relationshipId": "image-sensor/image-sensor",
8185
"endpoint": "{}web/api/imageSensor/imageSensors/{}",
@@ -135,10 +139,6 @@ class DeviceType(ExtendedEnumMixin):
135139
# "relationshipId": "",
136140
# "endpoint": "{}web/api/geolocation/fences/{}",
137141
# },
138-
DeviceType.GATE: {
139-
"relationshipId": "devices/gate",
140-
"endpoint": "{}web/api/devices/gates/{}",
141-
},
142142
DeviceType.GEO_DEVICE: {
143143
"relationshipId": "geolocation/geo-device",
144144
"endpoint": "{}web/api/geolocation/geoDevices/{}",
@@ -490,10 +490,8 @@ async def async_change_setting(self, slug: str, new_value: Any) -> None:
490490

491491
if not self._config_change_callback:
492492
log.error(
493-
(
494-
"async_change_setting called for %s, which does not have a"
495-
" config_change_callback set."
496-
),
493+
"async_change_setting called for %s, which does not have a"
494+
" config_change_callback set.",
497495
self.name,
498496
)
499497
return
@@ -507,10 +505,8 @@ async def async_change_setting(self, slug: str, new_value: Any) -> None:
507505
raise InvalidConfigurationOption
508506

509507
log.debug(
510-
(
511-
"BaseDevice -> async_change_setting: Calling change setting function"
512-
" for %s %s (%s) via extension %s."
513-
),
508+
"BaseDevice -> async_change_setting: Calling change setting function"
509+
" for %s %s (%s) via extension %s.",
514510
type(self).__name__,
515511
self.name,
516512
self.id_,

0 commit comments

Comments
 (0)