diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c6930a..b091d4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,26 @@ # Change Log +## [0.12.0](https://github.com/theolind/pymysensors/tree/0.12.0) (2018-01-14) +[Full Changelog](https://github.com/theolind/pymysensors/compare/0.11.1...0.12.0) + +**Closed issues:** + +- Problem after pull \#102 [\#109](https://github.com/theolind/pymysensors/issues/109) + +**Merged pull requests:** + +- Upgrade lint and test requirements [\#114](https://github.com/theolind/pymysensors/pull/114) ([MartinHjelmare](https://github.com/MartinHjelmare)) +- Dump JSON file with indentation [\#113](https://github.com/theolind/pymysensors/pull/113) ([Mirodin](https://github.com/Mirodin)) +- Fix reboot not returning to False [\#111](https://github.com/theolind/pymysensors/pull/111) ([MartinHjelmare](https://github.com/MartinHjelmare)) + ## [0.11.1](https://github.com/theolind/pymysensors/tree/0.11.1) (2017-08-29) [Full Changelog](https://github.com/theolind/pymysensors/compare/0.11...0.11.1) **Merged pull requests:** +- 0.11.1 [\#110](https://github.com/theolind/pymysensors/pull/110) ([MartinHjelmare](https://github.com/MartinHjelmare)) - Fix protocol version null and enhance validation [\#108](https://github.com/theolind/pymysensors/pull/108) ([MartinHjelmare](https://github.com/MartinHjelmare)) +- Update release instructions [\#107](https://github.com/theolind/pymysensors/pull/107) ([MartinHjelmare](https://github.com/MartinHjelmare)) ## [0.11](https://github.com/theolind/pymysensors/tree/0.11) (2017-08-21) [Full Changelog](https://github.com/theolind/pymysensors/compare/0.10...0.11) diff --git a/RELEASE.md b/RELEASE.md index 0469c80..260aff9 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -8,17 +8,20 @@ pypi test - [test] - repository = https://testpypi.python.org/pypi + [testpypi] + repository = https://test.pypi.org/legacy/ username = username password = password [pypi] - repository = https://pypi.python.org/pypi username = username password = password ``` - + There's a new API for PyPi, see this [page](https://packaging.python.org/guides/migrating-to-pypi-org/#uploading) for more info. +- Install `twine` v1.8.0+. + ``` + pip3 install --upgrade twine + ``` - Create a release branch from dev. - Merge master into the release branch to make the PR mergeable. - Update version in `mysensors/version.py` to the new version number, eg `'0.2.0'`. @@ -26,7 +29,7 @@ - Commit and push the release branch. - Create a pull request from release branch to master with the upcoming release number as the title. Put the changes for the new release from the updated changelog as the PR message. - Merge the pull request into master, do not squash. -- Go to github releases and tag a new release on the master branch. +- Go to github releases and tag a new release on the master branch. Put the PR message as the description for the release. - Fetch and checkout the master branch. - Generate `README.rst` by running `scripts/gen_rst` (pandoc needed). - Build source and wheel distributions: @@ -35,9 +38,8 @@ rm -rf dist python3 setup.py sdist bdist_wheel ``` -- Stage release: `twine upload -r test dist/*` +- Stage release: `twine upload -r testpypi dist/*` - Release: `twine upload -r pypi dist/*` -- Fetch and checkout the master branch. - Fetch and checkout the develop branch. - Merge master into develop. - Update version in `mysensors/version.py` to the new develop version number, eg `'0.3.0.dev0'` diff --git a/mysensors/__init__.py b/mysensors/__init__.py index 9d365aa..7e3f6ae 100644 --- a/mysensors/__init__.py +++ b/mysensors/__init__.py @@ -74,10 +74,11 @@ def _handle_presentation(self, msg): # this is a presentation of the sensor platform sensorid = self.add_sensor(msg.node_id) if sensorid is None: - return + if msg.node_id in self.sensors: + self.sensors[msg.node_id].reboot = False + return None self.sensors[msg.node_id].type = msg.sub_type self.sensors[msg.node_id].protocol_version = msg.payload - self.sensors[msg.node_id].reboot = False self.alert(msg) return msg else: @@ -85,18 +86,18 @@ def _handle_presentation(self, msg): if not self.is_sensor(msg.node_id): _LOGGER.error('Node %s is unknown, will not add child %s.', msg.node_id, msg.child_id) - return + return None child_id = self.sensors[msg.node_id].add_child_sensor( msg.child_id, msg.sub_type, msg.payload) if child_id is None: - return + return None self.alert(msg) return msg def _handle_set(self, msg): """Process a set message.""" if not self.is_sensor(msg.node_id, msg.child_id): - return + return None self.sensors[msg.node_id].set_child_value( msg.child_id, msg.sub_type, msg.payload) if self.sensors[msg.node_id].new_state: @@ -109,6 +110,7 @@ def _handle_set(self, msg): return msg.modify( child_id=SYSTEM_CHILD_ID, type=self.const.MessageType.internal, ack=0, sub_type=self.const.Internal.I_REBOOT, payload='') + return None def _handle_req(self, msg): """Process a req message. @@ -116,12 +118,14 @@ def _handle_req(self, msg): This will return the value if it exists. If no value exists, nothing is returned. """ - if self.is_sensor(msg.node_id, msg.child_id): - value = self.sensors[msg.node_id].children[ - msg.child_id].values.get(msg.sub_type) - if value is not None: - return msg.modify( - type=self.const.MessageType.set, payload=value) + if not self.is_sensor(msg.node_id, msg.child_id): + return None + value = self.sensors[msg.node_id].children[ + msg.child_id].values.get(msg.sub_type) + if value is not None: + return msg.modify( + type=self.const.MessageType.set, payload=value) + return None def _handle_heartbeat(self, msg): """Process a heartbeat message.""" @@ -151,10 +155,8 @@ def _handle_internal(self, msg): elif msg.sub_type == self.const.Internal.I_TIME: return msg.modify(ack=0, payload=calendar.timegm(time.localtime())) actions = self.const.HANDLE_INTERNAL.get(msg.sub_type) - if not actions: - return if actions.get('is_sensor') and not self.is_sensor(msg.node_id): - return + return None if actions.get('setattr'): setattr(self.sensors[msg.node_id], actions['setattr'], msg.payload) if actions.get('fun'): @@ -168,15 +170,17 @@ def _handle_internal(self, msg): msg.payload) if actions.get('msg'): return msg.modify(**actions['msg']) + return None def _handle_stream(self, msg): """Process a stream type message.""" if not self.is_sensor(msg.node_id): - return + return None if msg.sub_type == self.const.Stream.ST_FIRMWARE_CONFIG_REQUEST: return self.ota.respond_fw_config(msg) elif msg.sub_type == self.const.Stream.ST_FIRMWARE_REQUEST: return self.ota.respond_fw(msg) + return None def send(self, message): """Implement this method in a child class.""" @@ -194,7 +198,7 @@ def logic(self, data): msg.validate(self.protocol_version) except (ValueError, vol.Invalid) as exc: _LOGGER.warning('Not a valid message: %s', exc) - return + return None if msg.type == self.const.MessageType.presentation: ret = self._handle_presentation(msg) @@ -225,7 +229,8 @@ def _load_pickle(self, filename): def _save_json(self, filename): """Save sensors to json file.""" with open(filename, 'w') as file_handle: - json.dump(self.sensors, file_handle, cls=MySensorsJSONEncoder) + json.dump(self.sensors, file_handle, cls=MySensorsJSONEncoder, + indent=4) file_handle.flush() os.fsync(file_handle.fileno()) @@ -321,6 +326,7 @@ def _get_next_id(self): next_id = 1 if next_id <= self.const.MAX_NODE_ID: return next_id + return None def add_sensor(self, sensorid=None): """Add a sensor to the gateway.""" @@ -329,6 +335,7 @@ def add_sensor(self, sensorid=None): if sensorid is not None and sensorid not in self.sensors: self.sensors[sensorid] = Sensor(sensorid) return sensorid + return None def is_sensor(self, sensorid, child_id=None): """Return True if a sensor and its child exist.""" @@ -353,12 +360,13 @@ def is_sensor(self, sensorid, child_id=None): def _route_message(self, msg): if not isinstance(msg, Message) or \ msg.type == self.const.MessageType.presentation: - return + return None if (msg.node_id not in self.sensors or msg.type == self.const.MessageType.stream or not self.sensors[msg.node_id].new_state): return msg self.sensors[msg.node_id].queue.append(msg.encode()) + return None def handle_queue(self, queue=None): """Handle queue. @@ -368,17 +376,18 @@ def handle_queue(self, queue=None): """ if queue is None: queue = self.queue - if not queue.empty(): - start = timer() - func, args, kwargs = queue.get() - reply = func(*args, **kwargs) - queue.task_done() - end = timer() - if end - start > 0.1: - _LOGGER.debug( - 'Handle queue with call %s(%s, %s) took %.3f seconds', - func, args, kwargs, end - start) - return reply + if queue.empty(): + return None + start = timer() + func, args, kwargs = queue.get() + reply = func(*args, **kwargs) + queue.task_done() + end = timer() + if end - start > 0.1: + _LOGGER.debug( + 'Handle queue with call %s(%s, %s) took %.3f seconds', + func, args, kwargs, end - start) + return reply def fill_queue(self, func, args=None, kwargs=None, queue=None): """Put a function in a queue. @@ -492,7 +501,7 @@ def add_child_sensor(self, child_id, child_type, description=''): _LOGGER.warning( 'child_id %s already exists in children of node %s, ' 'cannot add child', child_id, self.sensor_id) - return + return None self.children[child_id] = ChildSensor( child_id, child_type, description) return child_id @@ -501,7 +510,7 @@ def set_child_value(self, child_id, value_type, value, **kwargs): """Set a child sensor's value.""" children = kwargs.get('children', self.children) if not isinstance(children, dict) or child_id not in children: - return + return None msg_type = kwargs.get('msg_type', 1) ack = kwargs.get('ack', 0) msg = Message().modify( @@ -513,13 +522,13 @@ def set_child_value(self, child_id, value_type, value, **kwargs): 'Not a valid message: node %s, child %s, type %s, ack %s, ' 'sub_type %s, payload %s', self.sensor_id, child_id, msg_type, ack, value_type, value) - return + return None try: msg = Message(msg_string) msg.validate(self.protocol_version) except (ValueError, AttributeError, vol.Invalid) as exc: _LOGGER.error('Not a valid message: %s', exc) - return + return None child = children[msg.child_id] child.values[msg.sub_type] = msg.payload return msg_string diff --git a/mysensors/gateway_mqtt.py b/mysensors/gateway_mqtt.py index a2a255c..666b60f 100644 --- a/mysensors/gateway_mqtt.py +++ b/mysensors/gateway_mqtt.py @@ -68,7 +68,7 @@ def _parse_mqtt_to_message(self, topic, payload, qos): prefix_end_idx = topic.find('/'.join(not_prefix)) - 1 prefix = topic[:prefix_end_idx] if prefix != self._in_prefix: - return + return None if qos and qos > 0: ack = '1' else: diff --git a/mysensors/gateway_tcp.py b/mysensors/gateway_tcp.py index bd4a937..2d64c29 100644 --- a/mysensors/gateway_tcp.py +++ b/mysensors/gateway_tcp.py @@ -50,8 +50,8 @@ def _check_connection(self): def _handle_internal(self, msg): if msg.sub_type == self.const.Internal.I_VERSION: self.tcp_disconnect_timer = time.time() - else: - return super()._handle_internal(msg) + return None + return super()._handle_internal(msg) def connect(self): """Connect to the socket object, on host and port.""" diff --git a/mysensors/ota.py b/mysensors/ota.py index e616763..59e9641 100644 --- a/mysensors/ota.py +++ b/mysensors/ota.py @@ -45,7 +45,7 @@ def check_fw(path): bin_string = intel_hex.tobinstr() except (IntelHexError, TypeError, ValueError) as exception: _LOGGER.error(exception) - return + return None pads = len(bin_string) % 128 # 128 bytes per page for atmega328 for _ in range(128 - pads): # pad up to even 128 bytes bin_string += b'\xff' @@ -106,7 +106,7 @@ def respond_fw(self, msg): fw_type, fw_ver, fware = self._get_fw( msg, (self.unstarted, self.started), req_fw_type, req_fw_ver) if fware is None: - return + return None blk_data = fware['data'][ req_blk * FIRMWARE_BLOCK_SIZE: req_blk * FIRMWARE_BLOCK_SIZE + FIRMWARE_BLOCK_SIZE] @@ -130,7 +130,7 @@ def respond_fw_config(self, msg): fw_type, fw_ver, fware = self._get_fw( msg, (self.requested, self.unstarted)) if fware is None: - return + return None if fw_type != req_fw_type: _LOGGER.warning( 'Firmware type %s of update is not identical to existing ' diff --git a/mysensors/version.py b/mysensors/version.py index cb33a13..34c2de1 100644 --- a/mysensors/version.py +++ b/mysensors/version.py @@ -1,5 +1,5 @@ """Store version constants.""" MAJOR_VERSION = 0 -MINOR_VERSION = 11 -PATCH_VERSION = 1 +MINOR_VERSION = 12 +PATCH_VERSION = '0' __version__ = '{}.{}.{}'.format(MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION) diff --git a/requirements_test.txt b/requirements_test.txt index 7a562c3..f5c69fc 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,6 +1,6 @@ -flake8==3.4.1 -pylint==1.7.2 -pytest==3.2.0 +flake8==3.5.0 +pylint==1.8.1 +pytest==3.3.2 pytest-cov==2.5.1 -pytest-timeout==1.2.0 -pydocstyle==2.0.0 +pytest-timeout==1.2.1 +pydocstyle==2.1.1 diff --git a/tests/test_mysensors.py b/tests/test_mysensors.py index 4aba8cd..56879bd 100644 --- a/tests/test_mysensors.py +++ b/tests/test_mysensors.py @@ -466,6 +466,8 @@ def test_set_and_reboot(self): sensor.reboot = True ret = self.gateway.logic('1;0;1;0;23;43\n') self.assertEqual(ret, '1;255;3;0;13;\n') + self.gateway.logic('1;255;0;0;17;1.4.1\n') + self.assertEqual(sensor.reboot, False) def test_set_child_value(self): """Test Gateway method set_child_value."""