· 6 years ago · Sep 03, 2019, 02:28 PM
1"""
2Custom component for Foscam Camera motion detection
3
4Foscam API used to make calls to cameras
5See: https://github.com/viswa-swami/python-foscam, https://pypi.org/project/libpyfoscam/
6
7-> Set port to 443 to enable ssl api calls
8
9Usage:
10binary_sensor:
11 - platform: foscam_motion
12 name: Front Entrance Camera Motion (optional)
13 ip_address: 192.168.1.11
14 username: !secret foscam_front_entrance_camera_username
15 password: !secret foscam_front_entrance_camera_password
16"""
17
18# TODO
19# - Add https and http as options? (Done automatically by setting port to 443)
20# - Make scan interval optional?
21
22import logging
23from datetime import timedelta
24import requests
25
26import homeassistant.helpers.config_validation as cv
27import voluptuous as vol
28from homeassistant.util import slugify
29from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, BinarySensorDevice
30from homeassistant.const import (CONF_PLATFORM, CONF_NAME, CONF_USERNAME,
31 CONF_PASSWORD, CONF_IP_ADDRESS, CONF_SCAN_INTERVAL,
32 CONF_PORT, STATE_ON, STATE_OFF, STATE_UNKNOWN)
33
34__version__ = '1.0.0'
35# Not sure which of these is defined by convention?
36PLATFORM = 'foscam_motion'
37DOMAIN = 'foscam_motion'
38REQUIREMENTS = []
39
40DEFAULT_PORT = 88
41DEFAULT_SCAN_INTERVAL = 1
42_LOGGER = logging.getLogger(__name__)
43
44# Override the default scan_interval for this component
45SCAN_INTERVAL = timedelta(seconds=1)
46
47PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
48 vol.Required(CONF_PLATFORM): PLATFORM,
49 vol.Required(CONF_IP_ADDRESS): cv.string,
50 vol.Required(CONF_USERNAME): cv.string,
51 vol.Required(CONF_PASSWORD): cv.string,
52 vol.Optional(CONF_NAME, default=None): cv.string,
53 # This must be handle internally, it causes an error trying to handle it this way, try using a different key other than CONF_SCAN_INTERVAL?
54 # vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL): vol.All(vol.Coerce(float), vol.Range(min=0.1)),
55 vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
56})
57
58
59def setup_platform(hass, config, add_entities, discovery_info=None):
60 """Setup the binary sensor platform."""
61 # si = config[CONF_SCAN_INTERVAL]
62 # SCAN_INTERVAL = timedelta(seconds=si)
63 camera_ip = config[CONF_IP_ADDRESS]
64 camera_port = config[CONF_PORT]
65 camera_username = config[CONF_USERNAME]
66 camera_password = config[CONF_PASSWORD]
67 camera_name = config[CONF_NAME]
68 add_entities([FoscamMotionBinarySensor(camera_name, camera_username, camera_password, camera_ip, camera_port)])
69
70
71class FoscamMotionBinarySensor(BinarySensorDevice):
72 """ Representation of a Foscam Motion binary sensor. """
73
74 def __init__(self, name, username, password, ip, port):
75 from libpyfoscam import FoscamCamera
76 self.attrs = {}
77 self.camera_name = name if name else username
78 self.camera_username = username
79 self.cam = FoscamCamera(ip, port, self.camera_username, password, verbose=False, daemon=False)
80 self.camera_state = STATE_OFF # Or should this start out as None (However, it will set state to 'unknown' between restarts if that is done)?
81
82 @property
83 def entity_id(self):
84 """ Return the entity id of the binary sensor. """
85 return 'binary_sensor.foscam_{}'.format(slugify(self.camera_name))
86
87 @property
88 def name(self):
89 """ Return the name of the binary sensor. """
90 return self.camera_name
91
92 @property
93 def device_class(self):
94 """ Return the class of this device, from component DEVICE_CLASSES. """
95 return 'motion'
96
97 @property
98 def state(self):
99 """ Return the current state of the binary sensor. """
100 return self.camera_state
101
102 @property
103 def device_state_attributes(self):
104 """ Return the attributes of the binary sensor """
105 return self.attrs
106
107 def get_motion_alert(self):
108 """ Return the motion alert status of camera: 0-disabled, 1-No motion, 2-Motion detected """
109 try:
110 result, response = self.cam.get_dev_state()
111 return int(response['motionDetectAlarm'])
112 except Exception as e:
113 _LOGGER.error('Error from {} foscam camera request: {}'.format(self.camera_name, e))
114 return 0
115
116 def update(self):
117 """ Update the motion binary sensor """
118 motion_status = self.get_motion_alert()
119 if motion_status == 2:
120 self.camera_state = STATE_ON
121 self.attrs['motion_status'] = 'enabled'
122 elif motion_status == 1:
123 self.camera_state = STATE_OFF
124 self.attrs['motion_status'] = 'enabled'
125 else:
126 self.camera_state = STATE_OFF
127 self.attrs['motion_status'] = 'disabled'